/* 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.h"
#include "ggui_channels.h"
#include "ggui_config.h"
#include "ggui_debug.h"
#include "ggui_files.h"
#include "ggui_properties.h"
#include "entry_dialog.h"

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


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

typedef struct _Channel
{
  JMChannel* jmchannel;
  JMAnn*     shown;

  /* TODO: This should be a list so we can support arbitrary
     linking. */
  GURL*	     channel;
  gchar*     path;

} Channel;


static GladeXML*   menu_xml;
static GtkCList*   channel_clist;
static GtkWidget*  channel_popup;
		   
static Channel*	   selected_channel;
static GHashTable* url_to_channel;


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

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

static void load_channel_func (const ElfNode* elf);
static void load_channel_ann_func (const ElfNode* elf);
static void save_channel_hfunc (gpointer key, gpointer value, gpointer user_data);
static void save_channel_ann_hfunc (gpointer key, gpointer value, gpointer user_data);

static Channel* channel_new (JMChannel* jmchannel);
static void channel_update (Channel* channel);
static void channel_delete (Channel* channel);
#define     channel_get(U)  ((Channel*) g_hash_table_lookup (url_to_channel, (U)))
static void channel_info (JMChannel* channel, JMChannelType type,
			  JMAnn* ann, gpointer user_data);
static void channel_error (JMChannel* jmchannel, gpointer user_data);

void on_channels_select_row(GtkCList* list, gint row, gint column, GdkEvent* event);
void on_channels_unselect_row(GtkCList* list, gint row, gint column, GdkEvent* event);
gint on_channels_button_press (GtkCList* list, GdkEventButton *event, gpointer user_data);

void on_channels_properties(gpointer callback_data, guint callback_action, GtkWidget* widget);

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


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


void
ggui_channel_init (void)
{
  /* Setup GUI */
  menu_xml = ggui_get_glade_xml ("channels_popup", "master_popup.glade");
  g_assert (menu_xml != NULL);
  glade_xml_signal_autoconnect(menu_xml);

  channel_clist = GTK_CLIST(glade_xml_get_widget(jmw_xml, "channels_clist"));
  channel_popup = GTK_WIDGET(glade_xml_get_widget(menu_xml, "channels_popup"));

  g_assert (channel_clist);
  g_assert (channel_popup);

  ggui_set_tooltip (GTK_WIDGET(channel_clist), _("Channels you have joined."));

  url_to_channel = g_hash_table_new (gnet_url_hash, gnet_url_equal);
  selected_channel = NULL;

}


void
ggui_channel_shutdown (void)
{
  /* Delete channels */
  g_hash_table_foreach (url_to_channel, shutdown_hfunc, NULL);
  g_hash_table_destroy (url_to_channel);
  url_to_channel = NULL;
  selected_channel = NULL;
}



void
shutdown_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Channel* channel = (Channel*) value;
  channel_delete (channel);
}


void
ggui_channel_load (void)
{
  GURL*    mc_url;
  Channel* mc_channel;

  ggui_load_elf ("channel",          load_channel_func);
  ggui_load_elf ("channel-announce", load_channel_ann_func);

  /* Setup Monkey Central, if it isn't there already */
  mc_url = gnet_url_new (JM_DEFAULT_CHANNEL);
  g_return_if_fail (mc_url);
  mc_channel = channel_get (mc_url);
  if (!mc_channel)
    {
      JMChannel* jmchannel;

      /* Create channel */
      jmchannel = jmchannel_setup (jm_btp_bpeer, mc_url);
      g_return_if_fail (jmchannel);
      if (jm_mtp_rvous_url && jm_mtp_server)
	jmchannel_set_mtp (jmchannel, jm_mtp_rvous_url, jm_mtp_server);

      /* Add it */
      channel_new (jmchannel);
    }

  gnet_url_delete (mc_url);
}


static void
load_channel_func (const ElfNode* elf)
{
  const gchar* name;
  const gchar* url_str;
  GURL*   url = NULL;
  JMChannel* jmchannel = 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)
    jmchannel = jmchannel_create (jm_btp_bpeer, name);
  else if (!strcmp (elf->name, "join") && url && !channel_get(url))
    jmchannel = jmchannel_join (jm_btp_bpeer, url);
  else if (!strcmp (elf->name, "setup") && url && !channel_get(url))
    jmchannel = jmchannel_setup (jm_btp_bpeer, url);
  if (jmchannel)
    {
      if (jm_mtp_rvous_url && jm_mtp_server)
	jmchannel_set_mtp (jmchannel, jm_mtp_rvous_url, jm_mtp_server);
      channel_new (jmchannel);
    }

  gnet_url_delete (url);
}


static void
load_channel_ann_func (const ElfNode* elf)
{
  const gchar*  curl_str;
  const gchar*  url_str;
  const gchar*  path;
  GURL*   curl = NULL;
  GURL*   url  = NULL;
  Channel* channel;
  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);

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

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

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

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

}



void
ggui_channel_save (void)
{
  ggui_save_hashtable ("channel",          url_to_channel, save_channel_hfunc);
  ggui_save_hashtable ("channel-announce", url_to_channel, save_channel_ann_hfunc);
}


static void
save_channel_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Channel* channel = (Channel*) value;
  FILE*    file = (FILE*) user_data;
  ElfNode* elf;
  gchar*   buf;
  gint     len;

  elf = elf_new (NULL);

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

      elf_set_name (elf, "join");
      url_str = gnet_url_get_nice_string (channel->jmchannel->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 (channel->jmchannel->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_channel_ann_hfunc (gpointer key, gpointer value, gpointer user_data)
{
  Channel* channel = (Channel*) value;
  FILE*    file = (FILE*) user_data;
  gchar*   curl;
  gchar*   url;
  ElfNode* elf;
  gchar*   buf;
  gint     len;

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

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

  elf = elf_new_format ("ann", "sss", "curl", curl, "path", channel->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);
}




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

JMChannel*  
ggui_channel_get (const GURL* url)
{
  Channel* channel;

  channel = channel_get (url);
  if (!channel)
    return NULL;

  return channel->jmchannel;

}

JMAnn*   
ggui_channel_get_ann (const GURL* url, const gchar* path)
{
  JMChannel* channel;

  channel = ggui_channel_get (url);
  if (!channel)
    return NULL;

  return jmchannel_get (channel, path);
}


gboolean
ggui_channel_check_path (const GURL* channel, const gchar* path, const gchar* name)
{
  JMChannel* pchannel;
  JMAnn*     pann;

  g_return_val_if_fail (channel, FALSE);
  g_return_val_if_fail (path,    FALSE);
  g_return_val_if_fail (name,    FALSE);

  /* Check channel */
  pchannel = ggui_channel_get (channel);
  if (!pchannel)
    {
      ggui_show_message ("warning", _("Channel %s no longer exists\n"), 
			 gnet_url_basename(channel));
      return FALSE;
    }

  /* Check path */
  pann = jmchannel_get (pchannel, path);
  if (!pann)
    {
      ggui_show_message ("warning", _("Path %s no longer exists on channel %s\n"),
			 path, jmchannel_get_name(pchannel));
      return FALSE;
    }

  /* Check if already exists */
  if (jmann_get_child (pann, name))
    {
      ggui_show_message ("warning", _("%s already exists in %s:%s\n"), 
			 name, jmchannel_get_name(pchannel), pann->path);
      return FALSE;
    }

  return TRUE;
}


gboolean
ggui_channel_is_up (const GURL* url)
{
  JMChannel* jmchannel;

  jmchannel = ggui_channel_get (url);
  if (!jmchannel)
    return FALSE;

  return jmchannel_is_up (jmchannel);
}


gboolean
ggui_channel_is_down (const GURL* url)
{
  JMChannel* jmchannel;

  jmchannel = ggui_channel_get (url);
  if (!jmchannel)
    return FALSE;

  return jmchannel_is_down (jmchannel);
}


gchar*
ggui_channel_get_properties (const GURL* url)
{
  JMChannel* jmchannel;


  jmchannel = ggui_channel_get (url);
  if (!jmchannel)
    return g_strdup ("");

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


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

static EntryDialog* create_dialog  = NULL;
static GURL*        create_channel = NULL;
static gchar* 	    create_path    = NULL;

static void create_ok (gchar* channel_name, gchar* ignore);
static void create_cancel (void);
static void create (const GURL* pchannel, const gchar* path, const gchar* name);


void
ggui_channel_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_dialog)
	{
	  entry_dialog_show (create_dialog);
	  return;
	}

      if (channel && path)
	{
	  create_channel = gnet_url_clone (channel);
	  create_path = g_strdup (path);
	}

      create_dialog  = entry_dialog_new(_("Create channel"), 
					_("Channel name"), NULL,
					create_ok, create_cancel);
      return;
    }

  create (channel, path, name);
}


static void
create_ok (gchar* channel_name, gchar* ignore)
{
  create (create_channel, create_path, channel_name);
  create_cancel ();
}


static void
create_cancel (void)
{
  create_dialog = NULL;
  gnet_url_delete (create_channel);
  create_channel = NULL;
  g_free (create_path);
  create_path = NULL;
}


static void
create (const GURL* pchannel, const gchar* path, const gchar* name)
{
  JMChannel* jmchannel;
  Channel*   channel;

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

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

  /* Create the channel */
  jmchannel = jmchannel_create (jm_btp_bpeer, name);
  if (!jmchannel)
    {
      ggui_show_message ("warning", _("Could not create channel \"%s\"\n"), name);
      return;
    }
  if (jm_mtp_rvous_url && jm_mtp_server)
    jmchannel_set_mtp (jmchannel, jm_mtp_rvous_url, jm_mtp_server);

  /* Add it */
  channel = channel_new (jmchannel);
  g_return_if_fail (channel);

  if (pchannel && path)
    {
      JMAnn* ann;

      ann = ggui_channel_add (pchannel, path, name, jmchannel->url);
      g_return_if_fail (ann);

      channel->channel = gnet_url_clone (pchannel);
      channel->path    = g_strdup (ann->path);
    }
}



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

static EntryDialog* join_dialog  = NULL;

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


void
ggui_channel_join (const GURL* url)
{
  Channel* channel;

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

      join_dialog = entry_dialog_new(_("Open channel"), 
				     _("Channel URL"), NULL,
				     join_ok, join_cancel);

      return;
    }

  /* Check if channel exists */
  channel = channel_get (url);
  if (channel)
    {
      if (!jmchannel_is_up (channel->jmchannel))
	{
	  jmchannel_rejoin (channel->jmchannel);
	  channel_update (channel);
	}
      return;
    }

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


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

  gurl = gnet_url_new (url);
  if (gurl  || !SAFESTRCMP(gurl->protocol, "jm"))
    {
      join (gurl);
      gnet_url_delete (gurl);
    }
  else
    {
      ggui_show_message ("warning", _("Malformed URL: %s (should be jm://something)\n"));
    }

  join_cancel ();
}


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


static void
join (const GURL* url)
{
  Channel*   channel;
  JMChannel* jmchannel;

  g_return_if_fail (url);

  /* Check if channel exists */
  channel = channel_get (url);
  if (channel)
    {
      if (!jmchannel_is_up (channel->jmchannel))
	{
	  jmchannel_rejoin (channel->jmchannel);
	  channel_update (channel);
	}

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

  /* Create channel */
  jmchannel = jmchannel_join (jm_btp_bpeer, url);
  if (!jmchannel)
    {
      ggui_show_message ("warning", _("Could not open channel\n"));
      return;
    }
  if (jm_mtp_rvous_url && jm_mtp_server)
    jmchannel_set_mtp (jmchannel, jm_mtp_rvous_url, jm_mtp_server);

  /* Add it */
  channel = channel_new (jmchannel);
  g_return_if_fail (channel);
}


void
ggui_channel_leave (const GURL* url)
{
  Channel* channel;

  g_return_if_fail (url);

  /* Get channel */
  channel = channel_get (url);
  if (!channel)
    return;

  /* Remove files */
  ggui_file_leave (url);

  /* Leave group */
  jmchannel_leave (channel->jmchannel);


  channel_update (channel);
}


void
ggui_channel_delete (const GURL* url)
{
  Channel* channel;
  gint     row;

  g_return_if_fail (url);

  /* Remove files */
  ggui_file_leave (url);

  /* Get channel */
  channel = channel_get (url);
  if (!channel)
    return;
  g_hash_table_remove (url_to_channel, url);

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

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

  /* Delete group */
  channel_delete (channel);
}


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

static Channel*
channel_new (JMChannel* jmchannel)
{
  Channel*   channel;
  gchar*     texts[2] = {NULL, NULL};
  gint 	     row;

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

  /* Make sure channel isn't there */
  g_return_val_if_fail (!channel_get(jmchannel->url), NULL);
  
  /* Create new channel */
  channel = g_new0 (Channel, 1);
  channel->jmchannel = jmchannel;
  g_hash_table_insert (url_to_channel, jmchannel->url, channel);

  /* Initialize it */
  jmchannel->info_func  = channel_info;
  jmchannel->error_func = channel_error;
  jmchannel->user_data  = channel;

  /* Add to clist */
  texts[0] = gnet_url_basename (jmchannel->url);
  row = gtk_clist_append (channel_clist, texts);
  gtk_clist_set_row_data (channel_clist, row, channel);

  /* Update */
  channel_update (channel);

  /* Select it if no other is selected */
  if (!ggui_channel_get_selected())
    ggui_channel_select (jmchannel->url);

  return channel;
}


static void
channel_update (Channel* channel)
{
  GdkPixmap* pixmap;
  GdkBitmap* mask;
  gint	     row;

  g_return_if_fail (channel);

  /* Update icons */
  ggui_get_pixmaps (channel->jmchannel->url, &pixmap, &mask);
  row = gtk_clist_find_row_from_data (channel_clist, channel);
  gtk_clist_set_pixtext (channel_clist, row, 0, 
			 gnet_url_basename(channel->jmchannel->url),
			 5, pixmap, mask);

  /* Update ggui_file */ 
  if (channel == selected_channel)
    {
      if (jmchannel_is_up (channel->jmchannel))
	{
	  if (!channel->shown)
	    channel->shown = channel->jmchannel->root_ann;
	  ggui_file_show (channel->jmchannel, channel->shown);
	}
      else
	ggui_file_show (NULL, NULL);
    }
}


static void
channel_delete (Channel* channel)
{
  jmchannel_leave (channel->jmchannel);
  jmchannel_delete (channel->jmchannel);
  gnet_url_delete (channel->channel);
  g_free (channel->path);
  g_free (channel); 
}



static void
channel_info (JMChannel* jmchannel, JMChannelType type, 
	      JMAnn* ann, gpointer user_data)
{
/*    Channel* channel = (Channel*) user_data; */

  switch (type)
    {
    case JMCHANNEL_UPDATE: 
      {
	ggui_file_announce_update (jmchannel, ann);
	break;
      }

    case JMCHANNEL_REMOVE: 
    case JMCHANNEL_REMOVE_TIMEOUT: 
      {
	Channel* channel;

	g_return_if_fail (ann != jmchannel->root_ann);

	channel = channel_get (jmchannel->url);
	if (channel && channel->shown == ann)
	  {
	    channel->shown = jmchannel->root_ann;
	    ggui_file_show (channel->jmchannel, channel->shown);
	  }

	ggui_file_announce_remove (jmchannel, ann);
	break;
      }

    default: 
      g_assert_not_reached();
    }
}


static void
channel_error (JMChannel* jmchannel, gpointer user_data)
{
  Channel* channel = (Channel*) user_data;
  channel_update (channel);
}


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

JMAnn*
ggui_channel_add_sha (const GURL* curl, const gchar* path, 
		      const gchar* name, const GURL* url,
		      const GSHA* sha)
{
  Channel* channel;
  JMAnn*   pann;
  ElfNode* elf;
  JMAnn*   ann;

  /* Get channel */
  channel = channel_get (curl);
  if (!channel)
    return NULL;

  /* Attempt to get parent */
  pann = jmchannel_get (channel->jmchannel, path);
  if (!pann)
    return NULL;

  if (jmann_get_child (pann, name))
    return NULL;

  elf = elf_new (NULL);
  if (sha)
    {
      gchar shastr[2 * GNET_SHA_HASH_LENGTH + 1];

      gnet_sha_copy_string (sha, shastr);
      shastr[2 * GNET_SHA_HASH_LENGTH] = '\0';
      elf_set_attribute (elf, "sha", shastr);
    }

  ann = jmchannel_add_elf (channel->jmchannel, pann, name, url, elf);
  /* jmchannel_add will call our channel_info */

  elf_delete (elf);

  return ann;
}


JMAnn*
ggui_channel_add_path_sha (const GURL* curl, const gchar* path, 
			   const GURL* url, const GSHA* sha)
{
  Channel* channel;
  ElfNode* elf;
  JMAnn*   ann;

  /* Get channel */
  channel = channel_get (curl);
  if (!channel)
    return NULL;

  /* Get ann */
  if (jmchannel_get (channel->jmchannel, path))
    return NULL;

  elf = elf_new (NULL);
  if (sha)
    {
      gchar shastr[2 * GNET_SHA_HASH_LENGTH + 1];

      gnet_sha_copy_string (sha, shastr);
      shastr[2 * GNET_SHA_HASH_LENGTH] = '\0';
      elf_set_attribute (elf, "sha", shastr);
    }

  ann = jmchannel_add_path_elf (channel->jmchannel, path, url, elf);
  /* jmchannel_add will call our channel_info */

  elf_delete (elf);

  return ann;
}


void
ggui_channel_remove (const GURL* url, const gchar* path, const gchar* name)
{
  Channel* channel;
  JMAnn*   pann;
  JMAnn*   ann;

  channel = channel_get (url);
  if (!channel)
    return;

  pann = jmchannel_get (channel->jmchannel, path);
  if (!pann)
    return;

  if ((ann = jmann_get_child (pann, name)) == NULL)
    return;

  jmchannel_remove (channel->jmchannel, ann);
  /* jmchannel_remove will call our channel_info */
}


void
ggui_channel_remove_path (const GURL* url, const gchar* path)
{
  Channel* channel;
  JMAnn*   ann;

  channel = channel_get (url);
  if (!channel)
    return;

  ann = jmchannel_get (channel->jmchannel, path);
  if (!ann)
    return;

  jmchannel_remove (channel->jmchannel, ann);
  /* jmchannel_remove will call our channel_info */
}



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

void
ggui_channel_select (const GURL* url)
{
  Channel* channel;
  gint     row;

  g_return_if_fail (url);

  channel = channel_get (url);
  if (!channel)
    return;

  row = gtk_clist_find_row_from_data (channel_clist, channel);
  if (row < 0) return;
  gtk_clist_select_row (channel_clist, row, 0);
}


GURL*
ggui_channel_get_selected (void)
{
  if (!selected_channel)
    return NULL;
  return selected_channel->jmchannel->url;
}


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


void 
on_channels_select_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  selected_channel = (Channel*) gtk_clist_get_row_data(list, row);

  if (selected_channel)
    {
      if (jmchannel_is_up (selected_channel->jmchannel))
	{
	  if (!selected_channel->shown)
	    selected_channel->shown = selected_channel->jmchannel->root_ann;
	  ggui_file_show (selected_channel->jmchannel, selected_channel->shown);
	}
      else
	ggui_file_show (NULL, NULL);
    }
  else
    ggui_file_show (NULL, NULL);
}


void 
on_channels_unselect_row (GtkCList* list, gint row, gint column, GdkEvent* event)
{
  /* Save selected file */
  if (selected_channel)
    {
      JMChannel* chan = NULL;
      JMAnn*     ann  = NULL;

      ggui_file_get_shown (&chan, &ann);
      if (chan == selected_channel->jmchannel)
	selected_channel->shown = ann;
      /* Otherwise, we may have previously called ggui_file_show.
         This happens when we leave a channel then unselect the
         channel. */
    }

  selected_channel = NULL;
  ggui_file_show (NULL, NULL);
}


#define HIDE_MENUITEM(MI) do {       					\
  GtkWidget* menu_item = glade_xml_get_widget(menu_xml, (MI));		\
  g_return_val_if_fail (menu_item != NULL, TRUE);			\
  gtk_widget_hide(menu_item);} while (0)

#define SHOW_MENUITEM(MI) do {       					\
  GtkWidget* menu_item = glade_xml_get_widget(menu_xml, (MI));		\
  g_return_val_if_fail (menu_item != NULL, TRUE);			\
  gtk_widget_show(menu_item);} while (0)


/* Return TRUE if we handle event; FALSE to pass on */
gint
on_channels_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 files and channels activate them.  We
	     don't do anything for directories because that might be
	     annoying. */
	  if (selected_channel)
	    ggui_channel_join (selected_channel->jmchannel->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(channel_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);
	}

      SHOW_MENUITEM("open_channel_mi");
      SHOW_MENUITEM("create_root_channel_mi");

      /* Build the menu */
      if (selected_channel)
	{
	  if (selected_channel->jmchannel->is_local)
	    {
	      SHOW_MENUITEM("remove_channel_mi");
	      SHOW_MENUITEM("separator1");
	    }
	  else if (jmchannel_is_up(selected_channel->jmchannel))
	    {
	      SHOW_MENUITEM("leave_channel_mi");
	      SHOW_MENUITEM("remove_channel_mi");
	      SHOW_MENUITEM("separator1");
	    }
	  else
	    {
	      SHOW_MENUITEM("join_channel_mi");
	      SHOW_MENUITEM("remove_channel_mi");
	      SHOW_MENUITEM("separator1");
	    }

	  SHOW_MENUITEM("separator3");
	  SHOW_MENUITEM("properties_mi");
	}
      else
	{
	  SHOW_MENUITEM("separator2");
	  SHOW_MENUITEM("exit_mi");
	}

      gtk_menu_popup (GTK_MENU(channel_popup), NULL, NULL, NULL, 
		      NULL, 3, event->time);

      rv = TRUE;
    }

  return rv;
}



POPUPFUNC (on_channels_join_channel)
{
  g_return_if_fail (selected_channel);
  ggui_channel_join (selected_channel->jmchannel->url);
}

POPUPFUNC (on_channels_open_channel)
{
  ggui_channel_join (NULL);
}

POPUPFUNC (on_channels_create_root_channel)
{
  ggui_channel_create (NULL, NULL, NULL);
}

POPUPFUNC (on_channels_remove_channel)
{
  GURL* url;

  g_return_if_fail (selected_channel);
  url = selected_channel->jmchannel->url;

  /*

    TODO:

    We do not elegantly handle the case where I'm leaving a channel
    where I'm offering stuff.  We might want to ask the user if
    they're sure they want to leave the channel.

  */


  if (selected_channel->jmchannel->is_local)
    {
      ggui_channel_delete (url);
    }
  else if (jmchannel_is_up(selected_channel->jmchannel))
    {
      ggui_channel_leave (url);
      ggui_channel_delete (url);
    }
  else
    {
      ggui_channel_delete (url);
    }
}

POPUPFUNC (on_channels_leave_channel)
{
  g_return_if_fail (selected_channel);
  ggui_channel_leave (selected_channel->jmchannel->url);
}

POPUPFUNC (on_channels_properties)
{
  g_return_if_fail (selected_channel);
  ggui_properties_show (selected_channel->jmchannel->url);
}


syntax highlighted by Code2HTML, v. 0.9.1