/* Jungle Monkey
 * Copyright (C) 1999-2001  The Regents of the University of Michigan
 *
 * 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
 */


#include "ggui_chat.h"
#include "ggui.h"
#include "ggui_channels.h"
#include "ggui_config.h"
#include "ggui_files.h"
#include "ggui_properties.h"
#include "entry_dialog.h"
#include "pixmaps.h"

#include "util/util.h"
#include "util/jmgnet.h"
#include "jmchat/jmchat.h"
#include "jmchannel/jmchannel.h"



/* ******************** */

typedef struct _Chat
{
  JMChat*	chat;
  gchar*	text;
  GURL*		channel;
  gchar*	path;

} Chat;


static GladeXML*     menu_xml;
static GtkCList*     chat_clist;
static GtkText*      chat_text;
static GtkEntry*     chat_entry;
static GtkLabel*     chat_nickname;
static GtkContainer* chat_container;
static GtkWidget*    chat_popup;
		     
static gchar*	     nickname;
static GHashTable*   url_to_chat;
static Chat*	     selected_chat;




/* ******************** */

static void  shutdown_hfunc (gpointer key, gpointer value, gpointer user_data);

static void  load_chat_func (const ElfNode* elf);
static void  load_chat_ann_func (const ElfNode* elf);
static void  save_chat_hfunc (gpointer key, gpointer value, gpointer user_data);
static void  save_chat_ann_hfunc (gpointer key, gpointer value, gpointer user_data);

static void  create (const GURL* channel, const gchar* path, const gchar* name);
static void  join (const GURL* url);

static Chat* chat_new (JMChat* jmchat);
static void  chat_update (Chat* chat);
#define chat_get(U) ((Chat*) g_hash_table_lookup(url_to_chat, U))
static void  chat_delete (Chat* chat);

void on_chat_select_row (GtkCList* list, gint row, gint column, GdkEvent* event);
void on_chat_unselect_row (GtkCList* list, gint row, gint column, GdkEvent* event);
gint on_chat_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data);
void on_chat_entry_activate (GtkEditable* editable, gpointer user_data);

static void chat_info_cb (JMChat* chat, JMChatMember* member,
			  JMChatType type, gchar* text1, gchar* text2,
			  gpointer user_data);
static void chat_error_cb (JMChat* chat, gpointer user_data);

static void my_text_insert (GtkText* text, gchar* str);


typedef enum
{
  IT_CREATE,
  IT_OPEN,
  IT_JOIN,
  IT_LEAVE,
  IT_REMOVE,
  
  IT_SEPARATOR1,
  IT_SEPARATOR2,
  
  IT_PROPERTIES,

} Item;


static void toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
			    const gchar* tooltip, void* func);
static void update_toolbar (void);

static void update_items (void* widget);
static void show_item (void* widget, Item it);

#define POPUPFUNC(F) void F(gpointer callback_data, guint callback_action, GtkWidget* widget)

POPUPFUNC(on_chat_join);
POPUPFUNC(on_chat_leave);
POPUPFUNC(on_chat_remove);
POPUPFUNC(on_chat_create);
POPUPFUNC(on_chat_open);
POPUPFUNC(on_chat_properties);



/* ************************************************************ */


void
ggui_chat_init(void)
{
  menu_xml = ggui_get_glade_xml("chat_popup", "master_popup.glade");
  g_assert (menu_xml != NULL);
  glade_xml_signal_autoconnect(menu_xml);

  chat_clist = GTK_CLIST(glade_xml_get_widget(jmw_xml, "chat_clist"));
  chat_text  = GTK_TEXT(glade_xml_get_widget(jmw_xml, "chat_text"));
  chat_entry  = GTK_ENTRY(glade_xml_get_widget(jmw_xml, "chat_entry"));
  chat_nickname = GTK_LABEL(glade_xml_get_widget(jmw_xml, "chat_nickname"));
  chat_container = GTK_CONTAINER(glade_xml_get_widget(jmw_xml, "chat_container"));

  chat_popup = GTK_WIDGET(glade_xml_get_widget(menu_xml, "chat_popup"));

  g_assert (chat_clist);
  g_assert (chat_text);
  g_assert (chat_entry);
  g_assert (chat_nickname);
  g_assert (chat_container);
  g_assert (chat_popup);

  gtk_widget_set_sensitive (GTK_WIDGET(chat_container), FALSE);

  nickname = ggui_chat_get_nickname ();
  gtk_label_set_text (chat_nickname, nickname);

  url_to_chat = g_hash_table_new (gnet_url_hash, gnet_url_equal);

  /* TODO: tooltips */
/*    ggui_set_tooltip (GTK_WIDGET(channels_clist), _("Channels you have joined.")); */
}


void
ggui_chat_shutdown (void)
{
  /* Delete chats */
  g_hash_table_foreach (url_to_chat, shutdown_hfunc, NULL);
  g_hash_table_destroy (url_to_chat);
  url_to_chat = NULL;
  selected_chat = NULL;

  /* Save nickname */
  if (nickname)
    ggui_config_set_string ("/Jungle Monkey/Chat/Nickname", nickname);
}


void
shutdown_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Chat* chat = (Chat*) value;
  chat_delete (chat);
}


void
ggui_chat_load (void)
{
  ggui_load_elf ("chat",          load_chat_func);
  ggui_load_elf ("chat-announce", load_chat_ann_func);
}


static void
load_chat_func (const ElfNode* elf)
{
  const gchar* name;
  const gchar* url_str;
  GURL*   url = NULL;
  JMChat* jmchat = NULL;

  name    = elf_get_attribute (elf, "name");
  url_str = elf_get_attribute (elf, "url");
  if (url_str) url = gnet_url_new (url_str);

  if (!strcmp (elf->name, "create") && name)
    jmchat = jmchat_create (jm_btp_bpeer, name, nickname);
  else if (!strcmp (elf->name, "join") && url && !chat_get(url))
    jmchat = jmchat_join (jm_btp_bpeer, url, nickname);
  else if (!strcmp (elf->name, "setup") && url && !chat_get(url))
    jmchat = jmchat_setup (jm_btp_bpeer, url, nickname);
  if (jmchat)
    chat_new (jmchat);

  gnet_url_delete (url);
}


static void
load_chat_ann_func (const ElfNode* elf)
{
  const gchar*  curl_str;
  const gchar*  url_str;
  const gchar*  path;
  GURL*   curl = NULL;
  GURL*   url  = NULL;
  Chat*   chat;
  JMAnn*  ann;

  curl_str = elf_get_attribute (elf, "curl");
  url_str  = elf_get_attribute (elf, "url");
  path     = elf_get_attribute (elf, "path");
  if (!curl_str || !url_str || !path) return;

  curl = gnet_url_new (curl_str);
  url = gnet_url_new (url_str);
  if (!url || !curl) goto done;
      
  /* Change URL's */
  if (!SAFESTRCMP(jm_hostname, curl->hostname) &&
      (!jm_btp_last_port || curl->port == jm_btp_last_port))
    gnet_url_set_port (curl, jm_btp_bpeer->port);
  if (!SAFESTRCMP(jm_hostname, url->hostname) &&
      (!jm_btp_last_port || url->port == jm_btp_last_port))
    gnet_url_set_port (url, jm_btp_bpeer->port);

  chat = chat_get (url);
  if (!chat || chat->channel) goto done; /* TODO: Multiple paths */

  ann = ggui_channel_add_path (curl, path, url);
  if (!ann) goto done;

  chat->channel = curl; curl = NULL;
  chat->path    = g_strdup (ann->path);

 done:
  gnet_url_delete (url);
  gnet_url_delete (curl);

}


void
ggui_chat_save (void)
{
  ggui_save_hashtable ("chat",          url_to_chat, save_chat_hfunc);
  ggui_save_hashtable ("chat-announce", url_to_chat, save_chat_ann_hfunc);
}


static void
save_chat_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Chat*    chat = (Chat*) value;
  FILE*    file = (FILE*) user_data;
  ElfNode* elf;
  gchar*   buf;
  gint     len;

  elf = elf_new (NULL);

  if (chat->chat->is_local)
    {
      elf_set_name (elf, "create");
      elf_set_attribute (elf, "name", gnet_url_basename (chat->chat->url));
    }
  else if (jmchat_is_up (chat->chat))
    {
      gchar* url_str;

      elf_set_name (elf, "join");
      url_str = gnet_url_get_nice_string (chat->chat->url);
      elf_set_attribute (elf, "url", url_str);
      g_free (url_str);
    }
  else
    {
      gchar* url_str;

      elf_set_name (elf, "setup");
      url_str = gnet_url_get_nice_string (chat->chat->url);
      elf_set_attribute (elf, "url", url_str);
      g_free (url_str);
    }

  elf_write (elf, &buf, &len);
  fwrite (buf, len, 1, file);

  elf_delete (elf);
  g_free (buf);
}


static void
save_chat_ann_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Chat*    chat = (Chat*) value;
  FILE*    file = (FILE*) user_data;
  gchar*   curl;
  gchar*   url;
  ElfNode* elf;
  gchar*   buf;
  gint     len;

  if (!(chat->channel && chat->path))
    return;

  url  = gnet_url_get_nice_string (chat->chat->url);
  curl = gnet_url_get_nice_string (chat->channel);

  elf = elf_new_format ("ann", "sss", "curl", curl, "path", chat->path, 
			"url", url);
  elf_write (elf, &buf, &len);
  fwrite (buf, len, 1, file);

  elf_delete (elf);
  g_free (buf);
  g_free (url);
  g_free (curl);
}




/* ******************** */

JMChat*  
ggui_chat_get (const GURL* url)
{
  Chat* chat;

  g_return_val_if_fail (url, NULL);

  chat = (Chat*) g_hash_table_lookup (url_to_chat, url);
  if (!chat)
    return NULL;

  return chat->chat;
}


gboolean
ggui_chat_is_up (const GURL* url)
{
  JMChat* jmchat;

  g_return_val_if_fail (url, FALSE);

  jmchat = ggui_chat_get (url);
  if (!jmchat)
    return FALSE;

  return jmchat_is_up (jmchat);
}


gboolean
ggui_chat_is_down (const GURL* url)
{
  JMChat* jmchat;

  g_return_val_if_fail (url, FALSE);

  jmchat = ggui_chat_get (url);
  if (!jmchat)
    return FALSE;

  return jmchat_is_down (jmchat);
}


gchar*   
ggui_chat_get_properties (const GURL* url)
{
  JMChat* jmchat;

  jmchat = ggui_chat_get (url);
  if (!jmchat)
    return g_strdup ("");

  return g_strdup_printf (_("Chat %s\n"), gnet_url_basename(url));
}



/* ******************** */

static EntryDialog* create_chat_dialog;
static GURL*        create_chat_channel;
static gchar* 	    create_chat_path;

static void	    create_ok (gchar* chat_name, gchar* ignore);
static void	    create_cancel (void);


void
ggui_chat_create (const GURL* channel, const gchar* path, const gchar* name)
{
  g_return_if_fail ((channel && path) || !(channel && path));

  /* Prompt for name if not given */
  if (!name)
    {
      if (create_chat_dialog)
	{
	  entry_dialog_show (create_chat_dialog);
	  return;
	}

      if (channel && path)
	{
	  create_chat_channel = gnet_url_clone (channel);
	  create_chat_path = g_strdup (path);
	}

      create_chat_dialog  = entry_dialog_new(_("Create chat"), 
					     _("Chat name"), NULL,
					     create_ok, create_cancel);
      return;
    }

  create (channel, path, name);
}


static void
create_ok (gchar* chat_name, gchar* ignore)
{
  create (create_chat_channel, create_chat_path, chat_name);
  create_cancel ();
}


static void
create_cancel (void)
{
  create_chat_dialog = NULL;
  gnet_url_delete (create_chat_channel);
  create_chat_channel = NULL;
  g_free (create_chat_path);
  create_chat_path = NULL;
}


static void
create (const GURL* channel, const gchar* path, const gchar* name)
{
  JMChat*    jmchat;
  Chat*	     chat;

  g_return_if_fail (name);
  g_return_if_fail ((channel && path) || !(channel && path));

  /* Check if path ok */
  if (channel && path && !ggui_channel_check_path (channel, path, name))
    return;

  /* Create the channel */
  jmchat = jmchat_create (jm_btp_bpeer, name, nickname);
  if (!jmchat)
    {
      ggui_show_message ("warning", _("Could not create chat \"%s\"\n"), name);
      return;
    }

  /* Add it */
  chat = chat_new (jmchat);
  g_return_if_fail (chat);

  /* Announce it */
  if (channel && path)
    {
      JMAnn* ann;
      ann = ggui_channel_add (channel, path, name, jmchat->url);
      g_return_if_fail (ann);
      chat->channel = gnet_url_clone (channel);
      chat->path = g_strdup (ann->path);
    }
}



/* ********** */

static EntryDialog* join_dialog  = NULL;

static void join_ok (gchar* url, gchar* ignore);
static void join_cancel (void);


void
ggui_chat_join (const GURL* url)
{
  Chat* chat;

  /* Prompt for name if not given */
  if (!url)
    {
      if (join_dialog)
	{
	  entry_dialog_show (join_dialog);
	  return;
	}

      join_dialog = entry_dialog_new(_("Open chat"), 
				     _("Chat URL"), NULL,
				     join_ok, join_cancel);

      return;
    }

  /* Check if chat exists */
  chat = (Chat*) g_hash_table_lookup (url_to_chat, url);
  if (chat)
    {
      if (!jmchat_is_up (chat->chat))
	{
	  jmchat_rejoin (chat->chat);
	  chat_update (chat);
	}
      return;
    }

  /* Create it */
  join (url);
}


static void
join_ok (gchar* url, gchar* ignore)
{
  GURL* gurl;

  gurl = gnet_url_new (url);
  if (gurl && gnet_url_is_scheme(gurl, "jmchat"))
    {
      join (gurl);
      gnet_url_delete (gurl);
    }
  else
    {
      ggui_show_message ("warning", _("Malformed URL: %s (should be jmchat://something)\n"));
    }

  join_cancel ();
}


static void
join_cancel(void)
{
  join_dialog = NULL;
}


static void
join (const GURL* url)
{
  Chat*   chat;
  JMChat* jmchat;

  g_return_if_fail (url);

  /* Check if chat exists */
  chat = (Chat*) g_hash_table_lookup (url_to_chat, url);
  if (chat)
    {
      if (!jmchat_is_up (chat->chat))
	{
	  jmchat_rejoin (chat->chat);
	  chat_update (chat);
	}

      ggui_show_message ("warning", _("Chat is already open\n"));
      return;
    }

  /* Create chat */
  jmchat = jmchat_join (jm_btp_bpeer, url, nickname);
  if (!jmchat)
    {
      ggui_show_message ("warning", _("Could not open chat\n"));
      return;
    }

  /* Add it */
  chat = chat_new (jmchat);
  g_return_if_fail (chat);
}



/* ********** */

void
ggui_chat_leave (const GURL* url)
{
  Chat* chat;

  g_return_if_fail (url);

  /* Get chat */
  chat = (Chat*) g_hash_table_lookup (url_to_chat, url);
  if (!chat)
    return;

  /* Leave group */
  jmchat_leave (chat->chat);
  g_free (chat->text);
  chat->text = NULL;

  /* Unselect chat */
  if (chat == selected_chat)
    {
      gtk_editable_delete_text (GTK_EDITABLE(chat_text), 0, -1);
      gtk_widget_set_sensitive (GTK_WIDGET(chat_container), FALSE);
    }

  chat_update (chat);
}


void
ggui_chat_remove (const GURL* url)
{
  Chat* chat;
  gint  row;

  g_return_if_fail (url);

  /* Get chat */
  chat = chat_get (url);
  if (!chat)
    return;
  g_hash_table_remove (url_to_chat, url);

  /* Remove the chat from the list (this will unselect the chat if it
     is selected) */
  row = gtk_clist_find_row_from_data (chat_clist, chat);
  if (row >= 0)
    gtk_clist_remove (chat_clist, row);

  /* Send remove */
  if (chat->chat->is_local && chat->channel && chat->path)
    ggui_channel_remove_path (chat->channel, chat->path);
  /* remove_path may delete url */

  /* Delete group */
  chat_delete (chat);
}


gchar*
ggui_chat_get_nickname (void)
{
  /* Try using config */
  if (nickname == NULL)
    {
      nickname = 
	ggui_config_get_string ("/Jungle Monkey/Chat/Nickname", NULL);

      /* Try g_get_user_name */
      if (!nickname && (nickname = g_get_user_name()))
	nickname = g_strdup (nickname);
      
      /* Try g_get_real_name */
      if (!nickname && (nickname = g_get_real_name()))
	nickname = g_strdup (nickname);

      if (!nickname)
	nickname = g_strdup ("Anonymous Monkey");
    }

  return nickname;
}


void
ggui_chat_set_nickname (const gchar* nick)
{
  g_return_if_fail (nick);

  if (nickname)
    g_free (nickname);

  nickname = g_strdup (nick);
  gtk_label_set_text (chat_nickname, nickname);
}


void
ggui_chat_select (const GURL* url)
{
  Chat* chat;
  gint  row;

  g_return_if_fail (url);

  /* Get chat */
  chat = (Chat*) g_hash_table_lookup (url_to_chat, url);
  if (!chat)
    return;

  row = gtk_clist_find_row_from_data (chat_clist, chat);
  if (row >= 0)
    gtk_clist_select_row (chat_clist, row, 0);
}


GURL*
ggui_chat_get_selected (void)
{
  if (selected_chat)
    return selected_chat->chat->url;

  return NULL;
}


/* **************************************** */

static Chat*
chat_new (JMChat* jmchat)
{
  Chat*      chat;
  gchar*     texts[2] = {NULL, NULL};
  gint 	     row;

  g_return_val_if_fail (jmchat, NULL);
  g_return_val_if_fail (jmchat->url, NULL);

  /* Make sure chat isn't there */
  chat = (Chat*) g_hash_table_lookup (url_to_chat, jmchat->url);
  g_return_val_if_fail (!chat, NULL);
  
  /* Create new chat */
  chat = g_new0 (Chat, 1);
  chat->chat = jmchat;
  g_hash_table_insert (url_to_chat, jmchat->url, chat);

  /* Initialize it */
  jmchat->info_func  = chat_info_cb;
  jmchat->error_func = chat_error_cb;
  jmchat->user_data  = chat;

  /* Add to clist */
  texts[0] = gnet_url_basename (jmchat->url);
  row = gtk_clist_append (chat_clist, texts);
  gtk_clist_set_row_data (chat_clist, row, chat);

  /* Update */
  chat_update (chat);

  /* Select it if no other is selected */
  if (!ggui_chat_get_selected())
    ggui_chat_select (jmchat->url);

  return chat;
}


static void
chat_update (Chat* chat)
{
  GdkPixmap* pixmap;
  GdkBitmap* mask;
  gint	     row;
  GURL*	     url;

  g_return_if_fail (chat);
  url = chat->chat->url;

  /* Update icons */
  ggui_get_pixmaps (url, &pixmap, &mask);
  row = gtk_clist_find_row_from_data (chat_clist, chat);
  gtk_clist_set_pixtext (chat_clist, row, 0, 
			 gnet_url_basename(url), 5, pixmap, mask);

  /* Set sensitivity */
  if (chat == selected_chat)
    {
      update_toolbar ();
      gtk_widget_set_sensitive (GTK_WIDGET(chat_container), jmchat_is_up(chat->chat));
    }

  /* Update files */
  ggui_file_update (url);
}


static void
chat_delete (Chat* chat)
{
  jmchat_leave (chat->chat);
  jmchat_delete (chat->chat);
  g_free (chat->text);
  gnet_url_delete (chat->channel);
  g_free (chat->path);
  g_free (chat); 
}



/* **************************************** */

static void
chat_info_cb (JMChat* jmchat, JMChatMember* member,
	      JMChatType type, gchar* text1, gchar* text2,
	      gpointer user_data)
{
  Chat* chat = (Chat*) user_data;
  gchar* str = NULL;

  g_return_if_fail (member);

  switch (type)
    {
    case JMCHAT_JOIN:
      {
	str = g_strdup_printf (_("-> %s has joined\n"), member->nick);
	break;
      }

    case JMCHAT_LEAVE:
      {
	str = g_strdup_printf (_("<- %s has left\n"), member->nick);
	break;
      }

    case JMCHAT_LEAVE_TIMEOUT:
      {
	str = g_strdup_printf (_("<- %s has left (timeout)\n"), member->nick);
	break;
      }

    case JMCHAT_SAY:
      {
	g_return_if_fail (text1);
	if (!text2)
	  str = g_strdup_printf (_("%s: %s\n"), member->nick, text1);
	else
	  str = g_strdup_printf (_("%s %s\n"), member->nick, text1);
	break;
      }
      
    default: 
      break;;
    }

  if (str)
    {
      if (chat == selected_chat)
	{
	  my_text_insert (chat_text, str);
	}

      else
	{
	  if (chat->text)
	    {
	      gchar* temp;

	      temp = g_strconcat (chat->text, str, NULL);
	      g_free (chat->text);
	      chat->text = temp;
	    }
	  else
	    chat->text = g_strdup (str);
	}

      g_free (str);
    }
}


static void
chat_error_cb (JMChat* jmchat, gpointer user_data)
{
  chat_update ((Chat*) user_data);
}





/* **************************************** */


void 
on_chat_select_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  Chat* new_selected_chat;

  new_selected_chat = (Chat*) gtk_clist_get_row_data(list, row);
  if (new_selected_chat == selected_chat)
    return;

  /* Save the text of the old selected chat */
  if (selected_chat)
    {
      gchar* text;

      text = gtk_editable_get_chars (GTK_EDITABLE(chat_text), 0, -1);
      g_return_if_fail (text);
      
      if (selected_chat->text)
	g_free (selected_chat->text);

      selected_chat->text = text;
    }

  /* Restore the text of the new selected chat */
  if (new_selected_chat)
    {
      gtk_editable_delete_text (GTK_EDITABLE(chat_text), 0, -1);

      if (new_selected_chat->text)
	{
	  gtk_text_insert (chat_text, NULL, NULL, NULL, 
			   new_selected_chat->text,
			   strlen(new_selected_chat->text));
	  gtk_text_set_point (chat_text, gtk_text_get_length (chat_text));
	}
    }

  selected_chat = new_selected_chat;

  gtk_widget_set_sensitive (GTK_WIDGET(chat_container), 
			    (selected_chat != NULL && jmchat_is_up (selected_chat->chat)));

  update_toolbar();
}


void 
on_chat_unselect_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  /* Save the text of the old selected chat */
  if (selected_chat)
    {
      gchar* text;

      text = gtk_editable_get_chars (GTK_EDITABLE(chat_text), 0, -1);
      g_return_if_fail (text);
      
      if (selected_chat->text)
	g_free (selected_chat->text);

      selected_chat->text = text;

      gtk_editable_delete_text (GTK_EDITABLE(chat_text), 0, -1);
    }


  selected_chat = NULL;
  gtk_widget_set_sensitive (GTK_WIDGET(chat_container), FALSE);
  update_toolbar();
}



/* Return TRUE if we handle event; FALSE to pass on */
gint
on_chat_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data)
{
  gint row, column;
  gboolean rv = FALSE;

  /* User double clicked - activate the thing selected */
  if (event->type == GDK_2BUTTON_PRESS && 
      (event->button == 1 || event->button == 2))
    {
      /* Get the selection information */
      if (gtk_clist_get_selection_info(list, event->x, event->y, &row, &column))
	{
	  /* Select the clicked row, if there is one */
	  gtk_clist_select_row (list, row, 0);
	
	  /* Double clicks on chats activates them. */
	  if (selected_chat && !jmchat_is_up(selected_chat->chat))
	    ggui_chat_join (selected_chat->chat->url);
	}
      else
	gtk_clist_unselect_all(list);

      rv = TRUE;
    }

  /* User right-clicked - show context menu */
  else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
    {
      GList* i;

      /* Select the clicked row, if there is one */
      if (gtk_clist_get_selection_info (list, event->x, event->y, &row, &column))
	gtk_clist_select_row (list, row, 0);
      else
	gtk_clist_unselect_all (list);

      /* Hide everything */
      for (i = GTK_MENU_SHELL(chat_popup)->children; i != NULL; i = i->next)
	{
	  GtkWidget* menu_item = GTK_WIDGET(i->data);
	  g_return_val_if_fail (GTK_MENU_ITEM(menu_item) != NULL, FALSE);
	  gtk_widget_hide(menu_item);
	}

      /* Update the popup menu */
      update_items (NULL);

      /* Pop up */
      gtk_menu_popup (GTK_MENU(chat_popup), NULL, NULL,
		      NULL, NULL, 3, event->time);

      rv = TRUE;
    }

  return rv;
}


void
on_chat_entry_activate (GtkEditable* editable, gpointer user_data)
{
  gchar* text;
  gchar* str;
  
  g_return_if_fail (chat_entry);
  g_return_if_fail (selected_chat);

  text = gtk_entry_get_text (chat_entry);
  if (!text)
    return;

  if (text[0] != '/')
    {
      /* Check if 'say' */
      if (text[0] != ':')
	{
	  str = g_strdup_printf (_("%s: %s\n"), nickname, text);
	  my_text_insert (chat_text, str);
	  g_free (str);

	  if (jmchat_is_up (selected_chat->chat))
	    jmchat_say (selected_chat->chat, text, FALSE);
	}

      /* Otherwise, is emote */
      else
	{
	  str = g_strdup_printf (_("%s %s\n"), nickname, &text[1]);
	  my_text_insert (chat_text, str);
	  g_free (str);

	  if (jmchat_is_up (selected_chat->chat))
	    jmchat_say (selected_chat->chat, &text[1], TRUE);
	}
    }
  else
    {
      if (!SAFESTRCMP (&text[1], _("help")))
	{
	  my_text_insert (chat_text, 
			  _("\nCommands:\n"
			    "\t/who       List users in this group\n"
			    "\t:<mesg>    Emote <mesg>\n"
			    "\n"));
	}
      else if (!SAFESTRCMP (&text[1], "who"))
	{
	  GSList* i;

	  my_text_insert (chat_text, _("\nMembers:\n"));

	  for (i = selected_chat->chat->members; i != NULL; i = i->next)
	    {
	      JMChatMember* member = (JMChatMember*) i->data;
	      gchar* str;

	      str = g_strdup_printf (_("\t%s\n"), member->nick);
	      my_text_insert (chat_text, str);
	      g_free (str);
	    }

	  my_text_insert (chat_text, "\n");
	}
      else
	{
	  my_text_insert (chat_text, _("Unknown command.  Try /help\n"));
	}
    }

  gtk_entry_set_text (chat_entry, "");
}


void
ggui_chat_show_toolbar (void)
{
  /* Add everything to the toolbar */
  toolbar_append (IT_OPEN, chat_pixmap, chat_mask, 
		  _("Open chat"), on_chat_open);
  toolbar_append (IT_CREATE, create_chat_pixmap, create_chat_mask, 
		  _("Create chat"), on_chat_create);
  toolbar_append (IT_JOIN, join_chat_pixmap, join_chat_mask, 
		  _("Join chat"), on_chat_join);
  toolbar_append (IT_LEAVE, leave_chat_pixmap, leave_chat_mask, 
		  _("Leave chat"), on_chat_leave);
  /* FIX: X */

  /* Update the toolbar */
  update_toolbar();
}


static void
toolbar_append (Item item, GdkPixmap* gpixmap, GdkBitmap* gbitmap,
	       const gchar* tooltip, void* func)
{
  GtkPixmap* pixmap;				     	 
  GtkWidget* widget;				     	 

  pixmap = (GtkPixmap*) gtk_pixmap_new (gpixmap, gbitmap);
  g_return_if_fail (pixmap);                         	 
  widget = gtk_toolbar_append_item (jmw_toolbar, NULL,   
				    tooltip, "private",                 
				    GTK_WIDGET(pixmap),           
				    (GtkSignalFunc) func, NULL);     
  g_return_if_fail (widget);                             
  gtk_object_set_user_data (GTK_OBJECT(widget), (gpointer) item);
}


static void
update_toolbar (void)
{
  GList* kids;
  GList* i;

  /* Hide all */
  kids = gtk_container_children (GTK_CONTAINER(jmw_toolbar));
  for (i = kids; i != NULL; i = i->next)
    gtk_widget_set_sensitive (GTK_WIDGET(i->data), FALSE);

  /* Update toolbar */
  update_items (kids);

  g_list_free (kids);
}


/* **************************************** */

static const char* item_mi_name[] =
{
  "create_mi",
  "open_mi",
  "join_mi",
  "leave_mi",
  "remove_mi",

  "separator1",
  "separator2",

  "properties_mi",
};

static void
update_items (void* widget)
{

  show_item (widget, IT_OPEN);
  show_item (widget, IT_CREATE);

  /* Build the menu */
  if (selected_chat)
    {
      show_item (widget, IT_SEPARATOR1);

      if (selected_chat->chat->is_local)
	show_item (widget, IT_REMOVE);
      else if (jmchat_is_up (selected_chat->chat))
	{
	  show_item (widget, IT_LEAVE);
	  show_item (widget, IT_REMOVE);
	}
      else
	{
	  show_item (widget, IT_JOIN);
	  show_item (widget, IT_REMOVE);
	}

      show_item (widget, IT_SEPARATOR2);
      show_item (widget, IT_PROPERTIES);
    }
}


static void
show_item (void* widget, Item it)
{
  if (!widget)	/* Popup menu */
    {
      GtkWidget* menu_item = glade_xml_get_widget(menu_xml, item_mi_name[it]);
      if (menu_item)
	gtk_widget_show(menu_item);
    }
  else		/* Toolbar    */
    {
      GList* kids = (GList*) widget;
      GList* i;

      for (i = kids; i != NULL; i = i->next)		 
	{
	  if (it == (guint) gtk_object_get_user_data(GTK_OBJECT(i->data)))
	    {                                                  
	      gtk_widget_set_sensitive (GTK_WIDGET(i->data), TRUE); 
	      break;                                           
	    }                                                  
	}
    }
}


POPUPFUNC(on_chat_join)
{
  g_return_if_fail (selected_chat);
  ggui_chat_join (selected_chat->chat->url);
}


POPUPFUNC(on_chat_leave)
{
  g_return_if_fail (selected_chat);
  ggui_chat_leave (selected_chat->chat->url);
}


POPUPFUNC(on_chat_remove)
{
  GURL* url;

  g_return_if_fail (selected_chat);
  url = selected_chat->chat->url;

  if (selected_chat->chat->is_local)
    {
      ggui_chat_remove (url);
    }
  else if (jmchat_is_up(selected_chat->chat))
    {
      ggui_chat_leave (url);
      ggui_chat_remove (url);
    }
  else
    {
      ggui_chat_remove (url);
    }
}


POPUPFUNC(on_chat_create)
{
  ggui_chat_create (NULL, NULL, NULL);
}


POPUPFUNC(on_chat_open)
{
  ggui_chat_join (NULL);
}



POPUPFUNC(on_chat_properties)
{
  g_return_if_fail (selected_chat);
  ggui_properties_show (selected_chat->chat->url);
}



/**

   What I want to do is this:
   
   If the scroll bar is at the bottom, insert the text and scroll down.

   If the scroll bar is not at the bottom, insert the text without
   moving the scroll bar.

   I can't figure out how to do this.

 */
static void
my_text_insert (GtkText* text, gchar* str)
{
/*    guint old_point; */
/*    guint old_position; */
/*    guint length; */
  guint str_len;

/*    gtk_text_freeze (text); */

/*    old_point = gtk_text_get_point (text); */
/*    old_position = gtk_editable_get_position (GTK_EDITABLE(text)); */
/*    length = gtk_text_get_length (text); */
  str_len = strlen(str);

/*    g_print ("OLD: position = %d, point = %d, length = %d, str_len = %d\n", */
/*  	   old_position, old_point, length, str_len); */
/*    g_print ("OLD vert adj value = %f, upper = %f, stepinc = %f, pageinc = %f, pagesize = %f\n", text->vadj->value, text->vadj->upper, text->vadj->step_increment, text->vadj->page_increment, text->vadj->page_size); */

  gtk_text_insert (text, NULL, NULL, NULL, str, str_len);
/*    gtk_editable_insert_text (GTK_EDITABLE(text), str, str_len, &length); */

/*    g_print ("NEW: position = %d, point = %d, length = %d\n", */
/*  	   gtk_editable_get_position (GTK_EDITABLE(text)),  */
/*  	   gtk_text_get_point (text),  */
/*  	   gtk_text_get_length (text)); */
/*    g_print ("NEW vert adj value = %f, upper = %f, stepinc = %f, pageinc = %f, pagesize = %f\n", text->vadj->value, text->vadj->upper, text->vadj->step_increment, text->vadj->page_increment, text->vadj->page_size); */

  /* Move point back if they had scrolled up */
/*    if (old_position == length) */
/*      gtk_editable_set_position (GTK_EDITABLE(text), length + str_len); */
  /* Otherwise move to the end */
/*    else */
/*      gtk_editable_set_position (GTK_EDITABLE(text), old_position); */

/*    if (old_point == length) */
/*      { */
/*        g_print ("set old point\n"); */
/*        gtk_text_set_point (text, length + str_len); */
/*      } */
/*    else */
/*      { */
/*        g_print ("set bottom point\n"); */
/*        gtk_text_set_point (text, old_point); */
/*      } */

/*    gtk_editable_changed (GTK_EDITABLE (text)); */

/*    gtk_text_thaw (text); */
}



syntax highlighted by Code2HTML, v. 0.9.1