Logo Search packages:      
Sourcecode: papaya version File versions  Download package

VTEVT.cpp

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gdk/gdkkeysyms.h>

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif

#include <vte/vte.h>


#include "mudclient.h"
#include "callbacks.h"
#include "Prefs.h"
#include "Message.h"
#include "SystemColour.h"
#include "PluginHandler.h"
#include "ColouredLabel.h"
#include "GTKTwoVT.h"
#include "BaseWindow.h"

#include "CommandInterpreter.h"

#include "TeloptFilter.h"

extern PluginHandler * phandler;
extern CommandInterpreter * commandInterpreter;
extern BaseWindow * mainWindow;

#define ESCAPE '\033'
#define PAGE_UP 65365
#define PAGE_DOWN 65366
#define TAB 65289
#define BACKSPACE 65288

gboolean prompt_expose(GtkWidget *, GdkEventExpose *, gpointer);

static gboolean window_title_changed(GtkWidget * widget, gpointer data) {
  Connection * conn = (Connection *) data;
  GTKTwoVT * vt = (GTKTwoVT *)conn->getVT();
  vt->setTitleBar((gchar *)vte_terminal_get_window_title(VTE_TERMINAL(conn->getVT()->getOutput())));
  return true;
}

static gboolean icon_title_changed(GtkWidget * widget, gpointer data) {
  Connection * conn = (Connection *) data;
  GTKTwoVT * vt = (GTKTwoVT *)conn->getVT();
  vt->setIcon((gchar *)vte_terminal_get_icon_title(VTE_TERMINAL(conn->getVT()->getOutput())));
  return true;
}

/*
  Then, you just need to know that the Editable widgets don't even to
  try to paste when not editable. So you need to connect to
  "button_press_event", and do a gtk_selection_convert() yourself.
  Then, you need to connect to "selection_received" to get the data when
  it arrives. Your "selection_received" handler then needs to
  do a gtk_signal_emit_stop_by_name(), so that the GtkEditable
  default selection_received handler doesn't get called.
*/

void papaya_selection_received(GtkWidget * widget, GtkSelectionData * selection_data, gpointer data) {

  GtkWidget * input = mainWindow->getCurrentConnection()->getVT()->getInput();

  if (selection_data->length < 0)
    return;

  char buf[16384];
  buf[0] = '\0';
  
  if (gtk_entry_get_text(GTK_ENTRY(input)))
    strcat(buf, gtk_entry_get_text(GTK_ENTRY(input))); 

  if (selection_data->type == gdk_atom_intern("STRING", FALSE)) {
    selection_data->data[selection_data->length] = 0;
    strcat(buf, (char *)selection_data->data);
  } else {
    if (selection_data->type == gdk_atom_intern("COMPOUND_TEXT", FALSE)
      || selection_data->type == gdk_atom_intern("TEXT", FALSE)) {
      gchar **list;
      gint count;
      gint i;
      
      count = gdk_text_property_to_text_list (selection_data->type,
                                    selection_data->format, 
                                    selection_data->data,
                                    selection_data->length,
                                    &list);
      for (i = 0; i < count; i++)
      if (list[i])
        strcat(buf, list[i]);
    }
  }
 
  gtk_entry_set_text(GTK_ENTRY(input), buf);
  
}

void output_size_changed(GtkWidget * widget, GdkEvent * event, gpointer data) {

  Connection * c = (Connection *)data;

  // Tell this connection to redo NAWS.
  TeloptFilter * filter = static_cast<TeloptFilter*>(c->getSocket()->inputFilters.findFilter("TelnetOptions"));
  if (filter)
    filter->do_naws();

  //  VT * vt = c->getVT();
  //  vt->scroll();

}

gboolean output_button_pressed(GtkWidget * widget, GdkEventButton * event, gpointer data) {

  GdkAtom ctext_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);

  switch (event->type) {
  case GDK_BUTTON_PRESS:
    switch (event->button) {
    case 2:
      
      gtk_selection_convert(widget, GDK_SELECTION_PRIMARY, ctext_atom, GDK_CURRENT_TIME);
      
      return true;
    }
    return false;

  case GDK_2BUTTON_PRESS:
    switch (event->button) {
    case 1:

      
      return false;
    }

  /* k0n pointed out that some combinations of GDK/C++ spew warnings if
   * the default option is not here.  (switch (enumeration) requires
   * a case statement for each item in the enumeration.)
   */
  default:
    return false;
  }

}


/// Start conversion here.




GTKTwoVT::GTKTwoVT(Connection * c) : VT(c) {

  custom_tag_list = NULL;
  input = (GtkWidget *)NULL;

  //  appended_to_filtered = false;
  appended_to_output = false;
  //  filtered_lines = 0;
  output_lines = 0;

  // Initialise the SystemColour class which contains the colour data.
  systemColour = new SystemColour(conn->queryPreferences());
  prompt_on_input_line = conn->queryPreferences()->getPreferenceBoolean("PromptOnInputLine");
  character_set = conn->queryPreferences()->getPreference("charset");

}

void GTKTwoVT::initialise() {
  initTextArea();
  setTerminalSettings(conn->queryPreferences());
}

GTKTwoVT::~GTKTwoVT() {
  delete prompt;
  delete systemColour;
}

void GTKTwoVT::initTextArea() {

  vbox = gtk_vbox_new(false, 2);

  terminal = vte_terminal_new();
  vte_terminal_set_font_from_string(VTE_TERMINAL(terminal),
                            conn->queryPreferences()->getPreference("OutputFont"));
  gtk_widget_show(terminal);
  gtk_widget_set_double_buffered(terminal, true);

  // Create the scrollbar
  GtkWidget * scrollbar = gtk_vscrollbar_new(VTE_TERMINAL(terminal)->adjustment);
  gtk_widget_show(scrollbar);

  // Create a hbox to hold the scrollbar and terminal
  GtkWidget * terminal_hbox = gtk_hbox_new(false, 2);
  gtk_widget_show(terminal_hbox);
  gtk_box_pack_start(GTK_BOX(terminal_hbox), terminal, true, true, 0);
  gtk_box_pack_start(GTK_BOX(terminal_hbox), scrollbar, false, false, 0);

  // Create the output VBOX, for putting the terminal_hbox and prompt into.
  output_vbox = gtk_vbox_new(false, 2);
  gtk_widget_show(output_vbox);

  gtk_box_pack_start(GTK_BOX(output_vbox), terminal_hbox, TRUE, TRUE, 0);

  // Create the prompt widget.
  prompt = new ColouredLabel(conn, "");
  prompt->createWidget();
  gtk_widget_show(prompt->getWidget());
  
  // Create the text entry widget.
  input = gtk_entry_new();
  gtk_widget_show (input);

  // Connect up a pile of signals.
  g_signal_connect_data (input, "activate",
                   GTK_SIGNAL_FUNC (papaya_input_entry_activate),
                   this, NULL, (GConnectFlags)0);
  
  g_signal_connect_data (input,
                   "key_press_event",
                   GTK_SIGNAL_FUNC(papaya_keypress),
                   NULL,
                   NULL,
                   G_CONNECT_AFTER);

  /*                 
  g_signal_connect_data (terminal,
                   "key_press_event",
                   GTK_SIGNAL_FUNC(papaya_keypress),
                   (gpointer)NULL,
                   NULL,
                   (GConnectFlags) 0);
  */

  g_signal_connect_data(terminal, "selection_received", GTK_SIGNAL_FUNC(papaya_selection_received), NULL, NULL, (GConnectFlags)0);

  gtk_box_pack_start(GTK_BOX(vbox), output_vbox, TRUE, TRUE, 0);

  input_vbox = gtk_vbox_new(false, 2);
  gtk_widget_show(input_vbox);

  if (prompt_on_input_line) {
    GtkWidget * pbox = gtk_hbox_new(false, 2);
    gtk_box_pack_start(GTK_BOX(pbox), prompt->getWidget(), false, false, 0);
    gtk_box_pack_start(GTK_BOX(pbox), input, TRUE, TRUE, 0);
    gtk_widget_show(pbox);
    
    gtk_box_pack_start(GTK_BOX(input_vbox), pbox, false, false, 0);
  } else {
    gtk_box_pack_start(GTK_BOX(input_vbox), prompt->getWidget(), false, false, 0);
    gtk_box_pack_start(GTK_BOX(input_vbox), input, false, false, 0);
  }

  gtk_box_pack_start(GTK_BOX(vbox), input_vbox, false, false, 0);

  gtk_container_add(GTK_CONTAINER(box), vbox);
  gtk_widget_show(vbox);

  g_signal_connect_data(GTK_OBJECT(terminal), "button_press_event",
                  GTK_SIGNAL_FUNC(output_button_pressed),
                  input, NULL, (GConnectFlags)0);

  // Need to connect up a signal for when the size of this window is changed.
  g_signal_connect_data(GTK_OBJECT(terminal), "size_allocate",
                  GTK_SIGNAL_FUNC(output_size_changed), conn,
                  NULL, (GConnectFlags)0);


  g_signal_connect_data(GTK_OBJECT(terminal), "window_title_changed",
                  GTK_SIGNAL_FUNC(window_title_changed), conn,
                  NULL, (GConnectFlags)0);

  g_signal_connect_data(GTK_OBJECT(terminal), "icon_title_changed",
                  GTK_SIGNAL_FUNC(icon_title_changed), conn,
                  NULL, (GConnectFlags)0);



}

void GTKTwoVT::hideInput() {
  gtk_widget_hide(input_vbox);
}

void GTKTwoVT::showInput() {
  gtk_widget_show(input_vbox);
}

char * GTKTwoVT::zapChars(char * text) {

  char * pc;

  if (*text == '\r')
    text++;

  pc = text;

  while (*pc != '\0') {
    if (*pc == '\r') { // || (unsigned char)*pc > 127) {
      memmove(pc, pc + 1, strlen(pc + 1) + 1);
      // Don't increment pc, as we just moved the next char to there
      continue;
    }

    if (*pc == MAGIC_PROMPT) {
      memmove(pc, pc + 1, strlen(pc + 1) + 1);
      continue;
    }

    pc++;
  }

  return text;
}

/**
 * Any text to be considered for the display will be sent to this method.
 * This includes prompts, which the VT must remove if it wishes to display
 * them in a separate area.
 */

void GTKTwoVT::append(char * text) {
  char * pc;
  
  snarfPrompt(text); // note: modifies text in place & sets prompt

  pc = zapChars(text);

  if (pc[0] != '\0') // strlen(pc) > 0
    mainWindow->nextText(conn);

  insertText(text);
}

char * GTKTwoVT::getColourCodes(char * s) {

  char * pc;
  static char buf[16384];
  int count = 0;

  while ((pc = strchr(s, ESCAPE))) {

    buf[count++] = *pc++; // ESCAPE
    buf[count++] = *pc++; // [
    while (*pc && !isalpha(*pc))
      buf[count++] = *pc++;

    if (*pc)
      buf[count++] = *pc++; // alpha terminating character

    s = pc;
  }

  buf[count] = '\0';

  return buf;
}

void GTKTwoVT::snarfPrompt(char * s)
{
    // Find last prompt-end marker
    char *marker = strrchr(s, MAGIC_PROMPT);
    if (!marker) // no eol to be seen, no changes needed.
      return;

    // Find last line-end before marker
    char *pc;
    for (pc = marker - 1; pc >= s; --pc)
      if (*pc == '\n')
          break;

    ++pc; // point to start of line (or buffer if no line-end found)

    // Take a copy of the prompt.
    int prompt_len = marker - pc;
    char *prompt = new char[prompt_len + 1];
    memcpy(prompt, pc, prompt_len);
    prompt[prompt_len] = 0;

    setPrompt(prompt);

    // Replace prompt with only the color codes from it.
    strcpy(pc, getColourCodes(prompt)); // guaranteed to be smaller
    strcpy(pc+strlen(pc), marker+1);
    delete[] prompt;
}

/**
 * Sets the prompt to 'text', without stripping colour codes as
 * prompt understands these now.
 */

void GTKTwoVT::setPrompt(char * text) {
  
  phandler->processPromptFilters(conn, text);
  if (strlen(text) == 0)
    return;

  char * pr = strdup(text); // Assume it isn't safe to edit text directly.
  prompt->setText(pr);
  free(pr);
}

void GTKTwoVT::insertText(char * text) {

  // Figure out which buffer we're writing to.
  GtkTextBuffer * target_buffer;
  gboolean free_it = false;

  gchar * output = NULL;
  gsize read, written;
  GError * error = NULL;

  if (!character_set)
    character_set = "ISO-8859-1";

  if (character_set[0] != '\0') // Allow path with no conversion at all with charset blank.
    output = g_convert(text, strlen(text), "UTF-8", character_set,
                   &read, &written, &error);

  if (output) {
#ifndef WIN32
    free_it = true;
#endif
    text = output;
  }

  vte_terminal_feed(VTE_TERMINAL(terminal), text, strlen(text));
  if (*text != '\0' && text[strlen(text) - 1] == '\n')
    vte_terminal_feed(VTE_TERMINAL(terminal), "\r", 1);

  if (free_it)
    g_free(text);
  return;
}

void GTKTwoVT::scroll() {
}

// @@ Perhaps an expose event should trigger this.

void StringToRGB(char *, gushort *, gushort *, gushort *);

GtkWidget * GTKTwoVT::getInput() {
  return input;
}

GtkWidget * GTKTwoVT::getOutput() {
  return terminal;
}

GtkWidget * GTKTwoVT::getBox() {
  return vbox;
}

GtkWidget * GTKTwoVT::getPrompt() {
  return prompt->getWidget();
}

int GTKTwoVT::width() {
  return vte_terminal_get_column_count(VTE_TERMINAL(terminal));
}

int GTKTwoVT::height() {
  return vte_terminal_get_row_count(VTE_TERMINAL(terminal));
}

void GTKTwoVT::setInput(char * s) {
  gtk_entry_set_text(GTK_ENTRY(input), s);
  gtk_editable_set_position(GTK_EDITABLE(input), -1);
}

/**
 * The preferences have been updated, so let's check the settings.
 */

void GTKTwoVT::prefsUpdated() {

  // Update the colour table stored in SystemColour.
  Prefs * prefs = conn->queryPreferences();
  setTerminalSettings(prefs);

  // This causes NAWS to be resent.
  output_size_changed(NULL, NULL, conn);

}

void GTKTwoVT::setTerminalSettings(Prefs * prefs) {  

  // Update the colour table.
  systemColour->update(prefs);

  // Is scrollback limited.
  if (conn->queryPreferences()->getPreferenceBoolean("UseMaxLines"))
    vte_terminal_set_scrollback_lines(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceInteger("MaxLines"));
  else
    vte_terminal_set_scrollback_lines(VTE_TERMINAL(terminal), 1000000); // Infinite

  // Set beep and bold preferences.
  vte_terminal_set_allow_bold(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceBoolean("UseBold"));
  vte_terminal_set_audible_bell(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceBoolean("Beep"));
  vte_terminal_set_visible_bell(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceBoolean("Beep"));

  // Set scroll preferences.
  vte_terminal_set_scroll_on_output(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceBoolean("ScrollOnInput"));
  vte_terminal_set_scroll_on_keystroke(VTE_TERMINAL(terminal), conn->queryPreferences()->getPreferenceBoolean("ScrollOnCommand"));

  // Change the font on the font input.
  PangoFontDescription * font = pango_font_description_from_string(conn->queryPreferences()->getPreference("InputFont"));
  gtk_widget_modify_font(getInput(), font);
  pango_font_description_free(font);

  // Set the appropriate font for the output area.
  vte_terminal_set_font_from_string(VTE_TERMINAL(terminal),
                            conn->queryPreferences()->getPreference("OutputFont"));

  // Cache a copy of the character set.
  character_set = conn->queryPreferences()->getPreference("charset");

  int i;
  
  // This VT should have its own colour table, rather than using one
  // stored in the prefs, the only thing stored in prefs should be the
  // colour specifications.
  struct colour_table * table = systemColour->getColourTable();

  // Set the foreground colour of the terminal.
  // Will be in the range of 0 to 8 (8 = transparent)
  int index = conn->queryPreferences()->getPreferenceInteger("DefaultForegroundColour");
  GdkColor color;
  if (index == -1 || index == 8) {
    StringToRGB(conn->queryPreferences()->getPreference("Background_RGB"),
            &(color.red), &(color.green), &(color.blue));
  } else {
    color.red = table[index].red;
    color.green = table[index].green;
    color.blue = table[index].blue;
  }
  vte_terminal_set_color_foreground(VTE_TERMINAL(terminal), &color);
  
  // Set the background colour of the terminal.
  index = conn->queryPreferences()->getPreferenceInteger("DefaultBackgroundColour");
  if (index == -1 || index == 8) {
    StringToRGB(conn->queryPreferences()->getPreference("Background_RGB"),
            &(color.red), &(color.green), &(color.blue));
  } else {
    color.red = table[index].red;
    color.green = table[index].green;
    color.blue = table[index].blue;
  }
  vte_terminal_set_color_background(VTE_TERMINAL(terminal), &color);

  // Allocate on the stack as vte_terminal takes a copy.
  GdkColor terminal_colors[16];
  for (i = 0; i < 16; i++) {
    terminal_colors[i].red = table[i].red;
    terminal_colors[i].green = table[i].green;
    terminal_colors[i].blue = table[i].blue;
    gdk_color_alloc(gdk_colormap_get_system(), &terminal_colors[i]);
  }
  vte_terminal_set_colors(VTE_TERMINAL(terminal), NULL, NULL, terminal_colors, 16);

}

void GTKTwoVT::keypressEvent(GtkWidget * widget, GdkEventKey * event) {
  papaya_keypress(widget, event);

  // We need to detect which widget currently has focus.  If it's not the
  // input, then we need to switch focus and append the character.

  if (GTK_WIDGET_HAS_FOCUS(widget))
    return;

  focus();

  // append the character.
  gchar * current_input = (gchar *)gtk_entry_get_text(GTK_ENTRY(getInput()));
  gchar * new_input = (gchar *)g_malloc(strlen(current_input) + 2);
  snprintf(new_input, strlen(current_input) + 2, "%s%c", current_input, event->keyval);
  setInput(new_input);
  g_free(new_input);

}

int papaya_keypress(GtkWidget * widget, GdkEventKey * event) {

  Connection * c = mainWindow->getCurrentConnection();
  if (!c)
    return 0;

  /* If this is line mode, check for command history. */
  if (c->getVT()->getMode() == VTLineMode) {
    switch(event->keyval) {

    case P:
      if (!(event->state & GDK_CONTROL_MASK))
      break;
    case UP_ARROW:
      c->getVT()->setInput(c->getHistory()->getPrev());
      return 1;

    case N:
      if (!(event->state & GDK_CONTROL_MASK))
      break;
    case DOWN_ARROW:
      c->getVT()->setInput(c->getHistory()->getNext());
      return 1;

    }
    return 0;
  }

  if (event->keyval == ENTER || event->keyval == KP_ENTER) {
    c->getSocket()->write("\r\0", 2);
    return 1;
  }

  if (event->keyval == TAB) {
    printf ("Sending horizontal tab.\n");
    c->getSocket()->write("\011", 1);
    return 1;
  }

  if (event->keyval == BACKSPACE) {
    c->getSocket()->write("\x7f", 1);
    return 1;
  }

  if (event->string && event->string[0] != '\0') {
    c->getSocket()->write(event->string, strlen(event->string));
    return 1;
  }

  return 0;
}

int papaya_input_entry_activate(GtkEditable * editable, gpointer data) {

  Connection * conn = mainWindow->getCurrentConnection();
  if (!conn)
    return 0;

  if (conn->getVT()->getMode() == VTCharacterMode) {
    conn->getVT()->setInput("");
    return 1;
  }

  GTKTwoVT * pvt = (GTKTwoVT *)conn->getVT();

  gchar * text = (gchar *)gtk_entry_get_text(GTK_ENTRY(pvt->getInput()));
  if (!commandInterpreter)
    commandInterpreter = new CommandInterpreter();

  // Convert this text from UTF-8 to the MUD character set if requested in
  // preferences.

  gchar * output;
  gsize read, written;
  GError * error = NULL;

  gchar * out_charset = conn->queryPreferences()->getPreference("charset");
  if (!out_charset)
    out_charset = "ISO-8859-1";

  if (out_charset[0] != '\0') {
    output = g_convert(text, strlen(text), out_charset, "UTF-8",
                   &read, &written, &error);
    commandInterpreter->interpret(conn, output);
#ifndef WIN32
    free(output);
#endif
  } else {
    commandInterpreter->interpret(conn, text);
  }

  return 1;
}

void GTKTwoVT::setMode(VTMode m) {

  VT::setMode(m);

  if (getMode() == VTCharacterMode)
    hideInput();
  
  if (getMode() == VTLineMode)
    showInput();

  focus();
}

void GTKTwoVT::focus() {
  switch (getMode()) {
  case VTCharacterMode:
    GTK_WIDGET_SET_FLAGS(GTK_WIDGET(terminal), GTK_CAN_FOCUS);
    gtk_widget_grab_focus(terminal);
    break;
  case VTLineMode:
    GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(terminal), GTK_CAN_FOCUS);
    gtk_widget_grab_focus(input);
    break;
  }
}

void GTKTwoVT::hideCommands() {
  gtk_entry_set_visibility(GTK_ENTRY(input), false);
}

void GTKTwoVT::showCommands() {
  gtk_entry_set_visibility(GTK_ENTRY(input), true);
}

bool GTKTwoVT::showingCommands() {
  return GTK_WIDGET_VISIBLE(GTK_ENTRY(input)->editable);
}

void GTKTwoVT::createTagTable() {

  return;
}

void GTKTwoVT::setTitleBar(char * text) {
  mainWindow->setTitleBar(text);
}

void GTKTwoVT::setIcon(char * text) {
  mainWindow->setIcon(text);
}

struct colour_table * GTKTwoVT::getColourTable() {
  return systemColour->getColourTable();
}

Generated by  Doxygen 1.6.0   Back to index