The R Project SVN R-packages

Rev

Rev 679 | Blame | Last modification | View Log | Download | RSS feed

/*
 *  R : A Computer Langage for Statistical Data Analysis
 *  Copyright (C) 1998-2004   Lyndon Drake
 *                            and the R Development Core Team
 *
 *  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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
# include <config.h> /* for HAVE_STRING_H / HAVE_STRINGS_H */
#endif

/* Avoid braced-groups warning from -Wall */
#define G_STMT_START do
#define G_STMT_END   while(0)

#include <gdk/gdkkeysyms.h>
#include <stdio.h>

#if defined (HAVE_STRING_H)
# include <string.h>
#elif defined (HAVE_STRINGS_H)
# include <strings.h>
#endif

#include "gtkconsole.h"

enum {
  ARG_0,              /* Skip 0, an invalid argument ID */
  ARG_INPUT_COLOR_GDK,
  ARG_OUTPUT_COLOR_GDK,
  ARG_BG_COLOR_GDK,
  ARG_FONT
};

enum
{
  CONSOLE_LINE_READY,
  CONSOLE_LINE_READY_WITH_VALUE,
  CONSOLE_CHAR_READY,
  CONSOLE_INPUT_ENABLED,
  CONSOLE_INPUT_DISABLED,
  LAST_SIGNAL
};

/* widget stuff */
static void gtk_console_class_init (GtkConsoleClass * klass);
static void gtk_console_init (GtkConsole * console);
static void gtk_console_destroy (GtkObject * object);
static void gtk_console_set_arg(GtkObject* object,
                GtkArg* arg,
                guint arg_id);

/* object arguments */
static void gtk_console_set_bg_color (GtkConsole * console,
                      GdkColor const * color);
static void gtk_console_set_input_color (GtkConsole * console,
                     GdkColor const * color);
static void gtk_console_set_output_color (GtkConsole * console,
                      GdkColor const *color);
static void gtk_console_set_font (GtkConsole * console, gchar const * font);

/* events */
static gint gtk_console_key_press (GtkWidget * widget, GdkEventKey * event);
static void gtk_console_insert_text (GtkConsole * console,
                     const gchar * new_text,
                     gint new_text_length, gint * position);
static void gtk_console_delete_text (GtkConsole * console, gint start_pos,
                     gint end_pos);
static void gtk_console_changed_post (GtkConsole * console);



static GtkWidgetClass *parent_class = NULL;

static guint console_signals[LAST_SIGNAL] = { 0 };


/* ********************* *
 * Basic Class Functions *
 * ********************* */

guint
gtk_console_get_type ()
{
  static guint console_type = 0;

  if (!console_type)
    {
      GtkTypeInfo console_info = {
    "GtkConsole",
    sizeof (GtkConsole),
    sizeof (GtkConsoleClass),
    (GtkClassInitFunc) gtk_console_class_init,
    (GtkObjectInitFunc) gtk_console_init,
    /* reserved */ NULL,
    /* reserved */ NULL,
    (GtkClassInitFunc) NULL,
      };

      console_type = gtk_type_unique (gtk_text_get_type (), &console_info);
    }

  return console_type;
}

static void
gtk_console_class_init (GtkConsoleClass * klass)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass *) klass;

  /* init local data */
  parent_class = gtk_type_class (gtk_text_get_type ());

  /* New signals */
  console_signals[CONSOLE_LINE_READY] =
    gtk_signal_new ("console_line_ready",
            GTK_RUN_LAST,
            object_class->type,
            GTK_SIGNAL_OFFSET (GtkConsoleClass, console_line_ready),
            gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);

  console_signals[CONSOLE_LINE_READY_WITH_VALUE] =
    gtk_signal_new ("console_line_ready_with_value",
            GTK_RUN_LAST,
            object_class->type,
            GTK_SIGNAL_OFFSET (GtkConsoleClass, console_line_ready_with_value),
            gtk_marshal_NONE__STRING, GTK_TYPE_NONE, 1, GTK_TYPE_STRING);

  console_signals[CONSOLE_CHAR_READY] =
    gtk_signal_new ("console_char_ready",
            GTK_RUN_LAST,
            object_class->type,
            GTK_SIGNAL_OFFSET (GtkConsoleClass, console_char_ready),
            gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);

  console_signals[CONSOLE_INPUT_ENABLED] =
    gtk_signal_new ("console_input_enabled",
            GTK_RUN_LAST,
            object_class->type,
            GTK_SIGNAL_OFFSET (GtkConsoleClass,
                       console_input_enabled),
            gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);

  console_signals[CONSOLE_INPUT_DISABLED] =
    gtk_signal_new ("console_input_disabled",
            GTK_RUN_LAST,
            object_class->type,
            GTK_SIGNAL_OFFSET (GtkConsoleClass,
                       console_input_disabled),
            gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, console_signals, LAST_SIGNAL);

  /* setup signals */
  object_class->destroy = gtk_console_destroy;

  klass->console_char_ready = NULL;
  klass->console_line_ready = NULL;
  klass->console_line_ready_with_value = NULL;
  klass->console_input_enabled = NULL;
  klass->console_input_disabled = NULL;


  /* Object arguments */
  gtk_object_add_arg_type("GtkConsole::input_color_gdk",
                          GTK_TYPE_GDK_COLOR, 
                          GTK_ARG_WRITABLE, 
                          ARG_INPUT_COLOR_GDK);
  gtk_object_add_arg_type("GtkConsole::output_color_gdk", 
                          GTK_TYPE_GDK_COLOR, 
                          GTK_ARG_WRITABLE, 
                          ARG_OUTPUT_COLOR_GDK);
  gtk_object_add_arg_type("GtkConsole::bg_color_gdk", 
                          GTK_TYPE_GDK_COLOR, 
                          GTK_ARG_WRITABLE, 
                          ARG_BG_COLOR_GDK);
  gtk_object_add_arg_type("GtkConsole::font", 
                          GTK_TYPE_STRING, 
                          GTK_ARG_WRITABLE, 
                          ARG_FONT);

  object_class->set_arg = gtk_console_set_arg;

}

static void
gtk_console_init (GtkConsole * console)
{
  /* Initialise variables */
  console->history = NULL;
  console->history_num_items = 0;
  console->history_cur = NULL;

  console->input_enabled = FALSE;
  console->input_start_index = FALSE;
  console->line_available = FALSE;

  console->buffer_type = CONSOLE_BUF_NONE;
  console->buffer_index = 0;

  /* register object arguments */
  

  /* to keep track of changes */
  gtk_signal_connect (GTK_OBJECT (console), "insert_text",
              GTK_SIGNAL_FUNC (gtk_console_insert_text), NULL);
  gtk_signal_connect (GTK_OBJECT (console), "delete_text",
              GTK_SIGNAL_FUNC (gtk_console_delete_text), NULL);
  gtk_signal_connect (GTK_OBJECT (console), "changed",
              GTK_SIGNAL_FUNC (gtk_console_changed_post), NULL);
  gtk_signal_connect (GTK_OBJECT (console), "key_press_event",
              GTK_SIGNAL_FUNC (gtk_console_key_press), NULL);
}

GtkWidget *
gtk_console_new (GtkAdjustment * hadj, GtkAdjustment * vadj)
{
  GtkConsole *console;

  console = gtk_type_new (gtk_console_get_type ());

  gtk_text_set_adjustments (GTK_TEXT (console), hadj, vadj);

  return GTK_WIDGET (console);
}

static void
gtk_console_destroy (GtkObject * object)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (object));


  GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
gtk_console_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GtkConsole *console;
  GdkColor *pcolor;

  console = GTK_CONSOLE (object);
  pcolor = GTK_VALUE_BOXED(*arg);  

  switch (arg_id)
    {
    case ARG_INPUT_COLOR_GDK:
      gtk_console_set_input_color (console, pcolor);
      break;
    case ARG_OUTPUT_COLOR_GDK:
      gtk_console_set_output_color (console, pcolor);
      break;
    case ARG_BG_COLOR_GDK:
      gtk_console_set_bg_color (console, pcolor);
      break;
    case ARG_FONT:
      gtk_console_set_font (console, GTK_VALUE_STRING (*arg));
      break;
    default:
      break;
    }
}

/* ************************************** *
 * Public functions to access the console *
 * ************************************** */

/* Applications can use this to read the input.  It's
   probably only useful if a signal has been received
   indicating that there's something to read. */
void
gtk_console_read (GtkConsole * object, gchar * buf, guint buf_len,
          gboolean add_to_history)
{
  gchar *text_chars;
  gchar *term_ptr;
  gchar *history_buf;

  g_return_if_fail (buf != NULL);
  /* make buf null-terminated, in case we can't copy */
  *buf = '\0';

  g_return_if_fail (buf_len > 0);
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (object));

  /* copy up to buf_len characters into buf,
   * starting from input_start_index */
  text_chars =
    gtk_editable_get_chars (GTK_EDITABLE (object), object->input_start_index,
                -1);

  g_return_if_fail (text_chars != NULL);

  strncpy (buf, text_chars, buf_len);

  /* add command to history */
  term_ptr = strchr (text_chars, '\n');
  if ((add_to_history == TRUE) && (term_ptr != NULL) && (*text_chars != '\n'))
    {
      history_buf = g_malloc (term_ptr - text_chars + 1);
      *term_ptr = '\0';
      strncpy (history_buf, text_chars, term_ptr - text_chars + 1);

      GTK_CONSOLE (object)->history =
    g_list_prepend (GTK_CONSOLE (object)->history, history_buf);
      GTK_CONSOLE (object)->history_num_items++;
    }

  g_free (text_chars);
}

/* Prompt the user for input */
void
gtk_console_enable_input (GtkConsole * object, gchar * prompt,
              guint prompt_len)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (object));

  gtk_console_flush (object);

  gtk_text_freeze (GTK_TEXT (object));

  /* Enable input */
  object->input_enabled = TRUE;

  /* Append prompt to end of text box */
  if ((prompt != NULL) && (*prompt != '\0') && (prompt_len != 0))
    {
      gtk_text_set_point (GTK_TEXT (object),
              gtk_text_get_length (GTK_TEXT (object)));

      gtk_text_insert (GTK_TEXT (object), NULL, NULL, NULL, prompt,
               prompt_len > 0 ? prompt_len : strlen(prompt));
    }

  /* Update input_start_index */
  object->input_start_index = gtk_text_get_length (GTK_TEXT (object));

  /* Move point to end of text box */
  gtk_text_set_point (GTK_TEXT (object),
              gtk_text_get_length (GTK_TEXT (object)));

  gtk_text_thaw (GTK_TEXT (object));

  gtk_editable_set_position (GTK_EDITABLE (object),
                 gtk_text_get_length (GTK_TEXT (object)));

  object->buffer_index = 0;

  /* reset history index */
  object->history_cur = NULL;
}

/* Disable input */
void
gtk_console_disable_input (GtkConsole * object)
{
  g_return_if_fail (object);
  g_return_if_fail (GTK_IS_CONSOLE (object));

  object->input_enabled = FALSE;
}

static void
_write_buffer (GtkConsole *object, gchar *str, guint str_len)
{
  /* Write str to output buffer of object.  If str is too long,
     then it is split into multiple writes */

  int str_written = 0;

  if(str_len < 0 && str)
    str_len = strlen(str);
 
  /* Run out of buffer space? Then flush buffer */
  if ((object->buffer_index > 0) && 
      (object->buffer_index + str_len > CONSOLE_MAX_BUF - 1))
    {
      gtk_console_flush (object);
    }
  
  /* Copy str to output buffer */
  strncpy (object->out_buf + object->buffer_index, str,
       CONSOLE_MAX_BUF - object->buffer_index - 1);

  str_written = MIN (CONSOLE_MAX_BUF - object->buffer_index - 1, str_len);
  object->buffer_index += str_written;
  object->out_buf[object->buffer_index] = '\0';

  /* Didn't write whole of str? Then try again */
  if (str_written < str_len)
    _write_buffer(object, str + str_written, str_len - str_written);
}

void
gtk_console_write (GtkConsole * object, gchar * buf, guint buf_len)
{
  /* NOTE: This also disables input.
     It must be enabled somewhere. Where? - MTP */

  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (object));

  gtk_console_disable_input (object);

  _write_buffer(object, buf, buf_len);
  switch (object->buffer_type)
    {
    case CONSOLE_BUF_NONE:
      /* Always flush */
      gtk_console_flush (object);
      break;
    case CONSOLE_BUF_LINE:
      /* Flush if output contains a newline character  */
      if (strchr (object->out_buf, '\n') != NULL)
    {
      gtk_console_flush (object);
    }
      break;
    case CONSOLE_BUF_BLOCK:
      /* Don't flush until buffer is full (handled by _write_buffer) */
      break;
    default:
      g_assert_not_reached();
    }
}

void
gtk_console_flush (GtkConsole * object)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (object));

  gtk_text_freeze (GTK_TEXT (object));

  /* Append up to buf_len characters from buf to end of text box */
  gtk_text_set_point (GTK_TEXT (object),
              gtk_text_get_length (GTK_TEXT (object)));
  gtk_text_insert (GTK_TEXT (object), NULL,  
           &(object->output_color), NULL,
           object->out_buf,
           strlen (object->out_buf));
  /* Move point to end of text box */
  gtk_text_set_point (GTK_TEXT (object),
              gtk_text_get_length (GTK_TEXT (object)));

  gtk_text_thaw (GTK_TEXT (object));

  gtk_editable_set_position (GTK_EDITABLE (object),
                 gtk_text_get_length (GTK_TEXT (object)));

  object->buffer_index = 0;
  object->out_buf[0] = '\0';
}

/* FIXME: return an error message if things go wrong */
/* FIXME: if errmsg is null, don't return an error message */
/* FIXME: read maxitems from history first, and use them to */
/*        fill up to maxitems after current history is written. */
/*        However, the simplest way to do that is for the app */
/*        to restore the history first. */
gboolean
gtk_console_save_history (GtkConsole * object, gchar * filename,
              guint maxitems, gchar * errmsg)
{
  FILE *hist_file;
  GList *cur_item;
  gint n;

  g_return_val_if_fail (object != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSOLE (object), FALSE);

  hist_file = fopen (filename, "w");

  /* check file OK */
  if (hist_file == NULL)
    return FALSE;

  if (maxitems > object->history_num_items)
    maxitems = object->history_num_items;

  cur_item = g_list_last (object->history);

  for (n = 0; (n < maxitems) && (cur_item != NULL);
       n++, cur_item = cur_item->prev)
    {
      fputs (cur_item->data, hist_file);
      fputs ("\n", hist_file);
    }
  fclose (hist_file);

  return TRUE;
}

void
gtk_console_clear_history (GtkConsole * object)
{
  object->history = NULL;
  object->history_num_items = 0;
  object->history_cur = NULL;
}


gboolean
gtk_console_restore_history (GtkConsole * object, gchar * filename,
                 guint maxitems, gchar * errmsg)
{
  FILE *hist_file;
  gchar line_buf[CONSOLE_MAX_BUF];
  gchar *history_buf, *term_ptr;
  gint n, buf_len;

  g_return_val_if_fail (object != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSOLE (object), FALSE);

  hist_file = fopen (filename, "r");

  /* check file ok */
  if (hist_file == NULL)
    return FALSE;

  for (n = 0; (n < maxitems) && (!ferror (hist_file)) && (!feof (hist_file));
       n++)
    {
      /* read line */
      line_buf[0] = '\0';
      if (fgets (line_buf, CONSOLE_MAX_BUF - 1, hist_file) != NULL)
    {
      line_buf[CONSOLE_MAX_BUF - 1] = '\0';

      /* remove \n if it's there */
      term_ptr = strchr (line_buf, '\n');
      if (term_ptr != NULL)
        *term_ptr = '\0';

      /* copy to real place */
      buf_len = strlen (line_buf) + 1;
      history_buf = g_malloc (buf_len);
      *history_buf = '\0';
      strncpy (history_buf, line_buf, buf_len - 1);
      *(history_buf + buf_len - 1) = '\0';

      /* add it to the list */
      object->history = g_list_prepend (object->history, history_buf);

      object->history_num_items++;
    }
    }

  object->history_cur = NULL;

  return TRUE;
}

guint
gtk_console_get_input_start_index (GtkConsole * object)
{
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (GTK_IS_CONSOLE (object), 0);

  return object->input_start_index;
}

gboolean
gtk_console_get_line_available (GtkConsole * object)
{
  g_return_val_if_fail (object != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSOLE (object), FALSE);

  return object->line_available;
}

gboolean
gtk_console_get_input_enabled (GtkConsole * object)
{
  g_return_val_if_fail (object != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSOLE (object), FALSE);

  return object->input_enabled;
}

static void
gtk_console_set_bg_color (GtkConsole * console, GdkColor const * color)
{
  GtkStyle *new_style;

  g_return_if_fail (GTK_IS_CONSOLE(console));

  console->bg_color = *color;

  new_style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(console)));
  new_style->base[GTK_STATE_NORMAL] = *color;
  gtk_widget_set_style(GTK_WIDGET(console), new_style);
}

static void
gtk_console_set_input_color (GtkConsole * console, GdkColor const * color)
{
  GtkStyle *new_style;

  g_return_if_fail (GTK_IS_CONSOLE(console));

  console->input_color = *color;

  new_style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(console)));
  new_style->text[GTK_STATE_NORMAL] = *color;
  gtk_widget_set_style(GTK_WIDGET(console), new_style);
}

static void
gtk_console_set_output_color (GtkConsole * console, GdkColor const * color)
{
  g_return_if_fail (GTK_IS_CONSOLE(console));

  console->output_color = *color;
}

static void
gtk_console_set_font (GtkConsole * console, gchar const *font)
{
  GtkStyle *new_style;

  new_style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(console)));
  gdk_font_unref(new_style->font);
  new_style->font = gdk_fontset_load(font);
  gtk_widget_set_style(GTK_WIDGET(console), new_style);
}


/* *************** *
 * Signal handlers *
 * *************** */

static void
key_gdk_up (GtkConsole * console)
{
  gint insert_pos;
  gchar *history_buf;
  GList *next = NULL;

  if (console->history_cur == NULL)
    {
      next = console->history;
    }
  else if (console->history_cur->next != NULL)
    {
      next = console->history_cur->next;
    }

  if (next != NULL)
    {
      console->history_cur = next;

      /* delete any existing input */
      if (gtk_text_get_length (GTK_TEXT (console)) >
      console->input_start_index)
    gtk_editable_delete_text (GTK_EDITABLE (console),
                  console->input_start_index,
                  gtk_text_get_length (GTK_TEXT (console)));

      history_buf = console->history_cur->data;

      if (history_buf != NULL)
    {
      insert_pos = gtk_text_get_length (GTK_TEXT (console));
      gtk_editable_insert_text (GTK_EDITABLE (console), history_buf,
                    strlen (history_buf), &insert_pos);
    }
    }
}

static void
key_gdk_down (GtkConsole * console)
{
  gint insert_pos;
  gchar *history_buf;

  if (console->history_cur != NULL)
    {
      console->history_cur = console->history_cur->prev;

      /* delete any existing input */
      if (gtk_text_get_length (GTK_TEXT (console)) >
      console->input_start_index)
    gtk_editable_delete_text (GTK_EDITABLE (console),
                  console->input_start_index,
                  gtk_text_get_length (GTK_TEXT (console)));

      if (console->history_cur != NULL)
    {
      history_buf = console->history_cur->data;

      insert_pos = gtk_text_get_length (GTK_TEXT (console));
      gtk_editable_insert_text (GTK_EDITABLE (console), history_buf,
                    strlen (history_buf), &insert_pos);
    }
    }
}

static void 
_extend_selection_back (GtkWidget * widget, int start, int pos)
{
  /* Utility function for extending selection (backwards only) */
  GtkEditable *editable;

  g_return_if_fail (widget);
  g_return_if_fail (GTK_IS_EDITABLE (widget));

  editable = GTK_EDITABLE (widget);
  if (editable->has_selection) 
    {
      if (pos == editable->selection_start_pos)
    /* Sanity check */
    {
      gtk_editable_select_region(editable, start,
                     editable->selection_end_pos);
    }
    }
  else
    {
      gtk_editable_select_region (editable, start, pos);
    }
}

/* FIXME: Completion will have to be accomplished by a callback.
   The callback should have the same syntax as the readline
   completion callback. */
static gint
gtk_console_key_press (GtkWidget * widget, GdkEventKey * event)
{
  gint pos, start;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSOLE (widget), FALSE);

  if (GTK_CONSOLE (widget)->input_enabled == FALSE)
    return FALSE;
  
  pos = gtk_editable_get_position (GTK_EDITABLE (widget));
  start = GTK_CONSOLE (widget)->input_start_index;

  if (pos >= start)
    /* stuff that we only change if the point is in the input area */
    {
      gboolean signal_caught = FALSE;
      if (event->state & GDK_CONTROL_MASK)
    {
      switch(event->keyval)
        {
        case 'A': case 'a':
          /* Beginning of line */
          gtk_editable_set_position (GTK_EDITABLE (widget), start);
          if(event->state & GDK_SHIFT_MASK)
        {
          _extend_selection_back (widget, start, pos);
        }
          signal_caught = TRUE;
          break;
        
        case 'B': case 'b': /* Backward one character */
        case GDK_Left:      /* Backward one word */
        case 'H': case 'h': /* Delete backward character */
        case 'W': case 'w': /* Delete backwards word */
          if (pos == start)
        {
          signal_caught = TRUE;
        }
          break;

        case 'N': case 'n':
          /* Next line => next history item*/
          key_gdk_down (GTK_CONSOLE (widget));
          signal_caught = TRUE;
          break;

        case 'P': case 'p':
          /* Previous line => previous history item */
          key_gdk_up (GTK_CONSOLE (widget));
          signal_caught = TRUE;
          break;

        case 'U': case 'u':
          /* Delete line */
          gtk_editable_delete_text(GTK_EDITABLE(widget),
                       start,
                       gtk_text_get_length(GTK_TEXT(widget)));
          signal_caught = TRUE;
          break;
        }
    }
      else if (event->state & GDK_MOD1_MASK)
    {
      switch (event->keyval)
        {
        case 'B': case 'b':
          /* Back one word */
          if (pos == start)
        {
          signal_caught = TRUE;
        }
          break;
        }
    }
      else /* No control or alt key */
    {
      switch (event->keyval)
        {
        case GDK_Tab:
          /* do completion */
          break;

        case GDK_Up:
          /* previous history item */
          key_gdk_up (GTK_CONSOLE (widget));
          signal_caught = TRUE;
          break;
          
        case GDK_Down:
          /* next history item */
          key_gdk_down (GTK_CONSOLE (widget));
          signal_caught = TRUE;
          break;

        case GDK_Left:
          if (pos == start)
        {
          signal_caught = TRUE;
        }
          break;

        case GDK_Home:
          if (!(event->state & GDK_SHIFT_MASK))
        {
          gtk_editable_set_position (GTK_EDITABLE (widget), start);
          signal_caught = TRUE;

        }
          break;
        }
    }
      if (signal_caught)
    {
      gtk_signal_emit_stop_by_name (GTK_OBJECT(widget),
                    "key_press_event");
      return TRUE;
    }
    }
  
  if (GTK_CONSOLE (widget)->input_enabled == TRUE)
    /* things that apply wherever the point is */
    {
      switch (event->keyval)
    {

    case GDK_Return:
      gtk_editable_set_position (GTK_EDITABLE (widget),
                     gtk_text_get_length (GTK_TEXT (widget)));
      GTK_CONSOLE (widget)->line_available = TRUE;
      break;
      
    }
    }

  return FALSE;
}

static void
gtk_console_insert_text (GtkConsole * console,
             const gchar * new_text,
             gint new_text_length, gint * position)
{
  g_return_if_fail (console != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (console));

  if (console->input_enabled == FALSE)
    {
      gtk_signal_emit_stop_by_name (GTK_OBJECT (console), "insert_text");
    }
  else if (*position < console->input_start_index)
    {
      *position = console->input_start_index;
    }
}

static void
gtk_console_delete_text (GtkConsole * console, gint start_pos, gint end_pos)
{
  g_return_if_fail (console != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (console));

  if ((console->input_enabled == FALSE)
      || (start_pos < console->input_start_index)
      || (end_pos < console->input_start_index))
    {
      gtk_signal_emit_stop_by_name (GTK_OBJECT (console), "delete_text");
      gtk_widget_queue_draw (GTK_WIDGET (console));
    }
}

static void
gtk_console_changed_post (GtkConsole * console)
{
  g_return_if_fail (console != NULL);
  g_return_if_fail (GTK_IS_CONSOLE (console));

  if ((gtk_text_get_length (GTK_TEXT (console)) -
       console->input_start_index) > 0)
    {
      gtk_signal_emit (GTK_OBJECT (console),
               console_signals[CONSOLE_CHAR_READY]);
    }

  if (console->line_available == TRUE)
    {
      char *line;
      line = gtk_editable_get_chars (GTK_EDITABLE (console), console->input_start_index, -1);
      gtk_signal_emit (GTK_OBJECT (console),
               console_signals[CONSOLE_LINE_READY_WITH_VALUE], line);
      g_free(line);
      gtk_signal_emit (GTK_OBJECT (console),
               console_signals[CONSOLE_LINE_READY]);
      console->line_available = FALSE;
    }

  return;
}