/*
    Silky - A GTK+ client for SILC.
    Copyright (C) 2003, 2004, 2005 Toni Willberg

    - GUI-related functions
    FIXME: move most of stuff from here to ui_ -files

    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

    http://silky.sourceforge.net/

*/

#include <glib.h>
#include <glade/glade.h>
#include <gtk/gtk.h>
#include <silcincludes.h>
#include <silcclient.h>

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

#include "support.h" /* context_nick_event() */
#include "gui.h"
#include "channel.h"
#include "query.h"
#include "buddylist.h"
#include "servers.h"
#include "preferences.h" /* prefs_get() */
#include "ui_preferences.h" /* populate_prefs_serverlist() */
#include "callbacks.h" /* on_window_hide() */
#include "log.h"
#include "commands.h"
#include "xmllayout.h"
#include "xmlconfig.h" /* xml_save_config() */
#include "cmdhistory.h"
#include "tips.h"
#include "ui_console.h"
#include "ui_channel.h"

#include "common.h"

extern GladeXML *xmlmain;
extern silkyStruct *silky;
extern gchar *previous_nick; /* my nickname just before sending NICK command */
extern SilkyChannel *channels; /* from channel.c */
extern SilkyChannel *channel_active;
extern SilkyQuery *query_active;
extern SilkyCmdHistoryItem *current_cmd_history_entry;	/* from cmdhistory.c */
extern SilkyServer *servers;
extern SilkyBuddy *buddies;


/* called when About button is clicked on the menu */
void on_ui_about(GtkWidget *widget, gpointer user_data) {
  gtk_widget_show(glade_xml_get_widget (xmlmain, "dialog_about"));
  /* I18N This is a window title */
  gtk_label_set_label( GTK_LABEL(glade_xml_get_widget (xmlmain, "about_silky_label")) , g_strdup_printf("<b>Silky %s</b>", SILKY_VERSION) );
 }



/* QUIT */
/* this function is *just* a callback, we call real quit function from here */
void on_ui_quit(GtkWidget *widget, gpointer user_data) {
  handle_command("QUIT");
}

/* User has clicked [Send] or hit enter in the input box.
    Is this a command or a message? */
void on_ui_send_check(GtkWidget *widget, gpointer input)
{
  GtkEntry *inmes;
  gchar *message;

  debug("on_ui_send_check()");
  /* Get value of the input box */
  inmes = GTK_ENTRY(glade_xml_get_widget (xmlmain, "inputbox"));
  message = g_strdup(gtk_entry_get_text(inmes));

  if (!message || strlen(message) <= 0) {
    debug("empty message, skipping!");
    return;
   }

  cmd_history_push(message);
  current_cmd_history_entry = NULL;

  if (message[0] == '/') {
    /* It's a command, parse it */
    debug("command");
    handle_command (message + 1);
    gtk_entry_set_text(GTK_ENTRY(inmes), "");
    free(message);
  }
  else {
    /* It's a message, send on to on_ui_send() */
    on_ui_send(widget,input);
  }
}




/* hide/show inputbox when on console/channel page */
void on_ui_tabs_switch_page(GtkNotebook *notebook, GtkNotebookPage *page,
			    guint pagenr, gpointer user_data) {

  SilkyChannel *ch = NULL;
  SilkyQuery *q = NULL;

  gui_renumber_tabs();

  debug ("on_ui_tabs_switch_page(%d)", pagenr);

  ch = channel_find_by_pagenr(gtk_notebook_get_current_page(notebook));

  if(ch == NULL) {
    channel_active = NULL;
    debug("no channel on this tab, setting channel_active = NULL");
  }

  q = query_find_by_pagenr(gtk_notebook_get_current_page(notebook));

  if(q == NULL) {
    query_active = NULL;
    debug("no query on this tab, setting query_active = NULL");
  }

  if( !q && !ch ) {
    debug("Not a channel or query page, returning.");
    return;
  }

  debug("got reply, page: %d", pagenr);

  gtk_widget_show (glade_xml_get_widget (xmlmain, "inputbox"));
  gtk_widget_show (glade_xml_get_widget (xmlmain, "button_send"));
  gtk_widget_grab_focus(glade_xml_get_widget (xmlmain, "inputbox"));

  /* if channel page FIXME; segfaults on null why? */
  if(ch && ch->channel_entry) {
   debug("Active channel now: %s", ch->channel_entry->channel_name);

   channel_active = ch;
   gtk_label_set_markup( (gpointer)ch->label_text, g_strconcat(CHANNEL_NAME(ch), NULL));
   gtk_image_set_from_stock( (gpointer)ch->label_image, GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_BUTTON );
  }
  else if( q && q->client_entry ) {
   debug("Active query now: %s", q->client_entry->nickname);

   query_active = q;
   gtk_label_set_markup( (gpointer)q->label_text, g_strconcat(q->client_entry->nickname, NULL));
   gtk_image_set_from_stock( (gpointer)q->label_image, GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_BUTTON );
  } else {
   debug("Active page doesn't belong to a channel or query.");
   channel_active = NULL;
   query_active = NULL;
  }
}



/* Send NICK command to the server */
gboolean on_nick_change(GtkWidget *widget, gpointer user_data) {
  GtkEntry *input;
  const gchar *new_nick;
  const gchar *old_nick;

  debug("on_nick_change()");

  if (!CONNECTED) {
    printconsole(_("Can not change nickname: You are not yet connected to server."));
    return FALSE;
  }

  input = GTK_ENTRY(glade_xml_get_widget (xmlmain, "new_nickname"));
  new_nick = gtk_entry_get_text(input);
  old_nick = silky->conn->nickname;

  if (strlen(new_nick) <1 ) {
    printconsole(_("Can not change nickname: Empty nickname not allowed."));
    return FALSE;
  }

  if (new_nick && old_nick && !strcmp(new_nick, old_nick) ) {
    printconsole(_("Can not change nickname: That is your current nickname already."));
    return FALSE;
  }

  /* set global variable for later use */
  previous_nick = strdup(old_nick);
  silc_client_command_call(silky->client, silky->conn, NULL, "NICK", new_nick, NULL);
  debug("\tCommand sent. New entry says the nick is '%s'", silky->conn->nickname);
  on_window_hide(glade_xml_get_widget (xmlmain, "dialog_nick_change"), NULL);

  return FALSE;
}




/* user asked to disconnect */
void on_disconnect_from_server(GtkWidget *widget, gpointer user_data) {
  debug("on_disconnect_from_server()");

  if ( CONNECTED ) {
    debug("We are connected; sending QUIT command");
    debug("setting state to STATE_DISCONNECTING");
    silky->state = STATE_DISCONNECTING;

    silc_client_command_call(silky->client, silky->conn, NULL, "QUIT", strdup(gtk_entry_get_text(GTK_ENTRY(glade_xml_get_widget (xmlmain,"set_quitmessage")))), NULL);
    debug("sent");
  }
  else {
    debug("We are not connected, can not disconnect now");
    printconsole(_("You are not connected to server."));
  }

}


/* when user wants to see info about the server he's connected to */
void on_serverinfo(GtkWidget *widget, gpointer user_data) {
  debug("on_serverinfo()");

  if (CONNECTED) {
    debug("sending MOTD %s", silky->conn->local_entry->server);
    silc_client_command_call(silky->client, silky->conn, NULL, "MOTD", silky->conn->local_entry->server, NULL);
    debug("sending INFO %s", silky->conn->local_entry->server);
    silc_client_command_call(silky->client, silky->conn, NULL, "INFO", silky->conn->local_entry->server, NULL);
    debug("sending STATS %s", silky->conn->local_entry->server);
    silc_client_command_call(silky->client, silky->conn, NULL, "STATS", silky->conn->local_entry->server, NULL);
  }
  else {
    debug("not yet connected");
    printconsole(_("You are not connected to server."));
  }

}




/* Show Join to a channel -dialog */
void on_show_join_channel_window(GtkWidget *widget, gpointer user_data) {
  debug("on_show_join_channel_window()");


  /* Show an error dialog if there's no connection */
  if (!CONNECTED) {
    printconsole(_("You are not connected to server."));
    return;
  }

  if (!widget) {
    debug("No such widget!?");
    return;
  }

  gtk_widget_show (glade_xml_get_widget (xmlmain, "dialog_join_channel"));
  /* I18N This is a window title */
  gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "dialog_join_channel")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Join a channel")));
  gtk_editable_select_region(GTK_EDITABLE(glade_xml_get_widget(xmlmain, "join_channel_name")), 0, -1);
}

/* Show incoming invite dialog */
void on_show_received_invite_window(GtkWidget *widget, gpointer user_data) {
  debug("on_show_received_invite_window()");

  if (!widget) {
    debug("No such widget!?");
    return;
  }

  gtk_widget_show (glade_xml_get_widget (xmlmain, "dialog_received_invite"));
  /* I18N This is a window title */
  gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "dialog_received_invite")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Join channel?")));
}


/* Show the CHANGE NICK dialog */
void on_show_nick_change_window(GtkWidget *widget, gpointer user_data) {
  debug("on_show_nick_change_window()");
 if ( !CONNECTED ) {
    printconsole(_("You are not connected to server."));
    return;
  }

  gtk_widget_show (glade_xml_get_widget (xmlmain, "dialog_nick_change"));
  gtk_entry_set_text(GTK_ENTRY(glade_xml_get_widget(xmlmain, "new_nickname")), silky->client->nickname);
  gtk_editable_select_region(GTK_EDITABLE(glade_xml_get_widget(xmlmain, "new_nickname")), 0, -1);
  /* I18N This is a window title */
  gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "dialog_nick_change")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Change nickname")));
}



/* Show the SERVER CONNECT dialog */
void on_show_server_connect_window(GtkWidget *widget, gpointer user_data) {
  SilkyServer *s;
  GtkWidget *combo;
  GList *serveritems = NULL;

  debug("on_show_server_connect_window()");

  if ( CONNECTED || CONNECTING ) {
    errordialog(_("You are already connected."));
    return;
  }

  combo = glade_xml_get_widget(xmlmain, "new_servername_combo");

  /* populate the dialog's combo list with out servers */
  for( s = servers; s; s = s->next ) {
    if(s->hostname && g_utf8_strlen(s->hostname, -1) ) {
      debug("server: '%s'", s->hostname);
      serveritems = g_list_append (serveritems, s->hostname);
    }
  }

  if (serveritems) {
    gtk_combo_set_popdown_strings (GTK_COMBO (combo), serveritems);
  }

  gtk_widget_show (glade_xml_get_widget (xmlmain, "dialog_connect_server"));
  /* I18N This is a window title */
  gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "dialog_connect_server")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Connect to a server")));
}



int on_window_hide(GtkWidget *widget, gpointer window) {
	debug("on_window_hide");
	if (!widget) {
	  debug("No such window on_window_hide()");
	  return FALSE;
	}

	gtk_widget_hide (widget);
	return TRUE;
}





/* SETTINGS */
void on_settings(GtkWidget *widget, gpointer user_data) {

	prefs_populate_gui();

	gtk_widget_show (glade_xml_get_widget (xmlmain, "dialog_preferences"));
	/* I18N This is a window title */
	gtk_window_set_title(GTK_WINDOW(glade_xml_get_widget (xmlmain, "dialog_preferences")), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Settings")));
}


void silky_gui_show_main() {

  GtkTextBuffer *tiptext = gtk_text_view_get_buffer(GTK_TEXT_VIEW(glade_xml_get_widget(xmlmain, "tipstextview")));
  gint ret = GTK_RESPONSE_OK;
  gchar *tip = NULL;

  debug ("Showing the main window");
  /* generate and show the main app window */
  gtk_window_set_title (GTK_WINDOW
			(glade_xml_get_widget (xmlmain, "window_main")),
			g_strdup_printf ("Silky v%s", SILKY_VERSION));
  gtk_widget_show (glade_xml_get_widget (xmlmain, "window_main"));

  /* set colors to the console tab */
  set_textview_colors(GTK_TEXT_VIEW(glade_xml_get_widget (xmlmain, "textviewwidget")));

  /* Show the input box for commands */
  gtk_widget_show (glade_xml_get_widget (xmlmain, "inputbox"));
  /* Don't forget the send button */
  gtk_widget_show (glade_xml_get_widget (xmlmain, "button_send"));

  /* set focus to inputbox of the main window */
  gtk_widget_grab_focus (glade_xml_get_widget (xmlmain, "inputbox"));

  debug ("setting state to STATE_DISCONNECTED");
  silky->state = STATE_DISCONNECTED;

 /* show the tips window */
  ret = GTK_RESPONSE_OK;

  if ( prefs_str_to_bool(prefs_get("show_tips")) )
    {
      while (ret == GTK_RESPONSE_OK) {
      debug ("showing a tip");
	tip = silky_get_random_tip();
        gtk_text_buffer_set_text(tiptext, tip, strlen(tip));
	g_free(tip);
	ret = gtk_dialog_run (GTK_DIALOG(glade_xml_get_widget (xmlmain, "dialog_tips")));
      }
      gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_tips"));
    }
  else
    {
      debug ("not showing tip of the day");
    }

}




/* this will check the type bits for sanity */
gboolean is_valid_msg_type(type) {

  if(type <= 0) return FALSE;

  /* MSG_PUBLIC and MSG_PRIVATE are mutually exclusive */
  if( (type & MSG_PUBLIC) && (type & MSG_PRIVATE) ) return FALSE;

  /* only one of MSG_MESSAGE, MSG_ACTION and MSG_MOTION can be set */
  if( (type & MSG_MESSAGE) && ((type & MSG_ACTION) || (type & MSG_MOTION)) )
    return FALSE;
  if( (type & MSG_ACTION) && ((type & MSG_MOTION) || (type & MSG_MESSAGE)) )
    return FALSE;
  if( (type & MSG_MOTION) && ((type & MSG_MESSAGE) || (type & MSG_ACTION)) )
    return FALSE;

  /* if it's signed, it *must* be one of MSG_MESSAGE or MSG_ACTION */
  if( (type & MSG_SIGNED) && !((type & MSG_MESSAGE) || (type & MSG_ACTION)) )
    return FALSE;

  /* FIXME: more sanity checks? */

  return TRUE;
}

void silky_print(gint type, void *target, gchar *text, ...) {
  va_list varg;
  SilkyChannel *ch, *foundch = NULL;
  gint found;
  gchar str[4097];

  if(!target) {
    debug("target == NULL");
    return;
  }

  /* sanity checks first */
  if(!is_valid_msg_type(type)) {
    debug("Invalid message type");
    return;
  }

  if(type & MSG_PUBLIC) {
  /* we assume this message will go to a channel tab, because it's public */
    found = 0;
    for(ch = channels; ch; ch = ch->next) {
      if( (SilcChannelEntry)target == ch->channel_entry ) {
        found++;
	foundch = ch;
	break;
      }
    }

    if(!found) {
      debug("Target for public message not found, returning.");
      return;
    }

    va_start(varg, text);
    g_vsnprintf(str, sizeof(str)-1, text, varg);
    va_end(varg);

    /* due to if(!found) check above, foundch will at this time will be
       non-NULL or we wouldn't be here */
    silky_print_channel(type, foundch, str);
    return;
  }

  if(type & MSG_PRIVATE) {
    /* blah blah */
    return;
  }
}


void silky_print_channel(gint type, SilkyChannel *channel, gchar *text) {
  gchar *str = g_strdup(text);

  /* motion (join, part, quit, ...) */
  if(type & MSG_MOTION) str = g_strdup_printf(template_motion, str);

  /* signed */
  if(type & MSG_SIGNED) str = g_strdup_printf(template_signed, "?", str);

  /* timestamp */
  str = g_strdup_printf(template_timestamp, utf_timestamp(), str);

  /* append linefeed */
  str = g_strdup_printf("%s\n", str);

  /* ask to print to the channel's textview widget */
  silky_print_buffer( GTK_TEXT_VIEW(glade_xml_get_widget(channel->glade_xml, "textviewwidget")), str );

}


/*
  print given text to given textview widget's buffer
  FIXME: THIS FUNCTION IS NOT YET IN USE
*/

void silky_print_buffer(GtkTextView *textview, gchar *text) {
  GtkTextBuffer *buffer;
  GtkTextIter iter;
  buffer = gtk_text_view_get_buffer(textview);

  gtk_text_buffer_insert (buffer, &iter, text, -1);
  gtk_text_buffer_get_end_iter(buffer, &iter);
  gtk_text_buffer_create_mark(buffer, "end_mark", &iter, FALSE);

  gtk_text_view_scroll_to_mark(textview, gtk_text_buffer_get_mark(buffer, "end_mark"), 0.1, FALSE, 0,1);

  return;
}

/* this should update GUI when cmode changes;*/
void silky_gui_check_channel_cmodes(SilcChannelEntry channel_entry, gchar *cmode) {
  GtkWidget *wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic");
  GtkWidget *wid_channel_topicbutton = get_stored_channel_widget(channel_entry->channel_name, "button_change_topic");

  debug("checking '%s'", channel_entry->channel_name);

  /* enable/disable the topicbox, if appropriate */
  if( !I_AM_OP(channel_entry) ) {
    if(channel_entry->mode & SILC_CHANNEL_MODE_TOPIC) {
      gtk_widget_set_sensitive(wid_channel_topic, FALSE);
      gtk_widget_set_sensitive(wid_channel_topicbutton, FALSE);
    } else {
      gtk_widget_set_sensitive(wid_channel_topic, TRUE);
      gtk_widget_set_sensitive(wid_channel_topicbutton, TRUE);
    }
  }
}

gint gui_tab_type(gint pagenr) {
  GtkWidget *notebook = glade_xml_get_widget(xmlmain, "tabs");
  GtkWidget *tab = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), pagenr);
/*  gpointer *data = NULL;

  if(!pagenr) return TAB_TYPE_CONSOLE;
  if((data = g_object_get_data(G_OBJECT(tab), "channel_name"))) return TAB_TYPE_CHANNEL;
  if((data = g_object_get_data(G_OBJECT(tab), "clientid"))) return TAB_TYPE_QUERY;
  return TAB_TYPE_UNKNOWN;
*/
  return (gint)g_object_get_data(G_OBJECT(tab), "tab_type");
}

void gui_renumber_tabs() {
  GtkNotebook *tabs = GTK_NOTEBOOK(glade_xml_get_widget(xmlmain, "tabs"));
  gint i;
  gchar *chname;
  SilcClientEntry ce;

  debug("starting renumbering");
  /* we'll skip tab 0, because it is our console and should not move */
  for( i = 1; i < gtk_notebook_get_n_pages(tabs); i++ ) {
    switch(gui_tab_type(i)) {
      case TAB_TYPE_CHANNEL:
        debug("Tab #%d is a CHANNEL tab", i);
        chname = g_object_get_data(G_OBJECT(gtk_notebook_get_nth_page(tabs, i)), "channel_name");
        channel_pagenr_set_by_name(chname, i);
	break;
      case TAB_TYPE_QUERY:
        debug("Tab #%d is a QUERY tab", i);
	ce = g_object_get_data(G_OBJECT(gtk_notebook_get_nth_page(tabs, i)), "cliententry");
	query_pagenr_set_by_cliententry(ce, i);
	break;
      case TAB_TYPE_CONSOLE:
        debug("Tab #%d is a CONSOLE tab", i);
	break;
      case TAB_TYPE_UNKNOWN:
        debug("Tab #%d is !!UNKNOWN!! type, this should not be");
	break;
    }
  }
}

/*
  call this to set color and font tags for given GtkTextView
*/

void set_textview_colors(GtkTextView *widget_textview) {
  gchar *value;
  GdkColor color;
  GtkTextBuffer *textview_buffer;
  PangoFontDescription *pangofont;
  GtkTextTagTable *tag_table;
  GtkTextTag *tag;
  gint j;
  GtkTextTag *tag_context_nick;

  /* map option names to tag names */
  struct {
    gchar *tagname;
    gchar *option_name;
  } tag_opt_map[] = {
    { "timestamp",		"color_timestamp" },
    { "server_messages",	"color_server_messages" },
    { "nickname",		"color_nickname" },
    { "nickname-own",		"color_my_nickname" },
    { "message",		"color_text" },
    { "message-own",		"color_my_text" },
    { "action",			"color_action" },
    { "action-own",		"color_my_action" },
    { NULL,			NULL } /* DON'T REMOVE THESE! */

  };

  debug("set_textview_colors() / setting PANGO style tags");

  /* set font */
  value = prefs_get("font");
  debug("set_font = '%s'", value);
  if (value && strlen(value) > 1 ) { /* font name must be more than just a '\n' */
    pangofont = pango_font_description_from_string(value);
    gtk_widget_modify_font(GTK_WIDGET(widget_textview), pangofont); /* set font */
  }

  /* set background color */
  value = prefs_get("color_background");
  if(!g_ascii_strcasecmp(value, "default")) value = "";
  debug("set_opt_background = '%s'", value);
  if (value && strlen(value) > 1 ) {  /* color must be more than just a '\n' */
    gdk_color_parse(value, &color); /* parse background color */
  }
  else {
    gdk_color_parse("#ffffff", &color); /* set default bgcolor to white */
  }
  gtk_widget_modify_base(GTK_WIDGET(widget_textview), GTK_STATE_NORMAL, &color); /* set color */

  /* get text buffer for output */
  textview_buffer = gtk_text_view_get_buffer(widget_textview);

  /* remove all previous tags */
  tag_table = gtk_text_buffer_get_tag_table(textview_buffer);

  /* set colortags using tag_opt_map */
  j = 0;
  while ( tag_opt_map[j].option_name && tag_opt_map[j].tagname) {
    value = prefs_get(tag_opt_map[j].option_name);
    if(!g_ascii_strcasecmp(value, "default")) value = "";
    debug("* '%s' = '%s'", tag_opt_map[j].tagname, value);

    /* try to find old pango tag */
    tag = gtk_text_tag_table_lookup(tag_table, tag_opt_map[j].tagname);
    if (tag) {
      /* found, replace it's value */
      if (value && strlen(value) > 1 ) { /* color must be more than just a '\n' */
	g_object_set( G_OBJECT(tag), "foreground", value, NULL ); /* override previous color */
      }
      else {
	g_object_set( G_OBJECT(tag), "foreground", "#000000", NULL ); /* set default fg color to black */
      }
    }
    else {
      /* create new tag */
      if (value && strlen(value) > 1 ) { /* color must be more than just a '\n' */
	/* create new fg color tag with color */
	gtk_text_buffer_create_tag (textview_buffer, tag_opt_map[j].tagname, "style", PANGO_STYLE_NORMAL, "foreground", value, NULL);
      }
      else {
	/* create new black fg color tag, because there was no color given */
	gtk_text_buffer_create_tag (textview_buffer, tag_opt_map[j].tagname, "style", PANGO_STYLE_NORMAL, "foreground", "#000000", NULL);
      }
    }

    j++; /* this is what I call progress ;) */
  } /* while */

  /* let's create other tags here too */
  if( !gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(textview_buffer), "context-nick") ) {
    tag_context_nick = gtk_text_buffer_create_tag(textview_buffer, "context-nick", NULL);
    g_signal_connect (tag_context_nick, "event", G_CALLBACK (context_nick_event), NULL);
  }

}



gboolean on_nicktree_columns_changed( GtkTreeView *tv, gpointer data ) {
  debug("nicktree columns has changed");
  return FALSE;
}

/* Shows a MODAL error dialog */
void errordialog(const char *errormessage) {
  GtkWindow *mainwin;
  GtkMessageDialog *dialog;

  mainwin = GTK_WINDOW(glade_xml_get_widget (xmlmain, "window_main"));
  dialog = GTK_MESSAGE_DIALOG(
			      gtk_message_dialog_new (mainwin,
						      GTK_DIALOG_DESTROY_WITH_PARENT,
						      GTK_MESSAGE_ERROR,
						      GTK_BUTTONS_CLOSE,
						      errormessage)
			      );
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (GTK_WIDGET(dialog));

  return;
}



/* this function closes all tabs in UI except the first (console) tab */
void close_all_tabs( void ) {
  GtkWidget *notebook;
  int i, n;

  if( !(notebook = glade_xml_get_widget(xmlmain, "tabs")) ) {
    debug("could not retrieve notebook, bailing out");
    return;
  }

  if( (n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook))) > 1 ) {
    debug("removing tabs left over from previous connection");
    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
    for( i = n; i > 0; i-- )
      gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), i);
  }
}


/* this prints given message to the ACTIVE tab */
int printconsole (const char *str) {
  GtkTextView *textview = NULL;
  gchar *timestamp;
  gchar *outstring;
  GtkTextBuffer *buffer;
  GtkTextIter iter; /* pointer to current location */
  GtkNotebook *storednotebook;
  GtkWidget *active_page;
  gint apagenr;
  gchar *channel_name;
  GladeXML *stored_query_xml;

  /* make sure it was utf8 */
  if (!g_utf8_validate(str, -1, NULL)) {
    debug("Found non-utf8 string: '%s'", str);
    str = g_strdup("Non-utf8 string received. Can not display.");
  }


  timestamp = g_strdup_printf("[%s] ", utf_timestamp() );
  outstring = g_strdup_printf("%s", str);

  /* see if there's active channel or query tab first */
  storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
  apagenr = gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook));

  /*
     console or other type of tab? 0 is always console
  */
  if (apagenr == 0) { /* console tab */
    debug("active: CONSOLE");
    textview = GTK_TEXT_VIEW(glade_xml_get_widget (xmlmain, "textviewwidget"));
  }
  else { /* other tab */
    debug("active: other tab... looking for type...");
    active_page = gtk_notebook_get_nth_page(storednotebook, apagenr); /* active pagenr from GTK */
    if (!active_page) {
      debug("no page was active, this should not happen!");
      return FALSE;
    }

    /* test if it was a channel tab */
    channel_name = g_object_get_data(G_OBJECT(active_page), "channel_name");
    if (channel_name) {
      debug("active CHANNEL");
      textview = GTK_TEXT_VIEW(get_stored_channel_widget(channel_name, "textviewwidget"));
    }
    else {

      /* this way we can get the textview widget without knowing the clientid */
      stored_query_xml = glade_get_widget_tree(active_page);
      textview = GTK_TEXT_VIEW(glade_xml_get_widget (stored_query_xml, "textviewwidget"));
    }

  }

  if (!textview) {
    debug("active NONE. no textview found!");
    return FALSE;
  }


  buffer = gtk_text_view_get_buffer(textview);

  /* append \n */
  outstring = g_strdup_printf("%s\n", outstring);

  /*  debug("`%s'", outstring); */
  /* Get the end of the buffer */
  gtk_text_buffer_get_end_iter(buffer, &iter);

  /* Print the text */

  /* timestamp */
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, timestamp, -1, "timestamp", NULL);

  /* server message */
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, outstring, -1, "server_messages", NULL);

  gtk_text_buffer_get_end_iter(buffer, &iter);
  gtk_text_buffer_create_mark(buffer, "end_mark", &iter, FALSE);
  gtk_text_view_scroll_to_mark(textview, gtk_text_buffer_get_mark(buffer, "end_mark"), 0.1, FALSE, 0,1);

  g_free(timestamp);
  g_free(outstring);

  return TRUE;
}

void silky_show_user_info(SilkyUserInfo *user) {
  gchar **uhost = g_strsplit(user->username, "@", 2);
  gchar **nickserv = g_strsplit(user->nickname, "@", 2);
  SilcAttributePayload attr;
  SilcAttribute attribute;
  char tmp[512];

  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_nick")), nickserv[0]);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_server")), nickserv[1]);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_user")), uhost[0]);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_host")), uhost[1]);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_realname")), user->realname);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_fp")), user->fingerprint);
  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_channels")), user->channels);

  /* FIXME: parse attrs */
  if(!user->attrs) {
    debug("No attributes to parse.");
  } else {
    silc_dlist_start(user->attrs);
    while ((attr = silc_dlist_get(user->attrs)) != SILC_LIST_END) {
      attribute = silc_attribute_get_attribute(attr);
      memset(tmp, 0, sizeof(tmp));

      switch(attribute) {
        case SILC_ATTRIBUTE_TIMEZONE:
          if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
	    continue;
	  gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(xmlmain, "label_userinfo_tz")), tmp);
	  break;
      }
    }
  }

  /* show the dialog */
  gtk_widget_show_all(glade_xml_get_widget(xmlmain, "dialog_userinfo"));

  /* The struct itself is supposed to be freed outside of this function, so it can be reused if necessary. */
}

void silky_free_user_info(SilkyUserInfo *user) {
  if( !user ) {
    debug("SilkyUserInfo is NULL, ignoring.");
    return;
  }

  if(user->nickname) g_free(user->nickname);
  if(user->username) g_free(user->username);
  if(user->realname) g_free(user->realname);
  if(user->fingerprint) g_free(user->fingerprint);
  if(user->channels) g_free(user->channels);

  g_free(user);
}


syntax highlighted by Code2HTML, v. 0.9.1