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

    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 <gtk/gtk.h>
#include <glade/glade.h>

#include <silcincludes.h>
#include <silcclient.h>

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

#include "ui_channel.h"
#include "support.h"
#include "preferences.h"
#include "gui.h"
#include "log.h"
#include "common.h"
#include "callbacks.h"
#include "ui_query.h" /* FIXME: this shouldn't be needed */
#include "ui_buddylist.h"

extern GladeXML *xmlmain;
extern SilkyChannel *channel_active; /* from channel.c */
extern silkyStruct *silky;

/* this re-orders nicklist, using current GTK sort settings for this widget */
void refresh_gui_nicklist(SilkyChannel *channel) {
  GtkTreeView *treeview = GTK_TREE_VIEW(glade_xml_get_widget(channel->glade_xml, "nicktreeview"));
  GtkTreeModel *sorted_nicklist = gtk_tree_view_get_model(treeview);
  gint colid = 0;
  GtkSortType order;

  populate_gui_nicklist(channel);

  /* get current sort settings */
  if( !gtk_tree_sortable_get_sort_column_id( GTK_TREE_SORTABLE(sorted_nicklist), &colid, &order) ) {
    debug("Couldn't get sort column id and order of nicklist.");
    debug_info("Re-ordering nicklist with default sort settings.");
    gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE(sorted_nicklist), USERLIST_COL_NICKNAME, GTK_SORT_ASCENDING);
    return;
  }

  debug("Re-ordering nicklist.");
  gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE(sorted_nicklist), colid, order);

}

/* this reads channel->members linked list and populates GUI nicklist accordingly */
/* - we'll sort it later */
void populate_gui_nicklist(SilkyChannel *channel) {
  GtkTreeView *treeview = GTK_TREE_VIEW(glade_xml_get_widget(channel->glade_xml, "nicktreeview"));
  GtkTreeModel *sn = gtk_tree_view_get_model(treeview);
  GtkListStore *liststore = GTK_LIST_STORE(gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(sn)));
  GtkWidget *usercount_label = glade_xml_get_widget(channel->glade_xml, "channel_usercount");
  GtkTreeIter iter;
  SilkyChannelMember *ch;
  guint usercount = 0;

  /* first we clear the nicklist liststore... */
  gtk_list_store_clear(liststore);

  /* ...and now let's start filling it anew */
  for( ch = channel->members; ch; ch = ch->next ) {
    debug("Adding '%s' to nicklist.", CHMEMBER_NAME(ch));

    gtk_list_store_append(liststore, &iter);

    /* founder column */
    if( IS_CHANNEL_FOUNDER(ch) ) gtk_list_store_set(liststore, &iter, USERLIST_COL_FOUNDER, "*", -1);

    /* if muted, let's use the same column, for founders cannot be muted */
    if( IS_CHANNEL_MUTED(ch) ) gtk_list_store_set(liststore, &iter, USERLIST_COL_FOUNDER, "&", -1);

    /* chanop column */
    if( IS_CHANNEL_OP(ch) ) gtk_list_store_set(liststore, &iter, USERLIST_COL_CHANOP, "@", -1);

    /* nickname column */
    gtk_list_store_set(liststore, &iter, USERLIST_COL_NICKNAME, CHMEMBER_NAME(ch), -1);

    /* clientid column (soon to be removed, redundant) */
    gtk_list_store_set(liststore, &iter, USERLIST_COL_CLIENTID, ch->chuser->client->id, -1);

    usercount++;
  }

  gtk_label_set_text(GTK_LABEL(usercount_label), g_strdup_printf(ngettext("%d user", "%d users", usercount), usercount));
}

/*
  This sets the colors to the nicklist widget.
*/

void silky_colorize_nicklist (GtkWidget *treeview) {
  gchar *value;
  GdkColor gdkcolor;

  debug("colorizing");

  /* set BACKGROUND COLOR of the userlist */
  value = prefs_get("color_background");
  if (value && strlen(value) > 1 && strcmp("default", value) ) {
    gdk_color_parse(value, &gdkcolor); /* parse background color */
  } else {
    gdk_color_parse("#ffffff", &gdkcolor); /* set default bgcolor to white */
  }

  debug("setting channel nicklist's background color to '%s'", value);
  gtk_widget_modify_base(GTK_WIDGET(treeview), GTK_STATE_NORMAL, &gdkcolor); /* set color */

  /* set FOREGROUND COLOR of the userlist, use color_text */
  value = prefs_get("color_text");
  if (value && strlen(value) > 1 && strcmp("default", value) ) {
    gdk_color_parse(value, &gdkcolor); /* parse background color */
    debug("setting channel nicklist's foreground color to '%s'", value);
  } else {
    gdk_color_parse("#ffffff", &gdkcolor); /* set default bgcolor to white */
  }

  gtk_widget_modify_text(GTK_WIDGET(treeview), GTK_STATE_NORMAL, &gdkcolor); /* set color */


  return;
}


/******************** TAB COMPLETION ************************/
void channel_tab_completion( void ) {
    GtkEntry *inmes = GTK_ENTRY(glade_xml_get_widget( xmlmain, "inputbox" ));
    SilkyChannelMember *m = NULL;
    gchar *text, *ch;

    if( !channel_active ) {
	debug("Active tab is not a channel tab.");
	return;
    }

    /* only read prefix once */
    if( !channel_active->tab_prefix ) {
	text = g_strdup(gtk_entry_get_text(inmes));
	/* let's find space or start of string */
	for( ch = g_utf8_offset_to_pointer(text, g_utf8_strlen(text, -1)); !g_unichar_isspace(g_utf8_get_char(ch)) && (ch = g_utf8_find_prev_char(text, ch)); );
	/* now separate last "word" and put rest into prefix */
	channel_active->tab_text = g_strndup(text, g_utf8_pointer_to_offset(text, ch));
	if( channel_active->tab_text && g_utf8_strlen(channel_active->tab_text, -1) )
	  channel_active->tab_prefix = g_strdup(g_utf8_next_char(ch));
	else
	  channel_active->tab_prefix = g_strdup(text);
	g_free(text);		/* we do not need this anymore */
    }

    /* tab_prefix and tab_text should not be NULL for code further below */
    if( !channel_active->tab_prefix ) channel_active->tab_prefix = g_strdup("");
    if( !channel_active->tab_text ) channel_active->tab_text = g_strdup("");

    /* make a foobar (SilkyChannelMember) with ->next pointing to start of memberlist,
       so we can start with ->next in both cases (first tab press or consequent tab presses */
    if( !channel_active->tab_member ) {
	channel_active->tab_member = malloc(sizeof(SilkyChannelMember));
	channel_active->tab_member->next = channel_active->members;
    }

    for( m = channel_active->tab_member->next; m; m = m->next ) {
        debug("Trying nick: '%s' against prefix '%s'", CHMEMBER_NAME(m), channel_active->tab_prefix);
        if( !g_utf8_collate(g_utf8_casefold(channel_active->tab_prefix, g_utf8_strlen(channel_active->tab_prefix, -1)), g_utf8_casefold(CHMEMBER_NAME(m), g_utf8_strlen(channel_active->tab_prefix, -1)) ) ) {
    	    debug("Found next suitable nick: '%s'", CHMEMBER_NAME(m));
	    channel_active->tab_member = m;
	    if( g_utf8_strlen(channel_active->tab_text, -1) )
	      gtk_entry_set_text(inmes, g_strdup_printf("%s %s", channel_active->tab_text, CHMEMBER_NAME(m)));
	    else
	      gtk_entry_set_text(inmes, g_strdup_printf("%s: ", CHMEMBER_NAME(m)));
	    return;
	}
    }
    channel_active->tab_member = NULL;
}

void channel_reset_tab_completion( void ) {
    if( !channel_active ) return;

    channel_active->tab_member = NULL;
    channel_active->tab_prefix = NULL;
    channel_active->tab_text = NULL;
}

/*
  Set back the original string to the ui.
  When user typed new topic, but lost focus and didn't press the button.
*/

gboolean on_topic_restore(GtkWidget *widget, gpointer user_data) {
  GtkWidget *wid_channel_topic;
  GtkWidget *topicbutton;
  SilcChannelEntry channel_entry;
  gchar *channel_name;

  return FALSE; /* FIXME:
		   see how to reorder signals, this should be called AFTER
		   on_topic is called.
		   DISABLED NOW.
		*/

  channel_name = get_active_channel_name();
  channel_entry = silc_client_get_channel(silky->client, silky->conn, channel_name);
  if (!channel_entry) {
    debug("didn't get channel_entry!");
    return FALSE; /* =don't let gtk proceed */
  }

  topicbutton = get_stored_channel_widget(channel_entry->channel_name, "button_change_topic");
  if (!GTK_WIDGET_HAS_FOCUS(widget)) { /* .glade points to topic button */
    debug("nothing to do as user pressed the button");
    return FALSE; /* user pressed topic change button, tell gtk to go there */
  }

  debug("restoring original topic");



  wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic");

  /* restore the old topic to UI */
  gtk_entry_set_text(GTK_ENTRY(wid_channel_topic), channel_entry->topic);

  return FALSE;
}

/*
  when user changes the topic in the GUI
*/
gboolean on_topic(GtkWidget *widget, gpointer user_data) {
  const gchar *topic;
  gchar *channel_name;
  gboolean ret;
  SilcChannelEntry channel_entry;
  GtkWidget *wid_channel_topic;

  debug("on_topic()");

  /* figure out the active channel */
  channel_name = get_active_channel_name();
  channel_entry = silc_client_get_channel(silky->client, silky->conn, channel_name);
  if (!channel_entry) {
    debug("didn't get channel_entry!");
    return FALSE;
  }

  wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic");
  topic = gtk_entry_get_text(GTK_ENTRY(wid_channel_topic));

  debug("old topic: '%s', new topic: '%s'", channel_entry->topic, topic);
  if (channel_entry->topic && !strcmp(topic, channel_entry->topic)) {
    debug("topic not changed");
    wid_channel_topic = get_stored_channel_widget(channel_entry->channel_name, "channeltopic");
    gtk_entry_set_text(GTK_ENTRY(wid_channel_topic), channel_entry->topic); /* restore original string, just to be sure */
    return FALSE;
  } else {
    gtk_entry_set_text(GTK_ENTRY(wid_channel_topic), topic); /* set new string to UI, ugly but required hack */
  }

  ret = silc_client_command_call(silky->client, silky->conn, NULL, "TOPIC", channel_name, topic, NULL);
  if (ret == FALSE) {
    debug("\terror");
    return FALSE;
  }
  else {
    debug("\tcommand sent successfully");
  }

  /* set focus to inputbox */
  gtk_window_set_focus(GTK_WINDOW(glade_xml_get_widget( xmlmain, "window_main" )), glade_xml_get_widget( xmlmain, "inputbox" ));

  return FALSE;
}



/*
   Get Channel Name of the active channel, if any
   FIXME: this shouldn't be needed anymore
*/
char * get_active_channel_name (void) {
  GtkNotebook *storednotebook;
  GtkWidget *active_page;
  gchar *channel_name;


  /* where are the names stored... */
  storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
  if (!storednotebook) {
    debug("can not find storednotebook, returning NULL");
    return NULL;
  }

  /* get active page entry */

  active_page = gtk_notebook_get_nth_page(storednotebook,
					 gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook))
					 );
  if (active_page == 0) /* if on console tab */ {
    debug("no console tab, returning NULL");
    return NULL;
  }

  /* get name of the channel from active_page object */
  channel_name = g_object_get_data(G_OBJECT(active_page), "channel_name");

  if (!channel_name) {
    debug("didn't get channel_name, returning NULL");
    return NULL;
  }

  debug("found active channel: '%s'", channel_name);
  return channel_name;
}



/* Get Channel ID of the active channel, if any */
SilcChannelID * get_active_channel_id (void) {
  GtkNotebook *storednotebook;
  GtkWidget *active_page;
  SilcChannelID *channel_id;

  debug("get_active_channel_id ()");

  /* where are the names stored... */
  storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
  if (!storednotebook) {
    debug("Did not get stored notebook");
    return NULL;
  }

  /* get active page entry */
  active_page = gtk_notebook_get_nth_page(storednotebook,
					  gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook))
					  );

  if(!active_page) {
    debug("Did not get active page...");
    return NULL;
  }

  /* get name of the channel from active_page object */
  channel_id = g_object_get_data(G_OBJECT(active_page), "channel_id");
  if (!channel_id) {
    debug("Did not get channel_id from active page");
    return NULL;
  }

  debug ("\treturning channel_id");
  return channel_id;
}




/* get stored channel widget from notebook by channel name
 FIXME: this shouldn't be necessary anymore*/
GtkWidget * get_stored_channel_widget (const char *channel_name, const char *widget_name) {
  GtkNotebook *storednotebook;
  GtkWidget *stored_channel_widget;
  GladeXML *stored_channel_xml;
  GtkWidget *stored_widget;

  debug("get_stored_channel_widget('%s', '%s')", channel_name, widget_name);

  /* get notebook widget */
  storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));

  /* get channel widget by channel_name */
  stored_channel_widget = g_object_get_data(G_OBJECT(storednotebook), channel_name);

  if (!stored_channel_widget) {
    debug("no stored channel widget found, not on active channel");
    printconsole(_("You are not on any channel."));
    return NULL;
  }

  stored_channel_xml = glade_get_widget_tree(stored_channel_widget);
  stored_widget = glade_xml_get_widget (stored_channel_xml, widget_name);

  return stored_widget;
}


/* get stored channel "window" widget from notebook by channel name */
GtkWidget * get_stored_channel_window (const char *channel_name) {
  GtkNotebook *notebook;
  GtkWidget *page;

  notebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
  page = g_object_get_data(G_OBJECT(notebook), channel_name);

  return page;
}


/* this prints given message to the CHANNEL tab, non-mime message */
int printchannel (const char *channel_name, const char *str) {
GtkTextView *textview;

  GtkTextBuffer *buffer;
  gchar *timestamp;
  gchar *outstring;
  GtkTextIter iter; /* pointer to current location */

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

  textview =  GTK_TEXT_VIEW(get_stored_channel_widget(channel_name, "textviewwidget"));

  buffer = gtk_text_view_get_buffer(textview);

  if (!buffer) {
    debug("Can not find text buffer!");
    return FALSE;
  }

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

  gtk_text_buffer_get_end_iter(buffer, &iter);
  gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, outstring, -1, "message", 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;
}






/* prints stuff to channel, FIXME: rewrite this */
int print_mime_to_channel (SilcClientEntry client_entry, const char *channel_name, int message_signed, const gchar *content_type, ...) {
  gchar *nick_name;
  GtkTextView *textview;
  GtkTextBuffer *buffer = NULL;
  GtkTextIter iter;
  GtkScrolledWindow *swindow;
  GtkAdjustment *vadjustment;
  
  SilkyChannel *ch; /* fixme; should get ch as input, not channel_name */
  unsigned char *message;
  int flags;
  unsigned char *mime_data_buffer;
  int mime_data_len;
  gchar *txt = NULL;

  va_list va;
  gchar *timestamp;
  gchar *outstring;
  GdkPixbufLoader *loader;
  GError *err;
  GdkPixbuf *pixbuf;
  /* strdup because modified here */
  gchar *hilightwords = g_strdup( prefs_get("highlight_words") );
  
  debug("print_mime_to_channel()");
  
  
  /* get ch */
  ch = channel_find_by_name(g_strdup(channel_name));
  
  if (!content_type) {
    debug("content-type was null, aborting");
    return FALSE;
  }
  
  nick_name = client_entry->nickname;
  textview =  GTK_TEXT_VIEW(get_stored_channel_widget(channel_name, "textviewwidget"));
  if (!textview) {
    debug("didn't find textview for channel '%s'", channel_name);
    return FALSE;
  }
  
  /* get text buffer for output */
  buffer = gtk_text_view_get_buffer(textview);
  
  if (!buffer) {
    debug("Can not find text buffer for channel '%s'", channel_name);
    return FALSE;
  }
  
  swindow = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(textview)));
  vadjustment = gtk_scrolled_window_get_vadjustment( swindow );

  /* get end position for printing */
  gtk_text_buffer_get_end_iter(buffer, &iter);
  
  va_start(va, content_type); /* last named argument before ... */

  /* handle TYPE text/plain */
  if (!strcmp(content_type, "text/plain")) {
    debug("content-type: '%s'", content_type);

    flags = va_arg(va, int);
    message = va_arg(va, unsigned char *);

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

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

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


    /* set action flags to tabs */
    if (g_pattern_match_simple(g_strconcat("*", silky->conn->local_entry->nickname, "*", NULL), message)) {
      debug("* my nick match");
      hilight_channel_window_by_ch(ch, HILIGHT_NICK);
    }
    else if (g_pattern_match_simple(g_strconcat("*", hilightwords, "*", NULL), message)) {
      debug("* hilight wordlist match");
      hilight_channel_window_by_ch(ch, HILIGHT_WORD);
    }
    else {
      hilight_channel_window_by_ch(ch, HILIGHT_SPEECH);
    }


    
    /* <mynickname> */
    if ( client_entry->id == silky->conn->local_entry->id) { /* myself */
      /* signed [S] */
      if (flags & SILC_MESSAGE_FLAG_SIGNED) {
	gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "[S]", -1, "nickname-own", NULL);
      }
      
      if (flags & SILC_MESSAGE_FLAG_ACTION) { /* action */
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "* ", -1, "nickname-own", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, nick_name, -1, "nickname-own", "context-nick", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, " ", -1, "nickname-own", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, message, -1, "action-own", NULL);
      }
      else { /* normal message */
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "<", -1, "nickname-own", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, nick_name, -1, "nickname-own", "context-nick", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "> ", -1, "nickname-own", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, message, -1, "message-own", NULL);
      }
    } else { /* <someone else> */
      /* signed [?,S,F] unknown signature, signed and verified, failed to verify */
      if (flags & SILC_MESSAGE_FLAG_SIGNED) {
        txt = g_strdup_printf("[%c]", (message_signed == 0 ? 'S' : (message_signed == 1 ? '?' : 'F')));
	gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, txt, -1, "nickname", NULL);
      }

      if ((flags & SILC_MESSAGE_FLAG_ACTION)) { /* action */
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "* ", -1, "nickname", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, nick_name, -1, "nickname", "context-nick", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, " ", -1, "nickname", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, message, -1, "action", NULL);
      }
      else { /* normal message */
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "<", -1, "nickname", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, nick_name, -1, "nickname", "context-nick", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "> ", -1, "nickname", NULL);
        gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, message, -1, "message", NULL);
      }
    }

    gtk_text_buffer_insert (buffer, &iter, "\n", -1);

  }

  /* handle type image/anything */
  if (!strcmp(content_type, "image/gif") || !strcmp(content_type, "image/png") || !strcmp(content_type, "image/jpeg")) {
    debug("content-type: '%s'", content_type);

    mime_data_buffer = va_arg(va, unsigned char *);
    mime_data_len = va_arg(va, int);
    debug("datalen: '%d'", mime_data_len);

    loader = gdk_pixbuf_loader_new ();
    err = NULL;

    /* this gives a warning but works?! */
    if (!gdk_pixbuf_loader_write (loader, mime_data_buffer, mime_data_len, &err)) {
      if (err) {
	debug ("Unable to load pixbuf data, error: '%s'", err->message);
      } else {
	debug("Unable to load pixbuf data, no error!");
      }
    }
    
    if (!gdk_pixbuf_loader_close (loader, &err)) {
      if (err) {
	debug ("Unable to close pixbuf, error: '%s'", err->message);
      } else {
	debug("Unable to close pixbuf, no error!");
      }
    }
    
    pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
    g_object_ref (pixbuf);
    g_object_unref (loader);

    /* insert nickname */
    gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "<", -1, "nickname", NULL);
    gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, nick_name, -1, "nickname", NULL);
    gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, ">", -1, "nickname", NULL);

    /* insert the image and a newline*/
    gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
    gtk_text_buffer_insert (buffer, &iter, "", -1);
    
  }
  

  va_end(va);
  
  
  gtk_text_buffer_get_end_iter(buffer, &iter);
  gtk_text_buffer_create_mark(buffer, "end_mark", &iter, FALSE);
  
  /* scroll to end only if we were already at the end before printing,
     this avoids scrolling when user is reading backlog */
  if (vadjustment->upper - vadjustment->page_size == vadjustment->value) {
    gtk_text_view_scroll_to_mark(textview, gtk_text_buffer_get_mark(buffer, "end_mark"), 0.1, FALSE, 0, 1);
  }
  
  return 1;
}


void error_no_channel_oper_priv (gchar *channel_name) {
  printchannel(channel_name,
	       g_strdup_printf(_("Permission denied. You are not channel operator of channel '%s'."), channel_name)
	       );

}

void error_no_channel_founder_priv (gchar *channel_name) {
  printchannel(channel_name,
	       g_strdup_printf(_("Permission denied. You are not channel founder of channel '%s'."), channel_name)
	       );

}

void error_user_not_on_channel (gchar *channel_name) {
  printchannel(channel_name,
	       g_strdup_printf(_("User is not on channel '%s'."), channel_name)
	       );
}


/*
 sets channel mode widgets

*/
gchar * set_cmode_window_widgets (gchar *channel_name, SilcUInt32 mode) {
  GtkToggleButton *toggle_button;
  gchar *str_mode;

  debug("cmode for '%s'", channel_name);

  /* set all modes cleared */
  str_mode = g_strdup(""); /* clear dynamic string */
  if (mode == SILC_CHANNEL_MODE_NONE) {
    str_mode = g_strdup("none");
  }


  /*
     Check mode and construct str_mode string accordingly.
     Also TOGGLE togglebutton widgets in the channel
  */
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_private")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_PRIVATE) {
    str_mode = g_strconcat(str_mode, "p", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE); /* button down */
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE); /* button up */
  }

  /* CMODE TOPIC */
  debug("BEFORE");
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_topic")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_TOPIC) {
    str_mode = g_strconcat(str_mode, "t", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }
  debug("AFTER");

  /* CMODE SECRET */
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_secret")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_SECRET) {
    str_mode = g_strconcat(str_mode, "s", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }

  /* CMODE PRIVKEY */
  /*
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_privkey"));
  if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
    str_mode = g_strconcat(str_mode, "k", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }
  */

  /* CMODE INVITE */

  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_invite")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_INVITE) {
    str_mode = g_strconcat(str_mode, "i", NULL);
      gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }


  /* CMODE ULIMIT */
  /*
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_ulimit"));
  if (mode & SILC_CHANNEL_MODE_ULIMIT) {
    str_mode = g_strconcat(str_mode, "l", NULL);
      gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }
  */

  /* CMODE PASSPHRASE */
  /*
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_passphrase"));
  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
    str_mode = g_strconcat(str_mode, "a", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }
  */

  /* CMODE SILENCE_USERS */
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_umute")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_SILENCE_USERS) {
    str_mode = g_strconcat(str_mode, "m", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }


  /* CMODE SILENCE_OPERS */
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_omute")); /* get toggle button */
  if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS) {
    str_mode = g_strconcat(str_mode, "M", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }

  /* CMODE FOUNDER_AUTH */
  /*
  toggle_button = GTK_TOGGLE_BUTTON(get_stored_channel_widget(channel_name, "cmode_founder"));
  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
    str_mode = g_strconcat(str_mode, "f", NULL);
    gtk_toggle_button_set_active(toggle_button, TRUE);
  }
  else {
    gtk_toggle_button_set_active(toggle_button, FALSE);
  }
  */


  return str_mode;
}



/* user clicked a nick from userlist with left mousebutton.
*/
void on_nicklist_selected (GtkTreeSelection *selection, gpointer data) {

  GtkTreeIter iter;
  GtkTreeModel *model;
  gchar *nickname;
  SilcClientID *clientid;

  debug("on_nicklist_selected()");

  if (gtk_tree_selection_get_selected (selection, &model, &iter))
    {
      gtk_tree_model_get (model, &iter, USERLIST_COL_NICKNAME, &nickname, -1);
      gtk_tree_model_get (model, &iter, USERLIST_COL_CLIENTID, &clientid, -1);

      debug ("You selected '%s' ", nickname);
      g_free (nickname);
    }

  return;
}



void on_nicklist_activated (GtkTreeView *treeview, GtkTreePath *treepath, GtkTreeViewColumn *column, gpointer user_data) {

  GtkTreeIter iter;
  GtkTreeModel *model = gtk_tree_view_get_model(treeview);
  gchar *nickname;
  SilcClientID *clientid;
  SilcClientEntry cliententry;

  model = gtk_tree_view_get_model(treeview);
  gtk_tree_model_get_iter(model, &iter, treepath);

  debug("on_nicklist_activated()");
  gtk_tree_model_get (model, &iter, USERLIST_COL_NICKNAME, &nickname, -1);
  gtk_tree_model_get (model, &iter, USERLIST_COL_CLIENTID, &clientid, -1);

  if (!clientid) {
    debug("Did not get clientid at on_nicklist_activated");
    return;
  }

  if(SILC_ID_CLIENT_COMPARE(clientid, silky->conn->local_entry->id)) {
    debug("\tI won't talk to myself. Sorry.");
    printconsole(_("You do not want to talk to yourself."));
      return;
  }


  cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid);
  if (!cliententry) {
    debug("Did not get cliententry at on_nicklist_activated() ");
    return;
  }

  /* this opens new query window if no existing is
     open for the same clientid */
  get_query_window(clientid);

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

  return;
}




/* Pops up a menu over nicklist when user presses right mousebutton .
   The menu object is created in silccallbacks.c
*/
gint on_nick_pressed (GtkTreeView *treeview, GdkEventButton *event) {
  GtkTreeSelection *selection;
  GtkTreeIter iter;
  GtkTreeModel *model;
  gchar *nickname;
  SilcClientID *clientid;
  GtkMenu *menu;
  GtkWidget *menuitem;

  debug("on_nick_pressed() button: %d", event->button);

  if (event->button == 3) {


    /* Get selected item from the nicklist */
    selection = gtk_tree_view_get_selection(treeview);

    /* popup the menu only if something was selected */
    if (gtk_tree_selection_get_selected (selection, &model, &iter))
      {
	gtk_tree_model_get (model, &iter, USERLIST_COL_NICKNAME, &nickname, -1);
	gtk_tree_model_get (model, &iter, USERLIST_COL_CLIENTID, &clientid, -1);

	debug ("You opened menu for '%s'", nickname);

	/* create the menu */
	menu = GTK_MENU(gtk_menu_new());


	/* ADD MENU items and signal handlers*/

	/* WHOIS */
	menuitem = gtk_menu_item_new_with_label(_("Who is"));
	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_whois), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show (menuitem);

	/* send a message */
	menuitem = gtk_menu_item_new_with_label(_("Send message"));
	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_query), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show (menuitem);

	/* add to buddylist */
	menuitem = gtk_menu_item_new_with_label(_("Add Buddy"));
	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_buddy_add), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show (menuitem);


	/* a separator here */
	menuitem = gtk_separator_menu_item_new();
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* cumode +o user */
	menuitem = gtk_menu_item_new_with_label(_("Op"));
	g_object_set_data(G_OBJECT(menuitem), "SILC_COMMAND_CUMODE", "+o"); /* op */
 	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_cumode), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* cumode -o user */
	menuitem = gtk_menu_item_new_with_label(_("Deop"));
	g_object_set_data(G_OBJECT(menuitem), "SILC_COMMAND_CUMODE", "-o"); /* deop */
 	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_cumode), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* cumode +q user */
	menuitem = gtk_menu_item_new_with_label(_("Mute"));
	g_object_set_data(G_OBJECT(menuitem), "SILC_COMMAND_CUMODE", "+q"); /* mute */
 	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_cumode), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* cumode -q user */
	menuitem = gtk_menu_item_new_with_label(_("Unmute"));
	g_object_set_data(G_OBJECT(menuitem), "SILC_COMMAND_CUMODE", "-q"); /* unmute */
 	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_cumode), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* a separator here */
	menuitem = gtk_separator_menu_item_new();
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);

	/* kick user */
	menuitem = gtk_menu_item_new_with_label(_("Kick"));
	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_kick), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);


	/* kill user */
	/* FIXME, the on_command_kill needs some work
	menuitem = gtk_menu_item_new_with_label(_("Kill"));
	g_signal_connect(menuitem, "activate", G_CALLBACK(on_command_kill), clientid);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
	gtk_widget_show(menuitem);
	*/

	gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
			event->button, event->time);
      }
  }

  return FALSE;
}



syntax highlighted by Code2HTML, v. 0.9.1