medit/moo/mooapp/moopythonconsole.c

441 lines
14 KiB
C

/*
* mooapp/moopythonconsole.c
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#ifdef USE_PYTHON
#include <Python.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "mooapp/moopython.h"
#include "mooapp/moopythonconsole.h"
#include "mooutils/moostock.h"
#include "mooutils/moocompat.h"
#include "mooutils/moowin.h"
#define MAX_HISTORY 500
static void moo_python_console_class_init (MooPythonConsoleClass *klass);
static GObject *moo_python_console_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
static void moo_python_console_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void moo_python_console_init (MooPythonConsole *console);
static void moo_python_console_finalize (GObject *object);
static void create_gui (MooPythonConsole *self);
static void entry_activate (MooPythonConsole *self);
static gboolean key_press_event (GtkEntry *entry,
GdkEventKey *event,
MooPythonConsole *self);
static void entry_commit (MooPythonConsole *self);
static void history_next (MooPythonConsole *self);
static void history_prev (MooPythonConsole *self);
static void write_in (const char *text,
int len,
MooPythonConsole *self);
static void write_out (const char *text,
int len,
MooPythonConsole *self);
static void write_err (const char *text,
int len,
MooPythonConsole *self);
static void queue_free (GQueue *que);
static guint queue_len (GQueue *que);
static gpointer queue_nth (GQueue *que,
guint n);
/* MOO_TYPE_PYTHON_CONSOLE */
G_DEFINE_TYPE (MooPythonConsole, moo_python_console, GTK_TYPE_WINDOW);
enum {
PROP_0,
PROP_PYTHON
};
static void moo_python_console_class_init (MooPythonConsoleClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = moo_python_console_constructor;
gobject_class->finalize = moo_python_console_finalize;
gobject_class->set_property = moo_python_console_set_property;
g_object_class_install_property (gobject_class,
PROP_PYTHON,
g_param_spec_object
("python",
"python",
"python",
MOO_TYPE_PYTHON,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}
static void moo_python_console_init (MooPythonConsole *console)
{
console->history = g_queue_new ();
console->current = 0;
}
static GObject *moo_python_console_constructor (GType type,
guint n_props,
GObjectConstructParam *props)
{
MooPythonConsole *console;
PangoFontDescription *font;
GtkTextIter iter;
GObject *obj =
G_OBJECT_CLASS (moo_python_console_parent_class)->constructor (type, n_props, props);
console = MOO_PYTHON_CONSOLE (obj);
create_gui (console);
moo_window_set_icon_from_stock (GTK_WINDOW (console), MOO_STOCK_APP);
g_signal_connect (console, "delete-event",
G_CALLBACK (gtk_widget_hide_on_delete),
NULL);
moo_python_set_log_func (console->python,
(MooPythonLogFunc) write_in,
(MooPythonLogFunc) write_out,
(MooPythonLogFunc) write_err,
console);
g_signal_connect_swapped (console->entry, "activate",
G_CALLBACK (entry_activate),
console);
g_signal_connect (console->entry, "key-press-event",
G_CALLBACK (key_press_event),
console);
console->buf = gtk_text_view_get_buffer (console->textview);
gtk_text_buffer_get_end_iter (console->buf, &iter);
console->end = gtk_text_buffer_create_mark (console->buf, NULL, &iter, FALSE);
console->in_tag = gtk_text_buffer_create_tag (console->buf, "input", "foreground", "blue", NULL);
console->out_tag = gtk_text_buffer_create_tag (console->buf, "output", "foreground", "black", NULL);
console->err_tag = gtk_text_buffer_create_tag (console->buf, "error", "foreground", "red", NULL);
font = pango_font_description_from_string ("Courier New 10");
if (font) {
gtk_widget_modify_font (GTK_WIDGET (console->textview), font);
gtk_widget_modify_font (console->entry, font);
pango_font_description_free (font);
}
console->current = 0;
return obj;
}
static void moo_python_console_finalize (GObject *object)
{
MooPythonConsole *console = MOO_PYTHON_CONSOLE (object);
queue_free (console->history);
G_OBJECT_CLASS (moo_python_console_parent_class)->finalize (object);
}
static void moo_python_console_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooPythonConsole *console = MOO_PYTHON_CONSOLE (object);
switch (prop_id)
{
case PROP_PYTHON:
console->python = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void entry_activate (MooPythonConsole *self)
{
return entry_commit (self);
}
static void entry_commit (MooPythonConsole *self)
{
char *s;
PyObject *res;
s = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->entry)));
gtk_entry_set_text (GTK_ENTRY (self->entry), "");
if (!s || !s[0])
{
g_free (s);
return;
}
res = (PyObject*) moo_python_run_string (self->python, s, FALSE);
if (res && res != Py_None)
{
PyObject* str = PyObject_Str (res);
if (str)
{
write_out (PyString_AsString(str), -1, self);
Py_DECREF (str);
}
else
{
PyErr_Print ();
}
}
else if (!res)
{
PyErr_Print ();
}
Py_XDECREF (res);
if (g_queue_is_empty (self->history) || strcmp (s, g_queue_peek_head (self->history)))
g_queue_push_tail (self->history, s);
else
g_free (s);
if (queue_len (self->history) >= MAX_HISTORY)
g_free (g_queue_pop_head (self->history));
self->current = queue_len (self->history);
}
static void write_in (const char *text_to_write,
int len,
MooPythonConsole *self)
{
char *text;
char **lines, **l;
GString *s;
GtkTextIter end;
if (!text_to_write || !text_to_write[0] || len == 0)
return;
if (len > 0)
text = g_strndup (text_to_write, len);
else
text = (char*)text_to_write;
s = g_string_new ("");
lines = g_strsplit (text, "\n", 0);
for (l = lines; *l != NULL; ++l)
if (**l)
g_string_append_printf (s, ">>> %s\n", *l);
gtk_text_buffer_get_end_iter (self->buf, &end);
gtk_text_buffer_insert_with_tags (self->buf, &end,
s->str, s->len,
self->in_tag, NULL);
gtk_text_buffer_get_end_iter (self->buf, &end);
gtk_text_buffer_place_cursor (self->buf, &end);
gtk_text_view_scroll_mark_onscreen (self->textview, self->end);
g_strfreev (lines);
g_string_free (s, TRUE);
if (text != text_to_write)
g_free (text);
}
static void write_out (const char *text,
int len,
MooPythonConsole *self)
{
GtkTextIter end;
gtk_text_buffer_get_end_iter (self->buf, &end);
gtk_text_buffer_insert_with_tags (self->buf, &end, text, len, self->out_tag, NULL);
gtk_text_view_scroll_mark_onscreen (self->textview, self->end);
}
static void write_err (const char *text,
int len,
MooPythonConsole *self)
{
GtkTextIter end;
gtk_text_buffer_get_end_iter (self->buf, &end);
gtk_text_buffer_insert_with_tags (self->buf, &end, text, len, self->err_tag, NULL);
gtk_text_view_scroll_mark_onscreen (self->textview, self->end);
}
static gboolean key_press_event (G_GNUC_UNUSED GtkEntry *entry,
GdkEventKey *event,
MooPythonConsole *self)
{
if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
return FALSE;
switch (event->keyval) {
case GDK_Tab:
return TRUE;
case GDK_Down:
history_next (self);
return TRUE;
case GDK_Up:
history_prev (self);
return TRUE;
case GDK_Return:
entry_commit (self);
return TRUE;
default:
return FALSE;
}
}
static void history_prev (MooPythonConsole *self)
{
const char *s = "";
guint hist_size = queue_len (self->history);
self->current -= 1;
if (self->current == (guint)-1)
self->current = hist_size;
if (self->current != hist_size)
s = queue_nth (self->history, self->current);
gtk_entry_set_text (GTK_ENTRY (self->entry), s);
}
static void history_next (MooPythonConsole *self)
{
const char *s = "";
guint hist_size = queue_len (self->history);
self->current += 1;
if (self->current == hist_size + 1)
self->current = 0;
if (self->current != hist_size)
s = queue_nth (self->history, self->current);
gtk_entry_set_text (GTK_ENTRY (self->entry), s);
}
static void create_gui (MooPythonConsole *self)
{
GtkWidget *vbox1;
GtkWidget *scrolledwindow1;
GtkWidget *alignment1;
GtkWidget *hseparator1;
gtk_window_set_title (GTK_WINDOW (self), "Python");
gtk_window_set_default_size (GTK_WINDOW (self), 400, 300);
vbox1 = gtk_vbox_new (FALSE, 0);
gtk_widget_show (vbox1);
gtk_container_add (GTK_CONTAINER (self), vbox1);
scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (scrolledwindow1);
gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0);
GTK_WIDGET_UNSET_FLAGS (scrolledwindow1, GTK_CAN_FOCUS);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_SHADOW_ETCHED_IN);
self->textview = GTK_TEXT_VIEW (gtk_text_view_new ());
gtk_widget_show (GTK_WIDGET (self->textview));
gtk_container_add (GTK_CONTAINER (scrolledwindow1), GTK_WIDGET (self->textview));
GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (self->textview), GTK_CAN_FOCUS);
gtk_text_view_set_editable (self->textview, FALSE);
gtk_text_view_set_wrap_mode (self->textview, GTK_WRAP_WORD);
alignment1 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_show (alignment1);
gtk_box_pack_start (GTK_BOX (vbox1), alignment1, FALSE, FALSE, 0);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment1), 3, 3, 0, 0);
hseparator1 = gtk_hseparator_new ();
gtk_widget_show (hseparator1);
gtk_container_add (GTK_CONTAINER (alignment1), hseparator1);
self->entry = gtk_entry_new ();
gtk_widget_show (self->entry);
gtk_box_pack_start (GTK_BOX (vbox1), self->entry, FALSE, FALSE, 0);
GTK_WIDGET_SET_FLAGS (self->entry, GTK_CAN_DEFAULT);
gtk_widget_grab_focus (self->entry);
gtk_widget_grab_default (self->entry);
}
MooPythonConsole *moo_python_console_new (struct _MooPython *python)
{
return MOO_PYTHON_CONSOLE (g_object_new (MOO_TYPE_PYTHON_CONSOLE,
"python", python,
NULL));
}
static void queue_free (GQueue *que)
{
GList *l;
if (!que) return;
for (l = que->head; l != NULL; l = l->next)
g_free (l->data);
g_queue_free (que);
}
static guint queue_len (GQueue *que)
{
GList *l;
guint len = 0;
g_return_val_if_fail (que != NULL, 0);
for (l = que->head; l != NULL; l = l->next)
len++;
return len;
}
static gpointer queue_nth (GQueue *que,
guint n)
{
GList *l;
guint i;
for (i = 0, l = que->head; i < n; ++i, l = l->next) ;
return l->data;
}
#endif /* USE_PYTHON */