summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/common/hexchat.h2
-rw-r--r--src/common/inbound.c2
-rw-r--r--src/common/server.c4
-rw-r--r--src/common/servlist.c266
-rw-r--r--src/common/servlist.h20
-rw-r--r--src/fe-gtk/menu.c4
-rw-r--r--src/fe-gtk/servlistgui.c536
7 files changed, 322 insertions, 512 deletions
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index 0a449bfa..060fd082 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -566,7 +566,7 @@ typedef struct server
 	time_t away_time;					/* when we were marked away */
 
 	char *encoding;					/* NULL for system */
-	char *autojoin;			/* list of channels & keys to join */
+	GSList *favlist;			/* list of channels & keys to join */
 
 	unsigned int motd_skipped:1;
 	unsigned int connected:1;
diff --git a/src/common/inbound.c b/src/common/inbound.c
index 4f23403f..926a34ba 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -1069,6 +1069,7 @@ inbound_nameslist_end (server *serv, char *chan)
 static gboolean
 check_autojoin_channels (server *serv)
 {
+#if 0//FIXME
 	char *po;
 	session *sess;
 	GSList *list = sess_list;
@@ -1137,6 +1138,7 @@ check_autojoin_channels (server *serv)
 
 	serv->joindelay_tag = 0;
 	fe_server_event (serv, FE_SE_LOGGEDIN, i);
+#endif
 	return FALSE;
 }
 
diff --git a/src/common/server.c b/src/common/server.c
index 26d9a7cb..7d48163f 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -2030,8 +2030,8 @@ server_free (server *serv)
 		free (serv->last_away_reason);
 	if (serv->encoding)
 		free (serv->encoding);
-	if (serv->autojoin)
-		free (serv->autojoin);
+	if (serv->favlist)
+		g_slist_free_full (serv->favlist, g_free);
 
 	fe_server_callback (serv);
 
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 79591331..7c1789a9 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -610,11 +610,13 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
 	{
 		sess->willjoinchannel[0] = 0;
 
-		if (net->autojoin)
+		if (net->favchanlist)
 		{
-			if (serv->autojoin)
-				free (serv->autojoin);
-			serv->autojoin = strdup (net->autojoin);
+			if (serv->favlist)
+			{
+				g_slist_free_full (serv->favlist, g_free);
+			}
+			serv->favlist = g_slist_copy_deep (net->favchanlist, (GCopyFunc) g_strdup, NULL);
 		}
 	}
 
@@ -804,7 +806,9 @@ servlist_server_find (ircnet *net, char *name, int *pos)
 		if (strcmp (serv->hostname, name) == 0)
 		{
 			if (pos)
+			{
 				*pos = i;
+			}
 			return serv;
 		}
 		i++;
@@ -814,6 +818,31 @@ servlist_server_find (ircnet *net, char *name, int *pos)
 	return NULL;
 }
 
+favchannel *
+servlist_favchan_find (ircnet *net, char *channel, int *pos)
+{
+	GSList *list = net->favchanlist;
+	favchannel *favchan;
+	int i = 0;
+
+	while (list)
+	{
+		favchan = list->data;
+		if (strcmp (favchan->name, channel) == 0)
+		{
+			if (pos)
+			{
+				*pos = i;
+			}
+			return favchan;
+		}
+		i++;
+		list = list->next;
+	}
+
+	return NULL;
+}
+
 commandentry *
 servlist_command_find (ircnet *net, char *cmd, int *pos)
 {
@@ -827,7 +856,9 @@ servlist_command_find (ircnet *net, char *cmd, int *pos)
 		if (strcmp (entry->command, cmd) == 0)
 		{
 			if (pos)
+			{
 				*pos = i;
+			}
 			return entry;
 		}
 		i++;
@@ -918,6 +949,32 @@ servlist_command_add (ircnet *net, char *cmd)
 	return entry;
 }
 
+favchannel *
+servlist_favchan_add (ircnet *net, char *channel)
+{
+	int pos;
+	favchannel *chan;
+
+	chan = malloc (sizeof (favchannel));
+	memset (chan, 0, sizeof (favchannel));
+
+	if (strchr (channel, ',') != NULL)
+	{
+		pos = strchr (channel, ',') - channel;
+		chan->name = g_strndup (channel, pos);
+		chan->key = g_strdup (channel + pos + 1);
+	}
+	else
+	{
+		chan->name = g_strdup (channel);
+		chan->key = NULL;
+	}
+
+	net->favchanlist = g_slist_append (net->favchanlist, chan);
+
+	return chan;
+}
+
 void
 servlist_server_remove (ircnet *net, ircserver *serv)
 {
@@ -946,6 +1003,15 @@ servlist_command_remove (ircnet *net, commandentry *entry)
 	net->commandlist = g_slist_remove (net->commandlist, entry);
 }
 
+void
+servlist_favchan_remove (ircnet *net, favchannel *channel)
+{
+	g_free (channel->name);
+	g_free (channel->key);
+	g_free (channel);
+	net->favchanlist = g_slist_remove (net->favchanlist, channel);
+}
+
 static void
 free_and_clear (char *str)
 {
@@ -991,10 +1057,10 @@ servlist_net_remove (ircnet *net)
 	if (net->real)
 		free (net->real);
 	free_and_clear (net->pass);
-	if (net->autojoin)
-		free (net->autojoin);
+	if (net->favchanlist)
+		g_slist_free_full (net->favchanlist, g_free);
 	if (net->commandlist)
-		g_slist_free_full (net->commandlist, (GDestroyNotify) g_free);
+		g_slist_free_full (net->commandlist, g_free);
 	if (net->comment)
 		free (net->comment);
 	if (net->encoding)
@@ -1049,7 +1115,7 @@ servlist_load_defaults (void)
 			net->encoding = strdup (IRC_DEFAULT_CHARSET);
 			if (def[i].channel)
 			{
-				net->autojoin = strdup (def[i].channel);
+				servlist_favchan_add (net, def[i].channel);
 			}
 			if (def[i].charset)
 			{
@@ -1127,7 +1193,7 @@ servlist_load (void)
 				net->pass = strdup (buf + 2);
 				break;
 			case 'J':
-				net->autojoin = strdup (buf + 2);
+				servlist_favchan_add (net, buf + 2);
 				break;
 			case 'C':
 				servlist_command_add (net, buf + 2);
@@ -1205,9 +1271,11 @@ servlist_save (void)
 	ircnet *net;
 	ircserver *serv;
 	commandentry *cmd;
+	favchannel *favchan;
 	GSList *list;
 	GSList *netlist;
 	GSList *cmdlist;
+	GSList *favlist;
 #ifndef WIN32
 	int first = FALSE;
 
@@ -1249,8 +1317,6 @@ servlist_save (void)
 			fprintf (fp, "R=%s\n", net->real);
 		if (net->pass)
 			fprintf (fp, "P=%s\n", net->pass);
-		if (net->autojoin)
-			fprintf (fp, "J=%s\n", net->autojoin);
 		if (net->logintype)
 			fprintf (fp, "L=%d\n", net->logintype);
 		if (net->encoding && g_ascii_strcasecmp (net->encoding, "System") &&
@@ -1284,6 +1350,23 @@ servlist_save (void)
 			cmdlist = cmdlist->next;
 		}
 
+		favlist = net->favchanlist;
+		while (favlist)
+		{
+			favchan = favlist->data;
+
+			if (favchan->key)
+			{
+				fprintf (fp, "J=%s,%s\n", favchan->name, favchan->key);
+			}
+			else
+			{
+				fprintf (fp, "J=%s\n", favchan->name);
+			}
+
+			favlist = favlist->next;
+		}
+
 		if (fprintf (fp, "\n") < 1)
 		{
 			fclose (fp);
@@ -1297,162 +1380,33 @@ servlist_save (void)
 	return TRUE;
 }
 
-static void
-joinlist_free1 (GSList *list)
-{
-	GSList *head = list;
-
-	for (; list; list = list->next)
-		g_free (list->data);
-	g_slist_free (head);
-}
-
-void
-joinlist_free (GSList *channels, GSList *keys)
-{
-	joinlist_free1 (channels);
-	joinlist_free1 (keys);
-}
-
-gboolean
-joinlist_is_in_list (server *serv, char *channel)
-{
-	GSList *channels, *keys;
-	GSList *list;
-
-	if (!serv->network || !((ircnet *)serv->network)->autojoin)
-		return FALSE;
-
-	joinlist_split (((ircnet *)serv->network)->autojoin, &channels, &keys);
-
-	for (list = channels; list; list = list->next)
-	{
-		if (serv->p_cmp (list->data, channel) == 0)
-			return TRUE;
-	}
-
-	joinlist_free (channels, keys);
-
-	return FALSE;
-}
-
-gchar *
-joinlist_merge (GSList *channels, GSList *keys)
+static int
+joinlist_find_chan (favchannel *curr_item, const char *channel)
 {
-	GString *out = g_string_new (NULL);
-	GSList *list;
-	int i, j;
-
-	for (; channels; channels = channels->next)
+	if (!g_ascii_strcasecmp (curr_item->name, channel))
 	{
-		g_string_append (out, channels->data);
-
-		if (channels->next)
-			g_string_append_c (out, ',');
+		return 0;
 	}
-
-	/* count number of REAL keys */
-	for (i = 0, list = keys; list; list = list->next)
-		if (list->data)
-			i++;
-
-	if (i > 0)
+	else
 	{
-		g_string_append_c (out, ' ');
-
-		for (j = 0; keys; keys = keys->next)
-		{
-			if (keys->data)
-			{
-				g_string_append (out, keys->data);
-				j++;
-				if (j == i)
-					break;
-			}
-
-			if (keys->next)
-				g_string_append_c (out, ',');
-		}
+		return 1;
 	}
-
-	return g_string_free (out, FALSE);
 }
 
-void
-joinlist_split (char *autojoin, GSList **channels, GSList **keys)
+gboolean
+joinlist_is_in_list (server *serv, char *channel)
 {
-	char *parta, *partb;
-	char *chan, *key;
-	int len;
-
-	*channels = NULL;
-	*keys = NULL;
-
-	/* after the first space, the keys begin */
-	parta = autojoin;
-	partb = strchr (autojoin, ' ');
-	if (partb)
-		partb++;
-
-	while (1)
+	if (!serv->network || !((ircnet *)serv->network)->favchanlist)
 	{
-		chan = parta;
-		key = partb;
-
-		if (1)
-		{
-			while (parta[0] != 0 && parta[0] != ',' && parta[0] != ' ')
-			{
-				parta++;
-			}
-		}
-
-		if (partb)
-		{
-			while (partb[0] != 0 && partb[0] != ',' && partb[0] != ' ')
-			{
-				partb++;
-			}
-		}
-
-		len = parta - chan;
-		if (len < 1)
-			break;
-		*channels = g_slist_append (*channels, g_strndup (chan, len));
-
-		len = partb - key;
-		*keys = g_slist_append (*keys, len ? g_strndup (key, len) : NULL);
-
-		if (parta[0] == ' ' || parta[0] == 0)
-			break;
-		parta++;
-
-		if (partb)
-		{
-			if (partb[0] == 0 || partb[0] == ' ')
-				partb = NULL;	/* no more keys, but maybe more channels? */
-			else
-				partb++;
-		}
+		return FALSE;
 	}
 
-#if 0
-	GSList *lista, *listb;
-	int i;
-
-	printf("-----\n");
-	i = 0;
-	lista = *channels;
-	listb = *keys;
-	while (lista)
+	if (g_slist_find_custom (((ircnet *)serv->network)->favchanlist, channel, (GCompareFunc) joinlist_find_chan))
 	{
-		printf("%d. |%s| |%s|\n", i, lista->data, listb->data);
-		i++;
-		lista = lista->next;
-		listb = listb->next;
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
 	}
-	printf("-----\n\n");
-#endif
 }
-
-
diff --git a/src/common/servlist.h b/src/common/servlist.h
index 141df127..350a1558 100644
--- a/src/common/servlist.h
+++ b/src/common/servlist.h
@@ -30,6 +30,12 @@ typedef struct commandentry
 	char *command;
 } commandentry;
 
+typedef struct favchannel
+{
+	char *name;
+	char *key;
+} favchannel;
+
 typedef struct ircnet
 {
 	char *name;
@@ -38,12 +44,12 @@ typedef struct ircnet
 	char *user;
 	char *real;
 	char *pass;
-	char *autojoin;
 	int logintype;
 	char *comment;
 	char *encoding;
 	GSList *servlist;
 	GSList *commandlist;
+	GSList *favchanlist;
 	int selected;
 	guint32 flags;
 } ircnet;
@@ -79,14 +85,20 @@ ircnet *servlist_net_find_from_server (char *server_name);
 
 ircserver *servlist_server_find (ircnet *net, char *name, int *pos);
 commandentry *servlist_command_find (ircnet *net, char *cmd, int *pos);
+favchannel *servlist_favchan_find (ircnet *net, char *channel, int *pos);
+
 ircserver *servlist_server_add (ircnet *net, char *name);
 commandentry *servlist_command_add (ircnet *net, char *command);
+favchannel *servlist_favchan_add (ircnet *net, char *channel);
+
 void servlist_server_remove (ircnet *net, ircserver *serv);
 void servlist_command_remove (ircnet *net, commandentry *entry);
+void servlist_favchan_remove (ircnet *net, favchannel *channel);
 
-void joinlist_split (char *autojoin, GSList **channels, GSList **keys);
 gboolean joinlist_is_in_list (server *serv, char *channel);
-void joinlist_free (GSList *channels, GSList *keys);
-gchar *joinlist_merge (GSList *channels, GSList *keys);
 
+/* FIXME
+void joinlist_split (char *autojoin, GSList **channels, GSList **keys);
+void joinlist_free (GSList *channels, GSList *keys);
+*/
 #endif
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c
index 79e1bad8..014ef3e7 100644
--- a/src/fe-gtk/menu.c
+++ b/src/fe-gtk/menu.c
@@ -1048,9 +1048,13 @@ menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel)
 	}
 
 	if (joinlist_is_in_list (serv, channel))
+	{
 		mg_create_icon_item (_("_Remove from Favorites"), GTK_STOCK_REMOVE, menu, menu_delfav_cb, serv);
+	}
 	else
+	{
 		mg_create_icon_item (_("_Add to Favorites"), GTK_STOCK_ADD, menu, menu_addfav_cb, serv);
+	}
 }
 
 static void
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index 439613a0..59012e42 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -181,75 +181,33 @@ servlist_select_and_show (GtkTreeView *treeview, GtkTreeIter *iter,
 	}
 }
 
-static void

-servlist_channel_save (void)

-{

-	GtkTreeModel *model;

-	GtkTreeIter iter;

-	char *channel, *key;

-	char *autojoin;

-	GSList *channels = NULL, *keys = NULL;

-

-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));

-

-	if (gtk_tree_model_get_iter_first (model, &iter))

-	{

-		do

-		{

-			gtk_tree_model_get (model, &iter, 0, &channel, 1, &key, -1);

-			channels = g_slist_append (channels, channel);

-			if (key && key[0] == 0)

-			{

-				/* NULL out empty keys */

-				g_free (key);

-				keys = g_slist_append (keys, NULL);				

-			}

-			else

-				keys = g_slist_append (keys, key);

-		}

-		while (gtk_tree_model_iter_next (model, &iter));

-	}

-

-	autojoin = joinlist_merge (channels, keys);

-	if (autojoin)

-	{

-			if (selected_net->autojoin)

-					g_free(selected_net->autojoin);

-			strcpy (selected_net->autojoin, autojoin);

-			g_free (autojoin);

-	}

-

-	/* this does g_free too */

-	joinlist_free (channels, keys);

-}
-
 static void
 servlist_channels_populate (ircnet *net, GtkWidget *treeview)
 {
 	GtkListStore *store;
-	GSList *channels, *keys;
-	GSList *clist, *klist;
 	GtkTreeIter iter;
+	int i;
+	favchannel *favchan;
+	GSList *list = net->favchanlist;
 
-	if (net->autojoin)
-	{
-		joinlist_split (net->autojoin, &channels, &keys);
-
-		clist = channels;
-		klist = keys;
+	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+	gtk_list_store_clear (store);
 
-		store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
+	i = 0;
+	while (list)
+	{
+		favchan = list->data;
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, 0, favchan->name, 1, favchan->key, 2, TRUE, -1);
 
-		while (clist)
+		if (net->selected == i)
 		{
-			gtk_list_store_append (store, &iter);
-			gtk_list_store_set (store, &iter, 0, clist->data, 1, klist->data, 2, TRUE, -1);
-
-			klist = klist->next;
-			clist = clist->next;
+			/* select this server */
+			servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
 		}
 
-		joinlist_free (channels, keys);
+		i++;
+		list = list->next;
 	}
 }
 
@@ -273,8 +231,10 @@ servlist_servers_populate (ircnet *net, GtkWidget *treeview)
 		gtk_list_store_set (store, &iter, 0, serv->hostname, 1, 1, -1);
 
 		if (net->selected == i)
+		{
 			/* select this server */
 			servlist_select_and_show (GTK_TREE_VIEW (treeview), &iter, store);
+		}
 
 		i++;
 		list = list->next;
@@ -414,7 +374,7 @@ servlist_addserver (void)
 	servlist_server_add (selected_net, "newserver/6667");
 
 	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, "newserver/6667", 1, 1, -1);
+	gtk_list_store_set (store, &iter, 0, "newserver/6667", 1, TRUE, -1);
 
 	/* select this server */
 	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[SERVER_TREE]), &iter, store);
@@ -436,7 +396,7 @@ servlist_addcommand (void)
 	servlist_command_add (selected_net, "ECHO hello");
 
 	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, "ECHO hello", 1, 1, -1);
+	gtk_list_store_set (store, &iter, 0, "ECHO hello", 1, TRUE, -1);
 
 	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[CMD_TREE]), &iter, store);
 	servlist_start_editing (GTK_TREE_VIEW (edit_trees[CMD_TREE]));
@@ -445,6 +405,28 @@ servlist_addcommand (void)
 }
 
 static void
+servlist_addchannel (void)
+{
+	GtkTreeIter iter;
+	GtkListStore *store;
+
+	if (!selected_net)
+		return;
+
+	store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE])));
+	servlist_favchan_add (selected_net, "#channel");
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter, 0, "#channel", 1, "", 2, TRUE, -1);
+
+	/* select this server */
+	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]), &iter, store);
+	servlist_start_editing (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+
+	servlist_server_row_cb (gtk_tree_view_get_selection (GTK_TREE_VIEW (networks_tree)), NULL);
+}
+
+static void
 servlist_addnet_cb (GtkWidget *item, GtkTreeView *treeview)
 {
 	GtkTreeIter iter;
@@ -702,6 +684,7 @@ servlist_edit_cb (GtkWidget *but, gpointer none)
 	edit_win = servlist_open_edit (serverlist_win, selected_net);
 	gtkutil_set_icon (edit_win);
 	servlist_servers_populate (selected_net, edit_trees[SERVER_TREE]);
+	servlist_channels_populate (selected_net, edit_trees[CHANNEL_TREE]);
 	servlist_commands_populate (selected_net, edit_trees[CMD_TREE]);
 	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[SERVER_TREE]))),
 							"changed", G_CALLBACK (servlist_server_row_cb), NULL);
@@ -836,6 +819,53 @@ servlist_deletecommand_cb (void)
 	}
 }
 
+static void
+servlist_deletechannel (favchannel *favchan, GtkTreeModel *model)
+{
+	GtkTreeSelection *sel;
+	GtkTreeIter iter;
+
+	/* remove from GUI */
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+	}
+
+	/* remove from list */
+	if (selected_net)
+	{
+		servlist_favchan_remove (selected_net, favchan);
+	}
+}
+
+static void
+servlist_deletechannel_cb (void)
+{
+	GtkTreeSelection *sel;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *name;
+	char *key;
+	favchannel *favchan;
+	int pos;
+
+	/* find the selected item in the GUI */
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
+
+	if (gtk_tree_selection_get_selected (sel, &model, &iter))
+	{
+		gtk_tree_model_get (model, &iter, 0, &name, 1, &key, -1);			/* query the content of the selection */
+		favchan = servlist_favchan_find (selected_net, name, &pos);
+		g_free (name);
+		if (favchan)
+		{
+			servlist_deletechannel (favchan, model);
+		}
+	}
+}
+
 static ircnet *
 servlist_find_selected_net (GtkTreeSelection *sel)
 {
@@ -889,7 +919,6 @@ servlist_savegui (void)
 	if (sp)
 		sp[0] = 0;	/* spaces will break the login */
 	/* strcpy (prefs.hex_irc_real_name, GTK_ENTRY (entry_greal)->text); */
-	//FIXME: Save commands(and load them) and favorites
 	servlist_save ();
 	save_config (); /* For nicks stored in hexchat.conf */
 
@@ -912,69 +941,6 @@ servlist_get_iter_from_name (GtkTreeModel *model, gchar *name, GtkTreeIter *iter
 }
 
 static void
-servlist_editchannel_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, GtkTreeModel *model)
-{
-	GtkTreeIter iter;
-	static int loop_guard = FALSE;
-
-	if (loop_guard)
-		return;
-
-	if (!servlist_get_iter_from_name (model, name, &iter))
-		return;
-
-	loop_guard = TRUE;
-	/* delete empty item */
-	if (newval[0] == 0)
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-	else
-		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, newval, -1);
-	loop_guard = FALSE;
-}
-
-static void
-servlist_editkey_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, GtkTreeModel *model)
-{
-	GtkTreeIter iter;
-
-	if (!servlist_get_iter_from_name (model, name, &iter))
-		return;
-
-	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, newval, -1);
-}
-
-static void
-servlist_addchannel (char *channel)
-{
-	GtkTreeIter iter;
-	GtkListStore *store;
-
-	store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE])));
-
-	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, channel, 1, "", 2, TRUE, -1);
-
-	/* select this server */
-	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]), &iter, store);
-	servlist_start_editing (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
-}
-
-static void
-servlist_deletechannel_cb (void)
-{
-	GtkTreeSelection *sel;
-	GtkTreeModel *model;
-	GtkTreeIter iter;
-
-	/* find the selected item in the GUI */
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
-	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (edit_trees[CHANNEL_TREE]));
-
-	if (gtk_tree_selection_get_selected (sel, &model, &iter))
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-}
-
-static void
 servlist_addbutton_cb (GtkWidget *item, GtkNotebook *notebook)
 {
 		switch (gtk_notebook_get_current_page (notebook))
@@ -983,7 +949,7 @@ servlist_addbutton_cb (GtkWidget *item, GtkNotebook *notebook)
 						servlist_addserver ();
 						break;
 				case CHANNEL_TREE:
-						servlist_addchannel ("#channel");
+						servlist_addchannel ();
 						break;
 				case CMD_TREE:
 						servlist_addcommand ();
@@ -1015,184 +981,25 @@ servlist_deletebutton_cb (GtkWidget *item, GtkNotebook *notebook)
 void
 servlist_autojoinedit (ircnet *net, char *channel, gboolean add)
 {
-#if 0 // FIXME: adding favorites from menu
-	GtkWidget *win;
-	GtkWidget *scrolledwindow;
-	GtkTreeModel *model;
-	GtkListStore *store;
-	GtkCellRenderer *renderer;
-	GtkWidget *tree;
-	GtkWidget *table;
-	GtkWidget *label;
-	GtkWidget *label2;
-	GtkWidget *bbox;
-	GtkWidget *wid;
-
-	GtkWidget *vbuttonbox1;
-	GtkWidget *buttonadd;
-	GtkWidget *buttonremove;
-	GtkWidget *buttonedit;
-
-	char buf[128];
-	char lab[128];
-	GSList *channels, *keys;
-	GSList *clist, *klist;
-	GtkTreeIter iter;
-
-	if (edit_win && selected_net)
-		/* update net->autojoin */
-		servlist_edit_update (selected_net);
-
-	win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-	gtk_container_set_border_width (GTK_CONTAINER (win), 4);
-	gtk_window_set_title (GTK_WINDOW (win), _(DISPLAY_NAME": Favorite Channels (Auto-Join List)"));
-	gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_MOUSE);
-	if (edit_win)
-		gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (edit_win));
-	gtk_window_set_modal (GTK_WINDOW (win), TRUE);
-	gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_window_set_role (GTK_WINDOW (win), "editserv");
-
-	table = gtk_table_new (1, 1, FALSE);
-	gtk_container_add (GTK_CONTAINER (win), table);
-	gtk_widget_show (table);
-
-	snprintf (buf, sizeof (buf), _("These channels will be joined whenever you connect to %s."), net->name);
-	label = gtk_label_new (buf);
-	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 2, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL, 3, 3);
-	gtk_widget_show (label);
+	favchannel *fav;
+	char *buf;
 
-	label2 = gtk_label_new ("");
-	gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_CENTER);
-	gtk_table_attach (GTK_TABLE (table), label2, 0, 2, 1, 2, GTK_FILL, 0, 3, 3);
-	gtk_widget_show (label2);
-
-	scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
-	gtk_table_attach (GTK_TABLE (table), scrolledwindow, 0, 1, 2, 3, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
-											  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
-													 GTK_SHADOW_IN);
-	gtk_widget_show (scrolledwindow);
-
-	store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
-	model = GTK_TREE_MODEL (store);
-
-	tree = gtk_tree_view_new_with_model (model);
-	g_object_unref (model);
-	gtk_container_add (GTK_CONTAINER (scrolledwindow), tree);
-	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), TRUE);
-	gtk_widget_show (tree);
-
-	renderer = gtk_cell_renderer_text_new ();
-	g_signal_connect (G_OBJECT (renderer), "edited",
-							G_CALLBACK (servlist_editchannel_cb), model);
-	gtk_tree_view_insert_column_with_attributes (
-								GTK_TREE_VIEW (tree), -1,
-						 		_("Channel"), renderer,
-						 		"text", 0,
-								"editable", 2,
-								NULL);
-
-	renderer = gtk_cell_renderer_text_new ();
-	g_signal_connect (G_OBJECT (renderer), "edited",
-							G_CALLBACK (servlist_editkey_cb), model);
-	gtk_tree_view_insert_column_with_attributes (
-								GTK_TREE_VIEW (tree), -1,
-						 		_("Key (Password)"), renderer,
-						 		"text", 1,
-								"editable", 2,
-								NULL);
-
-	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (tree), 0), TRUE);
-	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (tree), 1), TRUE);
-
-	gtk_tree_sortable_set_sort_column_id ((GtkTreeSortable *)model, 0, GTK_SORT_ASCENDING);
-
-	/* ===BUTTONS=== */
-	vbuttonbox1 = gtk_vbutton_box_new ();
-	gtk_box_set_spacing (GTK_BOX (vbuttonbox1), 3);
-	gtk_button_box_set_layout (GTK_BUTTON_BOX (vbuttonbox1), GTK_BUTTONBOX_START);
-	gtk_widget_show (vbuttonbox1);
-	gtk_table_attach (GTK_TABLE (table), vbuttonbox1, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 3, 0);
-
-	buttonadd = gtk_button_new_from_stock ("gtk-add");
-	g_signal_connect (G_OBJECT (buttonadd), "clicked",
-							G_CALLBACK (servlist_addchannel_cb), tree);
-	gtk_widget_show (buttonadd);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonadd);
-	GTK_WIDGET_SET_FLAGS (buttonadd, GTK_CAN_DEFAULT);
-
-	buttonremove = gtk_button_new_from_stock ("gtk-remove");
-	g_signal_connect (G_OBJECT (buttonremove), "clicked",
-							G_CALLBACK (servlist_deletechannel_cb), tree);
-	gtk_widget_show (buttonremove);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonremove);
-	GTK_WIDGET_SET_FLAGS (buttonremove, GTK_CAN_DEFAULT);
-
-	buttonedit = gtk_button_new_with_mnemonic (_("_Edit"));
-	g_signal_connect (G_OBJECT (buttonedit), "clicked",
-							G_CALLBACK (servlist_editchannelbutton_cb), tree);
-	gtk_widget_show (buttonedit);
-	gtk_container_add (GTK_CONTAINER (vbuttonbox1), buttonedit);
-	GTK_WIDGET_SET_FLAGS (buttonedit, GTK_CAN_DEFAULT);
-
-	bbox = gtk_hbutton_box_new ();
-	gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
-	gtk_box_set_spacing (GTK_BOX (bbox), 4);
-	gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 3, 4, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 4);
-	gtk_widget_show (bbox);
-
-	wid = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
-	g_signal_connect (G_OBJECT (wid), "clicked", G_CALLBACK (gtkutil_destroy), win);
-	gtk_container_add (GTK_CONTAINER (bbox), wid);
-	gtk_widget_show (wid);
-
-	wid = gtk_button_new_from_stock (GTK_STOCK_OK);
-	g_signal_connect (G_OBJECT (wid), "clicked", G_CALLBACK (servlist_autojoineditok_cb), tree);
-	gtk_container_add (GTK_CONTAINER (bbox), wid);
-	gtk_widget_show (wid);
-	gtk_widget_grab_focus (wid);
-	/* =========== */
-	if (net->autojoin)
+	if (add)
 	{
-		joinlist_split (net->autojoin, &channels, &keys);
-
-		clist = channels;
-		klist = keys;
-
-		while (clist)
-		{
-			if (channel && !add && !rfc_casecmp (channel, clist->data))
-			{
-				snprintf (buf, sizeof (buf), _("%s has been removed."), channel);
-				snprintf (lab, sizeof (lab), "<span foreground=\"#2222DD\">%s</span>", buf);
-				gtk_label_set_markup (GTK_LABEL (label2), lab);
-			}
-			else
-			{
-				gtk_list_store_append (store, &iter);
-				gtk_list_store_set (store, &iter, 0, clist->data, 1, klist->data, 2, TRUE, -1);
-			}
-
-			klist = klist->next;
-			clist = clist->next;
-		}
-
-		joinlist_free (channels, keys);
+		servlist_favchan_add (net, channel);
+		servlist_save ();
+		buf = g_strdup_printf (_("Channel %s added to favorites."), channel);
 	}
-
-	if (channel && add)
+	else
 	{
-		servlist_addchannel (channel);
-		snprintf (buf, sizeof (buf), _("%s has been added."), channel);
-		snprintf (lab, sizeof (lab), "<span foreground=\"#2222DD\">%s</span>", buf);
-		gtk_label_set_markup (GTK_LABEL (label2), lab);
+		fav = servlist_favchan_find (net, channel, NULL);
+		servlist_favchan_remove (net, fav);
+		servlist_save ();
+		buf = g_strdup_printf (_("Channel %s removed from favorites."), channel);
 	}
 
-	fav_add_net = net;
-#endif
+	fe_message (buf, FE_MSG_INFO);
+	g_free (buf);
 }
 
 static void
@@ -1212,12 +1019,6 @@ servlist_toggle_global_user (gboolean sensitive)
 }
 
 static void
-servlist_autojoinedit_cb (GtkWidget *button, ircnet *net)
-{
-	servlist_autojoinedit (net, NULL, FALSE);
-}
-
-static void
 servlist_connect_cb (GtkWidget *button, gpointer userdata)
 {
 	if (!selected_net)
@@ -1376,29 +1177,7 @@ servlist_create_entry (GtkWidget *table, char *labeltext, int row,
 	gtk_entry_set_text (GTK_ENTRY (entry), def ? def : "");
 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
 
-#if 0	/* FIXME */
-	if (row == 12)	/* for "Favorite channels:" */
-	{
-		GtkWidget *button, *box;
-
-		box = gtk_hbox_new (0, 0);
-		button = gtk_button_new_with_label ("...");
-		g_signal_connect (G_OBJECT (button), "clicked",
-								G_CALLBACK (servlist_autojoinedit_cb), selected_net);
-
-		gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 0);
-		gtk_box_pack_end (GTK_BOX (box), button, 0, 0, 0);
-		gtk_widget_show_all (box);
-
-		gtk_table_attach (GTK_TABLE (table), box, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
-	}
-	else
-	{
-#endif
-		gtk_table_attach (GTK_TABLE (table), entry, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
-#if 0
-	}
-#endif
+	gtk_table_attach (GTK_TABLE (table), entry, 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, 0, SERVLIST_X_PADDING, SERVLIST_Y_PADDING);
 
 	return entry;
 }
@@ -1462,23 +1241,20 @@ servlist_sanitize_command (char *cmd)
 }
 
 static void
-servlist_editserver_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2,
-								gpointer user_data)
+servlist_editserver_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
 {
 	GtkTreeModel *model = (GtkTreeModel *)user_data;
 	GtkTreeIter iter;
-	GtkTreePath *path;
 	char *servname;
 	ircserver *serv;
 
 	if (!selected_net)
+	{
 		return;
+	}
 
-	path = gtk_tree_path_new_from_string (arg1);
-
-	if (!gtk_tree_model_get_iter (model, &iter, path))
+	if (!servlist_get_iter_from_name (model, name, &iter))
 	{
-		gtk_tree_path_free (path);
 		return;
 	}
 
@@ -1489,39 +1265,34 @@ servlist_editserver_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2,
 	if (serv)
 	{
 		/* delete empty item */
-		if (arg2[0] == 0)
+		if (newval[0] == 0)
 		{
 			servlist_deleteserver (serv, model);
-			gtk_tree_path_free (path);
 			return;
 		}
 
 		servname = serv->hostname;
-		serv->hostname = servlist_sanitize_hostname (arg2);
+		serv->hostname = servlist_sanitize_hostname (newval);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, serv->hostname, -1);
 		free (servname);
 	}
-
-	gtk_tree_path_free (path);
 }
 
 static void
-servlist_editcommand_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2, gpointer user_data)
+servlist_editcommand_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
 {
 	GtkTreeModel *model = (GtkTreeModel *)user_data;
 	GtkTreeIter iter;
-	GtkTreePath *path;
 	char *cmd;
 	commandentry *entry;
 
 	if (!selected_net)
+	{
 		return;
+	}
 
-	path = gtk_tree_path_new_from_string (arg1);
-
-	if (!gtk_tree_model_get_iter (model, &iter, path))
+	if (!servlist_get_iter_from_name (model, name, &iter))
 	{
-		gtk_tree_path_free (path);
 		return;
 	}
 
@@ -1532,20 +1303,88 @@ servlist_editcommand_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2, gp
 	if (entry)
 	{
 		/* delete empty item */
-		if (arg2[0] == 0)
+		if (newval[0] == 0)
 		{
 			servlist_deletecommand (entry, model);
-			gtk_tree_path_free (path);
 			return;
 		}
 
 		cmd = entry->command;
-		entry->command = servlist_sanitize_command (arg2);
+		entry->command = servlist_sanitize_command (newval);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, entry->command, -1);
 		free (cmd);
 	}
+}
 
-	gtk_tree_path_free (path);
+static void
+servlist_editchannel_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
+{
+	GtkTreeModel *model = (GtkTreeModel *)user_data;
+	GtkTreeIter iter;
+	char *chan;
+	char *key;
+	favchannel *favchan;
+
+	if (!selected_net)
+	{
+		return;
+	}
+
+	if (!servlist_get_iter_from_name (model, name, &iter))
+	{
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter, 0, &chan, 1, &key, -1);
+	favchan = servlist_favchan_find (selected_net, chan, NULL);
+	g_free (chan);
+
+	if (favchan)
+	{
+		/* delete empty item */
+		if (newval[0] == 0)
+		{
+			servlist_deletechannel (favchan, model);
+			return;
+		}
+
+		chan = favchan->name;
+		favchan->name = g_strdup (newval);
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, favchan->name, -1);
+		g_free (chan);
+	}
+}
+
+static void
+servlist_editkey_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, gpointer user_data)
+{
+	GtkTreeModel *model = (GtkTreeModel *)user_data;
+	GtkTreeIter iter;
+	char *chan;
+	char *key;
+	favchannel *favchan;
+
+	if (!selected_net)
+	{
+		return;
+	}
+
+	if (!servlist_get_iter_from_name (model, name, &iter))
+	{
+		return;
+	}
+
+	gtk_tree_model_get (model, &iter, 0, &chan, 1, &key, -1);
+	favchan = servlist_favchan_find (selected_net, chan, NULL);
+	g_free (chan);
+
+	if (favchan)
+	{
+		key = favchan->key;
+		favchan->key = g_strdup (newval);
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, favchan->key, -1);
+		g_free (key);
+	}
 }
 
 static void
@@ -1789,7 +1628,6 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview_channels), 0), TRUE);
 	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview_channels), 1), TRUE);
 	gtk_tree_sortable_set_sort_column_id ((GtkTreeSortable *)model, 0, GTK_SORT_ASCENDING);
-	servlist_channels_populate (selected_net, treeview_channels);
 
 
 	/* Command Tree */