Mercurial > pidgin
changeset 8113:d60272410bd5
[gaim-migrate @ 8817]
Tim 'marv' Ringenbach wrote us a wonderful core/ui split chat room list
thing-a-ma-jig. I've taken the liberty of adding jabber support to it
as well.
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Thu, 15 Jan 2004 22:20:29 +0000 |
parents | 67387ec77301 |
children | 7a6e30eb7aad |
files | ChangeLog src/Makefile.am src/gtkblist.c src/gtkroomlist.c src/gtkroomlist.h src/main.c src/protocols/jabber/chat.c src/protocols/jabber/chat.h src/protocols/jabber/jabber.c src/protocols/jabber/jabber.h src/protocols/yahoo/yahoo.c src/protocols/yahoo/yahoo.h src/protocols/yahoo/yahoochat.c src/protocols/yahoo/yahoochat.h src/prpl.h src/roomlist.c src/roomlist.h |
diffstat | 17 files changed, 1855 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Jan 15 17:08:38 2004 +0000 +++ b/ChangeLog Thu Jan 15 22:20:29 2004 +0000 @@ -3,6 +3,7 @@ version 0.76cvs: * WYSIWYG text input (with scrollbars, too!) * Improved accessibility in conversation windows (Nathan Fredrickson) + * Chatroom List support (thanks, Tim Ringenbach) * Catalan translation updated (Xan (DXpublica)) * English (British) translation updated (Luke Ross (lukeross)) * Hebrew translation had 1 character removed (Ambrose C. LI)
--- a/src/Makefile.am Thu Jan 15 17:08:38 2004 +0000 +++ b/src/Makefile.am Thu Jan 15 22:20:29 2004 +0000 @@ -92,6 +92,8 @@ prpl.h \ request.c \ request.h \ + roomlist.c \ + roomlist.h \ server.c \ server.h \ sha.c \ @@ -157,6 +159,8 @@ gtkpounce.h \ gtkrequest.c \ gtkrequest.h \ + gtkroomlist.c \ + gtkroomlist.h \ gtksound.c \ gtksound.h \ gtksourceiter.c \
--- a/src/gtkblist.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/gtkblist.c Thu Jan 15 22:20:29 2004 +0000 @@ -44,6 +44,7 @@ #include "gtkpounce.h" #include "gtkprefs.h" #include "gtkprivacy.h" +#include "gtkroomlist.h" #include "gtkutils.h" #include "ui.h" @@ -1882,6 +1883,7 @@ { "/Tools/sep1", NULL, NULL, 0, "<Separator>" }, { N_("/Tools/A_ccounts"), "<CTL>A", gaim_gtk_accounts_window_show, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, { N_("/Tools/_File Transfers"), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, + { N_("/Tools/R_oom List"), NULL, gaim_gtk_roomlist_dialog_show, 0, NULL }, { N_("/Tools/Pr_eferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, { N_("/Tools/Pr_ivacy"), NULL, gaim_gtk_privacy_dialog_show, 0, NULL }, #if 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkroomlist.c Thu Jan 15 22:20:29 2004 +0000 @@ -0,0 +1,627 @@ +/** + * @file gtkroomlist.c Gtk Room List UI + * @ingroup gtkui + * + * gaim + * + * Copyright (C) 2003, Timothy Ringenbach <omarvo@hotmail.com> + * + * 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 "gtkinternal.h" +#include "gtkutils.h" +#include "stock.h" +#include "debug.h" +#include "account.h" +#include "connection.h" +#include "notify.h" + +#include "gtkroomlist.h" + +typedef struct _GaimGtkRoomlist { + GaimGtkRoomlistDialog *dialog; + GtkTreeStore *model; + GtkWidget *tree; + GHashTable *cats; /**< Meow. */ + gint num_rooms, total_rooms; +} GaimGtkRoomlist; + +struct _GaimGtkRoomlistDialog { + GtkWidget *window; + GtkWidget *account_widget; + GtkWidget *progress; + GtkWidget *sw; + + GtkWidget *list_button; + GtkWidget *stop_button; + GtkWidget *close_button; + + GaimAccount *account; + GaimRoomlist *roomlist; + +}; + +enum { + NAME_COLUMN = 0, + ROOM_COLUMN, + NUM_OF_COLUMNS, +}; + +static GList *roomlists = NULL; + + + +static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) +{ + GaimGtkRoomlistDialog *dialog; + + dialog = (GaimGtkRoomlistDialog *) d; + + /* free stuff here */ + if (dialog->roomlist) + gaim_roomlist_unref(dialog->roomlist); + g_free(dialog); + + return FALSE; +} + +static void dialog_select_account_cb(GObject *w, GaimAccount *account, + GaimGtkRoomlistDialog *dialog) +{ + dialog->account = account; +} + +static void list_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) +{ + GaimConnection *gc; + GaimGtkRoomlist *rl; + + gc = gaim_account_get_connection(dialog->account); + if (!gc) + return; + + dialog->roomlist = gaim_roomlist_get_list(gc); + gaim_roomlist_ref(dialog->roomlist); + rl = dialog->roomlist->ui_data; + rl->dialog = dialog; + if (dialog->account_widget) + gtk_widget_set_sensitive(dialog->account_widget, FALSE); + gtk_widget_set_sensitive(dialog->list_button, FALSE); + gtk_container_add(GTK_CONTAINER(dialog->sw), rl->tree); /* XXX */ +} + +static void stop_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) +{ + gaim_roomlist_cancel_get_list(dialog->roomlist); + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); +} + +static void close_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) +{ + GtkWidget *window = dialog->window; + + delete_win_cb(NULL, NULL, dialog); + gtk_widget_destroy(window); +} + +struct _menu_cb_info { + GaimRoomlist *list; + GaimRoomlistRoom *room; +}; + +static void do_join_cb(GtkWidget *w, struct _menu_cb_info *info) +{ + GHashTable *components; + GList *l, *j; + GaimConnection *gc; + + gc = gaim_account_get_connection(info->list->account); + if (!gc) + return; + + components = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_replace(components, g_strdup("name"), g_strdup(info->room->name)); + for (l = info->list->fields, j = info->room->fields; l && j; l = l->next, j = j->next) { + GaimRoomlistField *f = l->data; + + g_hash_table_replace(components, f->name, j->data); + } + + serv_join_chat(gc, components); + + g_hash_table_destroy(components); +} + +static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2, + GaimRoomlist *list) +{ + GaimGtkRoomlist *grl = list->ui_data; + GtkTreeIter iter; + GaimRoomlistRoom *room; + GValue val = { 0, }; + struct _menu_cb_info info; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); + gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); + room = g_value_get_pointer(&val); + if (!room || !(room->type & GAIM_ROOMLIST_ROOMTYPE_ROOM)) + return; + + info.list = list; + info.room = room; + + do_join_cb(GTK_WIDGET(tv), &info); +} + +static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, GaimRoomlist *list) +{ + GtkTreePath *path; + GaimGtkRoomlist *grl = list->ui_data; + GValue val = { 0, }; + GaimRoomlistRoom *room; + GtkTreeIter iter; + GtkWidget *menu; + static struct _menu_cb_info info; /* XXX? */ + + if (event->button != 3 || event->type != GDK_BUTTON_PRESS) + return FALSE; + + /* Here we figure out which room was clicked */ + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) + return FALSE; + gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get_value (GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); + room = g_value_get_pointer(&val); + + if (!room || !(room->type & GAIM_ROOMLIST_ROOMTYPE_ROOM)) + return FALSE; + + info.list = list; + info.room = room; + + + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, + G_CALLBACK(do_join_cb), &info, 0, 0, NULL); + + + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); + + return FALSE; +} + +static void row_expanded_cb(GtkTreeView *treeview, GtkTreeIter *arg1, GtkTreePath *arg2, gpointer user_data) +{ + GaimRoomlist *list = user_data; + GaimRoomlistRoom *catagory; + GValue val = { 0, }; + + gtk_tree_model_get_value(gtk_tree_view_get_model(treeview), arg1, ROOM_COLUMN, &val); + catagory = g_value_get_pointer(&val); + + if (!catagory->expanded_once) { + gaim_roomlist_expand_catagory(list, catagory); + catagory->expanded_once = TRUE; + } +} + +static gboolean accounts_filter_func(GaimAccount *account) +{ + GaimConnection *gc; + + gc = gaim_account_get_connection(account); + if (!gc) + return FALSE; + return gaim_roomlist_is_possible(gc); +} + +GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new_with_account(GaimAccount *account) +{ + GaimGtkRoomlistDialog *dialog; + GtkWidget *window; + GtkWidget *vbox1, *vbox2; + GtkWidget *account_hbox; + GtkWidget *bbox; + GtkWidget *label; + GtkWidget *button; + GaimAccount *first_account = NULL; + + if (!account) { + GList *c; + GaimConnection *gc; + + for (c = gaim_connections_get_all(); c != NULL; c = c->next) { + gc = c->data; + + if (gaim_roomlist_is_possible(gc)) { + first_account = gaim_connection_get_account(gc); + break; + } + } + + if (first_account == NULL) { + gaim_notify_error(NULL, NULL, + _("You are not currently signed on with any " + "protocols that have the ability to list rooms."), + NULL); + + return NULL; + } + } + + dialog = g_new0(GaimGtkRoomlistDialog, 1); + + /* Create the window. */ + dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(window), "room list"); + gtk_window_set_title(GTK_WINDOW(window), _("Room List")); + + gtk_container_set_border_width(GTK_CONTAINER(window), 12); + gtk_widget_realize(window); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(delete_win_cb), dialog); + + /* Create the parent vbox for everything. */ + vbox1 = gtk_vbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(window), vbox1); + gtk_widget_show(vbox1); + + /* Create the main vbox for top half of the window. */ + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox1), vbox2, FALSE, FALSE, 0); + gtk_widget_show(vbox2); + + account_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), account_hbox, TRUE, TRUE, 0); + gtk_widget_show(account_hbox); + + /* accounts dropdown list */ + if (!account) { + dialog->account = first_account; + label = gtk_label_new(NULL); + gtk_box_pack_start(GTK_BOX(account_hbox), label, TRUE, TRUE, 0); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + + dialog->account_widget = gaim_gtk_account_option_menu_new(first_account, FALSE, + G_CALLBACK(dialog_select_account_cb), accounts_filter_func, dialog); + + gtk_box_pack_start(GTK_BOX(account_hbox), dialog->account_widget, TRUE, TRUE, 0); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(dialog->account_widget)); + gtk_widget_show(label); + gtk_widget_show(dialog->account_widget); + } else { + dialog->account = account; + } + + + /* Now the button box for the buttons */ + bbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(bbox), 6); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE); + gtk_box_pack_start(GTK_BOX(vbox2), bbox, FALSE, TRUE, 0); + gtk_widget_show(bbox); + + /* Get list button */ + button = gtk_button_new_with_mnemonic(_("Get _list")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + dialog->list_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_button_cb), dialog); + /* Stop button */ + button = gtk_button_new_from_stock(GTK_STOCK_STOP); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + gtk_widget_set_sensitive(button, FALSE); + dialog->stop_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(stop_button_cb), dialog); + /* Close button */ + button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + dialog->close_button = button; + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(close_button_cb), dialog); + + /* The pusling dilly */ + dialog->progress = gtk_progress_bar_new(); + gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(dialog->progress), + " "); + gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, TRUE, TRUE, 0); + gtk_widget_show(dialog->progress); + + + gtk_widget_show(dialog->window); + + dialog->sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(vbox1), dialog->sw, TRUE, TRUE, 0); + gtk_widget_show(dialog->sw); + + return dialog; +} + +GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new(void) +{ + return gaim_gtk_roomlist_dialog_new_with_account(NULL); +} + +void gaim_gtk_roomlist_dialog_show(void) +{ + gaim_gtk_roomlist_dialog_new(); +} + +static void gaim_gtk_roomlist_new(GaimRoomlist *list) +{ + GaimGtkRoomlist *rl; + + rl = g_new0(GaimGtkRoomlist, 1); + + list->ui_data = rl; + + rl->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free); + + roomlists = g_list_append(roomlists, list); +} + +static void int_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, + GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) +{ + gchar buf[16]; + int myint; + + gtk_tree_model_get(model, iter, GPOINTER_TO_INT(user_data), &myint, -1); + + if (myint) + g_snprintf(buf, sizeof(buf), "%d", myint); + else + buf[0] = '\0'; + + g_object_set(renderer, "text", buf, NULL); +} + +/* this sorts backwards on purpose, so that clicking name sorts a-z, while clicking users sorts + infinity-0. you can still click again to reverse it on any of them. */ +static gint int_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) +{ + int c, d; + + c = d = 0; + + gtk_tree_model_get(model, a, GPOINTER_TO_INT(user_data), &c, -1); + gtk_tree_model_get(model, b, GPOINTER_TO_INT(user_data), &d, -1); + + if (c == d) + return 0; + else if (c > d) + return -1; + else + return 1; +} + +static void gaim_gtk_roomlist_set_fields(GaimRoomlist *list, GList *fields) +{ + GaimGtkRoomlist *grl = list->ui_data; + gint columns = NUM_OF_COLUMNS; + int j; + GtkTreeStore *model; + GtkWidget *tree; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GList *l; + GType *types; + + g_return_if_fail(grl != NULL); + + columns += g_list_length(fields); + types = g_new(GType, columns); + + types[NAME_COLUMN] = G_TYPE_STRING; + types[ROOM_COLUMN] = G_TYPE_POINTER; + + for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) { + GaimRoomlistField *f = l->data; + + switch (f->type) { + case GAIM_ROOMLIST_FIELD_BOOL: + types[j] = G_TYPE_BOOLEAN; + break; + case GAIM_ROOMLIST_FIELD_INT: + types[j] = G_TYPE_INT; + break; + case GAIM_ROOMLIST_FIELD_STRING: + types[j] = G_TYPE_STRING; + break; + } + } + + model = gtk_tree_store_newv(columns, types); + g_free(types); + + tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE); + + g_object_unref(model); + + grl->model = model; + grl->tree = tree; + gtk_widget_show(grl->tree); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, + "text", NAME_COLUMN, NULL); + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), + GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN); + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + + for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) { + GaimRoomlistField *f = l->data; + + if (f->hidden) + continue; + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(f->label, renderer, + "text", j, NULL); + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), + GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), j); + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); + if (f->type == GAIM_ROOMLIST_FIELD_INT) { + gtk_tree_view_column_set_cell_data_func(column, renderer, int_cell_data_func, + GINT_TO_POINTER(j), NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), j, int_sort_func, + GINT_TO_POINTER(j), NULL); + } + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + } + + g_signal_connect(G_OBJECT(tree), "button-press-event", G_CALLBACK(room_click_cb), list); + g_signal_connect(G_OBJECT(tree), "row-expanded", G_CALLBACK(row_expanded_cb), list); + g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(row_activated_cb), list); + /* gtk_container_add(GTK_CONTAINER(grl->sw), tree); */ +} + +static void gaim_gtk_roomlist_add_room(GaimRoomlist *list, GaimRoomlistRoom *room) +{ + GaimGtkRoomlist *rl= list->ui_data; + GtkTreeRowReference *rr, *parentrr = NULL; + GtkTreePath *path; + GtkTreeIter iter, parent, child; + GList *l, *k; + int j; + gboolean append = TRUE; + + rl->total_rooms++; + if (room->type == GAIM_ROOMLIST_ROOMTYPE_ROOM) + rl->num_rooms++; + + if (rl->dialog) { + if (rl->total_rooms > 100) + gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(rl->dialog->progress), + 0.01); + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); + } + if (room->parent) { + parentrr = g_hash_table_lookup(rl->cats, room->parent); + path = gtk_tree_row_reference_get_path(parentrr); + if (path) { + GaimRoomlistRoom *tmproom = NULL; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(rl->model), &parent, path); + gtk_tree_path_free(path); + + if (gtk_tree_model_iter_children(GTK_TREE_MODEL(rl->model), &child, &parent)) { + gtk_tree_model_get(GTK_TREE_MODEL(rl->model), &child, ROOM_COLUMN, &tmproom, -1); + if (!tmproom) + append = FALSE; + } + } + } + + if (append) + gtk_tree_store_append(rl->model, &iter, (parentrr ? &parent : NULL)); + else + iter = child; + + if (room->type & GAIM_ROOMLIST_ROOMTYPE_CATAGORY) + gtk_tree_store_append(rl->model, &child, &iter); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(rl->model), &iter); + + if (room->type & GAIM_ROOMLIST_ROOMTYPE_CATAGORY) { + rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(rl->model), path); + g_hash_table_insert(rl->cats, room, rr); + } + + gtk_tree_path_free(path); + + gtk_tree_store_set(rl->model, &iter, NAME_COLUMN, room->name, -1); + gtk_tree_store_set(rl->model, &iter, ROOM_COLUMN, room, -1); + + for (j = NUM_OF_COLUMNS, l = room->fields, k = list->fields; l && k; j++, l = l->next, k = k->next) { + GaimRoomlistField *f = k->data; + if (f->hidden) + continue; + gtk_tree_store_set(rl->model, &iter, j, l->data, -1); + } +} + +static void gaim_gtk_roomlist_in_progress(GaimRoomlist *list, gboolean flag) +{ + GaimGtkRoomlist *rl = list->ui_data; + + if (!rl || !rl->dialog) + return; + + gtk_widget_set_sensitive(rl->dialog->stop_button, flag); + if (flag) { + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(rl->dialog->progress), + _("Downloading List...")); + } else { + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(rl->dialog->progress), + " "); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(rl->dialog->progress), 0.0); + } +} + +static void gaim_gtk_roomlist_destroy(GaimRoomlist *list) +{ + GaimGtkRoomlist *rl; + + roomlists = g_list_remove(roomlists, list); + + rl = list->ui_data; + + g_return_if_fail(rl != NULL); + + g_hash_table_destroy(rl->cats); + g_free(rl); + list->ui_data = NULL; +} + +static GaimRoomlistUiOps ops = { + gaim_gtk_roomlist_new, + gaim_gtk_roomlist_set_fields, + gaim_gtk_roomlist_add_room, + gaim_gtk_roomlist_in_progress, + gaim_gtk_roomlist_destroy +}; + + +void gaim_gtk_roomlist_init(void) +{ + gaim_roomlist_set_ui_ops(&ops); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkroomlist.h Thu Jan 15 22:20:29 2004 +0000 @@ -0,0 +1,53 @@ +/** + * @file gtkroomlist.h Gtk Room List UI + * @ingroup gtkui + * + * gaim + * + * Copyright (C) 2003, Timothy Ringenbach <omarvo@hotmail.com> + * + * 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 + */ + +#ifndef _GAIM_GTK_ROOMLIST_H_ +#define _GAIM_GTK_ROOMLIST_H_ + +#include "roomlist.h" + + +typedef struct _GaimGtkRoomlistDialog GaimGtkRoomlistDialog; + +/** + * Initializes the room list subsystem. + */ +void gaim_gtk_roomlist_init(void); + +void gaim_gtk_roomlist_dialog_show(void); +/** + * Create a new room list dialog. + * + * @return The new dialog. + */ +GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new(void); + +/** + * Create a new room list dialog with no account selector. + * + * @param account The account to force. + * @return The new dialog. + */ +GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new_with_account(GaimAccount *account); + +#endif /* _GAIM_GTK_ROOMLIST_H_ */
--- a/src/main.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/main.c Thu Jan 15 22:20:29 2004 +0000 @@ -49,6 +49,7 @@ #include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkrequest.h" +#include "gtkroomlist.h" #include "gtksound.h" #include "gtkutils.h" #include "stock.h" @@ -509,6 +510,7 @@ gaim_gtk_pounces_init(); gaim_gtk_privacy_init(); gaim_gtk_xfers_init(); + gaim_gtk_roomlist_init(); } static void
--- a/src/protocols/jabber/chat.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/jabber/chat.c Thu Jan 15 22:20:29 2004 +0000 @@ -22,6 +22,8 @@ #include "debug.h" #include "multi.h" /* for proto_chat_entry */ #include "notify.h" +#include "request.h" +#include "roomlist.h" #include "util.h" #include "chat.h" @@ -152,9 +154,12 @@ handle = g_hash_table_lookup(data, "handle"); passwd = g_hash_table_lookup(data, "password"); - if(!room || !server || !handle) + if(!room || !server) return; + if(!handle) + handle = js->user->node; + if(!jabber_nodeprep_validate(room)) { char *buf = g_strdup_printf(_("%s is not a valid room name"), room); gaim_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), @@ -602,4 +607,123 @@ g_free(room_jid); } +static void roomlist_disco_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + xmlnode *query; + xmlnode *item; + const char *type; + if(!js->roomlist) + return; + + if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { + /* XXX: error msg */ + gaim_roomlist_set_in_progress(js->roomlist, FALSE); + return; + } + + if(!(query = xmlnode_get_child(packet, "query"))) { + /* XXX: error msg */ + gaim_roomlist_set_in_progress(js->roomlist, FALSE); + return; + } + + for(item = query->child; item; item = item->next) { + const char *name; + GaimRoomlistRoom *room; + JabberID *jid; + + if(item->type != NODE_TYPE_TAG) + continue; + if(strcmp(item->name, "item")) + continue; + + if(!(jid = jabber_id_new(xmlnode_get_attrib(item, "jid")))) + continue; + name = xmlnode_get_attrib(item, "name"); + + + room = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, jid->node, NULL); + gaim_roomlist_room_add_field(js->roomlist, room, jid->node); + gaim_roomlist_room_add_field(js->roomlist, room, jid->domain); + gaim_roomlist_room_add_field(js->roomlist, room, name ? name : ""); + gaim_roomlist_room_add(js->roomlist, room); + + jabber_id_free(jid); + } + gaim_roomlist_set_in_progress(js->roomlist, FALSE); + gaim_roomlist_unref(js->roomlist); + js->roomlist = NULL; +} + +static void roomlist_ok_cb(JabberStream *js, const char *server) +{ + JabberIq *iq; + + if(!js->roomlist) + return; + + if(!server || !*server) { + gaim_notify_error(js->gc, _("Invalid Server"), _("Invalid Server"), NULL); + return; + } + + gaim_roomlist_set_in_progress(js->roomlist, TRUE); + + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); + + xmlnode_set_attrib(iq->node, "to", server); + + jabber_iq_set_callback(iq, roomlist_disco_result_cb, NULL); + + jabber_iq_send(iq); +} + +GaimRoomlist *jabber_roomlist_get_list(GaimConnection *gc) +{ + JabberStream *js = gc->proto_data; + GList *fields = NULL; + GaimRoomlistField *f; + + if(js->roomlist) + gaim_roomlist_unref(js->roomlist); + + js->roomlist = gaim_roomlist_new(gaim_connection_get_account(gc)); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "room", TRUE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "server", TRUE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Description"), "description", FALSE); + fields = g_list_append(fields, f); + + gaim_roomlist_set_fields(js->roomlist, fields); + + gaim_request_input(gc, _("Enter a Conference Server"), _("Enter a Conference Server"), + _("Select a conference server to query"), + js->chat_servers ? js->chat_servers->data : "conference.jabber.org", + FALSE, FALSE, _("Find Rooms"), G_CALLBACK(roomlist_ok_cb), _("Cancel"), NULL, js); + + return js->roomlist; +} + +void jabber_roomlist_cancel(GaimRoomlist *list) +{ + GaimConnection *gc; + JabberStream *js; + + gc = gaim_account_get_connection(list->account); + js = gc->proto_data; + + gaim_roomlist_set_in_progress(list, FALSE); + + if (js->roomlist == list) { + js->roomlist = NULL; + gaim_roomlist_unref(list); + } +} + + +
--- a/src/protocols/jabber/chat.h Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/jabber/chat.h Thu Jan 15 22:20:29 2004 +0000 @@ -25,6 +25,7 @@ #include "internal.h" #include "connection.h" #include "conversation.h" +#include "roomlist.h" #include "jabber.h" @@ -59,5 +60,8 @@ void jabber_chat_change_nick(JabberChat *chat, const char *nick); void jabber_chat_part(JabberChat *chat, const char *msg); +GaimRoomlist *jabber_roomlist_get_list(GaimConnection *gc); +void jabber_roomlist_cancel(GaimRoomlist *list); + #endif /* _GAIM_JABBER_CHAT_H_ */
--- a/src/protocols/jabber/jabber.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/jabber/jabber.c Thu Jan 15 22:20:29 2004 +0000 @@ -1170,7 +1170,10 @@ NULL, /* remove_group */ jabber_chat_buddy_real_name, jabber_chat_set_topic, - jabber_find_blist_chat + jabber_find_blist_chat, + jabber_roomlist_get_list, + jabber_roomlist_cancel, + NULL }; static GaimPluginInfo info =
--- a/src/protocols/jabber/jabber.h Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/jabber/jabber.h Thu Jan 15 22:20:29 2004 +0000 @@ -24,6 +24,7 @@ #include <glib.h> #include "connection.h" +#include "roomlist.h" #include "sslconn.h" #include "jutil.h" @@ -65,6 +66,7 @@ GHashTable *chats; GList *chat_servers; + GaimRoomlist *roomlist; GHashTable *callbacks; int next_id;
--- a/src/protocols/yahoo/yahoo.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/yahoo/yahoo.c Thu Jan 15 22:20:29 2004 +0000 @@ -2707,15 +2707,12 @@ return; /* It seems to work better without this */ - - /* - * if (gc->account->perm_deny != 4) - * return; - * - * if (!who || who[0] == '\0') - * return; - */ - + /* if (gc->account->perm_deny != 4) + return; */ + + if (!who || who[0] == '\0') + return; + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, 1, gaim_connection_get_display_name(gc)); yahoo_packet_hash(pkt, 7, who); @@ -3158,8 +3155,13 @@ #if 0 yahoo_ask_send_file, yahoo_send_file, - yahoo_has_send_file + yahoo_has_send_file, #endif + NULL, + NULL, + yahoo_roomlist_get_list, + yahoo_roomlist_cancel, + yahoo_roomlist_expand_catagory, }; static GaimPluginInfo info = @@ -3206,6 +3208,9 @@ option = gaim_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = gaim_account_option_string_new(_("Chat Room List Url"), "room_list", YAHOO_ROOMLIST_URL); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + my_protocol = plugin; yahoo_init_colorht();
--- a/src/protocols/yahoo/yahoo.h Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/yahoo/yahoo.h Thu Jan 15 22:20:29 2004 +0000 @@ -34,6 +34,7 @@ #define YAHOO_PROFILE_URL "http://profiles.yahoo.com/" #define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com" #define YAHOO_XFER_PORT 80 +#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/" #define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg"
--- a/src/protocols/yahoo/yahoochat.c Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/yahoo/yahoochat.c Thu Jan 15 22:20:29 2004 +0000 @@ -1004,3 +1004,403 @@ } } + +struct yahoo_roomlist { + int fd; + int inpa; + guchar *rxqueue; + int rxlen; + gboolean started; + char *path; + char *host; + GaimRoomlist *list; + GaimRoomlistRoom *cat; + GaimRoomlistRoom *ucat; + GMarkupParseContext *parse; + +}; + +static void yahoo_roomlist_destroy(struct yahoo_roomlist *yrl) +{ + if (yrl->inpa) + gaim_input_remove(yrl->inpa); + if (yrl->rxqueue) + g_free(yrl->rxqueue); + if (yrl->path) + g_free(yrl->path); + if (yrl->host) + g_free(yrl->host); + if (yrl->parse) + g_markup_parse_context_free(yrl->parse); +} + +enum yahoo_room_type { + yrt_yahoo, + yrt_user, +}; + +struct yahoo_chatxml_state { + GaimRoomlist *list; + struct yahoo_roomlist *yrl; + GQueue *q; + struct { + enum yahoo_room_type type; + char *name; + char *topic; + char *id; + int users, voices, webcams; + } room; +}; + +struct yahoo_lobby { + int count, users, voices, webcams; +}; + +static struct yahoo_chatxml_state *yahoo_chatxml_state_new(GaimRoomlist *list, struct yahoo_roomlist *yrl) +{ + struct yahoo_chatxml_state *s; + + s = g_new0(struct yahoo_chatxml_state, 1); + + s->list = list; + s->yrl = yrl; + s->q = g_queue_new(); + + return s; +} + +static void yahoo_chatxml_state_destroy(struct yahoo_chatxml_state *s) +{ + g_queue_free(s->q); + if (s->room.name) + g_free(s->room.name); + if (s->room.topic) + g_free(s->room.topic); + if (s->room.id) + g_free(s->room.id); + g_free(s); +} + +static void yahoo_chatlist_start_element(GMarkupParseContext *context, const gchar *ename, + const gchar **anames, const gchar **avalues, + gpointer user_data, GError **error) +{ + struct yahoo_chatxml_state *s = user_data; + GaimRoomlist *list = s->list; + GaimRoomlistRoom *r; + GaimRoomlistRoom *parent; + int i; + + if (!strcmp(ename, "category")) { + const gchar *name = NULL, *id = NULL; + + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "id")) + id = avalues[i]; + if (!strcmp(anames[i], "name")) + name = avalues[i]; + } + if (!name || !id) + return; + + parent = g_queue_peek_head(s->q); + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY, name, parent); + gaim_roomlist_room_add_field(list, r, (gpointer)name); + gaim_roomlist_room_add_field(list, r, (gpointer)id); + gaim_roomlist_room_add(list, r); + g_queue_push_head(s->q, r); + } else if (!strcmp(ename, "room")) { + s->room.users = s->room.voices = s->room.webcams = 0; + + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "id")) { + if (s->room.id) + g_free(s->room.id); + s->room.id = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "name")) { + if (s->room.name) + g_free(s->room.name); + s->room.name = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "topic")) { + if (s->room.topic) + g_free(s->room.topic); + s->room.topic = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "type")) { + if (!strcmp("yahoo", avalues[i])) + s->room.type = yrt_yahoo; + else + s->room.type = yrt_user; + } + } + + } else if (!strcmp(ename, "lobby")) { + struct yahoo_lobby *lob = g_new0(struct yahoo_lobby, 1); + + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "count")) { + lob->count = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "users")) { + s->room.users += lob->users = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "voices")) { + s->room.voices += lob->voices = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "webcams")) { + s->room.webcams += lob->webcams = strtol(avalues[i], NULL, 10); + } + } + + g_queue_push_head(s->q, lob); + } + +} + +static void yahoo_chatlist_end_element(GMarkupParseContext *context, const gchar *ename, + gpointer user_data, GError **error) +{ + struct yahoo_chatxml_state *s = user_data; + + if (!strcmp(ename, "category")) { + g_queue_pop_head(s->q); + } else if (!strcmp(ename, "room")) { + struct yahoo_lobby *lob; + GaimRoomlistRoom *r, *l; + + if (s->room.type == yrt_yahoo) + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM, + s->room.name, s->yrl->cat); + else + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM, + s->room.name, s->yrl->ucat); + + gaim_roomlist_room_add_field(s->list, r, s->room.name); + gaim_roomlist_room_add_field(s->list, r, s->room.id); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.users)); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.voices)); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.webcams)); + gaim_roomlist_room_add_field(s->list, r, s->room.topic); + gaim_roomlist_room_add(s->list, r); + + while ((lob = g_queue_pop_head(s->q))) { + char *name = g_strdup_printf("%s:%d", s->room.name, lob->count); + l = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, name, r); + + gaim_roomlist_room_add_field(s->list, l, name); + gaim_roomlist_room_add_field(s->list, l, s->room.id); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->users)); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->voices)); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->webcams)); + gaim_roomlist_room_add_field(s->list, l, s->room.topic); + gaim_roomlist_room_add(s->list, l); + + g_free(name); + g_free(lob); + } + + } + +} + +static GMarkupParser parser = { + yahoo_chatlist_start_element, + yahoo_chatlist_end_element, + NULL, + NULL, + NULL +}; + +static void yahoo_roomlist_cleanup(GaimRoomlist *list, struct yahoo_roomlist *yrl) +{ + gaim_roomlist_set_in_progress(list, FALSE); + + if (yrl) { + list->proto_data = g_list_remove(list->proto_data, yrl); + yahoo_roomlist_destroy(yrl); + } + + gaim_roomlist_unref(list); +} + +static void yahoo_roomlist_pending(gpointer data, gint source, GaimInputCondition cond) +{ + struct yahoo_roomlist *yrl = data; + GaimRoomlist *list = yrl->list; + char buf[1024]; + int len; + guchar *start; + struct yahoo_chatxml_state *s; + + len = read(yrl->fd, buf, sizeof(buf)); + + if (len <= 0) { + if (yrl->parse) + g_markup_parse_context_end_parse(yrl->parse, NULL); + yahoo_roomlist_cleanup(list, yrl); + return; + } + + + yrl->rxqueue = g_realloc(yrl->rxqueue, len + yrl->rxlen); + memcpy(yrl->rxqueue + yrl->rxlen, buf, len); + yrl->rxlen += len; + + if (!yrl->started) { + yrl->started = TRUE; + start = g_strstr_len(yrl->rxqueue, yrl->rxlen, "\r\n\r\n"); + if (!start || (start - yrl->rxqueue + 4) >= yrl->rxlen) + return; + start += 4; + } else { + start = yrl->rxqueue; + } + + if (yrl->parse == NULL) { + s = yahoo_chatxml_state_new(list, yrl); + yrl->parse = g_markup_parse_context_new(&parser, 0, s, + (GDestroyNotify)yahoo_chatxml_state_destroy); + } + + if (!g_markup_parse_context_parse(yrl->parse, start, (yrl->rxlen - (start - yrl->rxqueue)), NULL)) { + + yahoo_roomlist_cleanup(list, yrl); + return; + } + + yrl->rxlen = 0; +} + +static void yahoo_roomlist_got_connected(gpointer data, gint source, GaimInputCondition cond) +{ + struct yahoo_roomlist *yrl = data; + GaimRoomlist *list = yrl->list; + char *buf, *cookie; + struct yahoo_data *yd = gaim_account_get_connection(list->account)->proto_data; + + if (source < 0) { + gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed.")); + yahoo_roomlist_cleanup(list, yrl); + return; + } + + yrl->fd = source; + + cookie = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\nCookie: %s\r\n\r\n", yrl->path, yrl->host, cookie); + write(yrl->fd, buf, strlen(buf)); + g_free(cookie); + g_free(buf); + yrl->inpa = gaim_input_add(yrl->fd, GAIM_INPUT_READ, yahoo_roomlist_pending, yrl); + +} + +GaimRoomlist *yahoo_roomlist_get_list(GaimConnection *gc) +{ + struct yahoo_roomlist *yrl; + GaimRoomlist *rl; + char *url; + GList *fields = NULL; + GaimRoomlistField *f; + + url = g_strdup_printf("%s?chatcat=0", + gaim_account_get_string( + gaim_connection_get_account(gc), + "room_list", YAHOO_ROOMLIST_URL)); + + yrl = g_new0(struct yahoo_roomlist, 1); + rl = gaim_roomlist_new(gaim_connection_get_account(gc)); + yrl->list = rl; + + gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path)); + g_free(url); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "room", TRUE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "id", TRUE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Voices"), "voices", FALSE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Webcams"), "webcams", FALSE); + fields = g_list_append(fields, f); + + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE); + fields = g_list_append(fields, f); + + gaim_roomlist_set_fields(rl, fields); + + if (gaim_proxy_connect(gaim_connection_get_account(gc), + yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0) + { + gaim_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list.")); + yahoo_roomlist_cleanup(rl, yrl); + return NULL; + } + + rl->proto_data = g_list_append(rl->proto_data, yrl); + + gaim_roomlist_set_in_progress(rl, TRUE); + return rl; +} + +void yahoo_roomlist_cancel(GaimRoomlist *list) +{ + GList *l, *k; + + k = l = list->proto_data; + list->proto_data = NULL; + + gaim_roomlist_set_in_progress(list, FALSE); + + for (; l; l = l->next) { + yahoo_roomlist_destroy(l->data); + gaim_roomlist_unref(l->data); + } + g_list_free(k); +} + +void yahoo_roomlist_expand_catagory(GaimRoomlist *list, GaimRoomlistRoom *catagory) +{ + struct yahoo_roomlist *yrl; + char *url; + char *id; + + if (catagory->type != GAIM_ROOMLIST_ROOMTYPE_CATAGORY) + return; + + if (!(id = g_list_nth_data(catagory->fields, 1))) { + gaim_roomlist_set_in_progress(list, FALSE); + return; + } + + url = g_strdup_printf("%s?chatroom_%s=0", + gaim_account_get_string( + list->account, + "room_list", YAHOO_ROOMLIST_URL), id); + + yrl = g_new0(struct yahoo_roomlist, 1); + yrl->list = list; + yrl->cat = catagory; + list->proto_data = g_list_append(list->proto_data, yrl); + + gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path)); + g_free(url); + + yrl->ucat = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATAGORY, _("User Rooms"), yrl->cat); + gaim_roomlist_room_add(list, yrl->ucat); + + if (gaim_proxy_connect(list->account, + yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0) + { + gaim_notify_error(gaim_account_get_connection(list->account), + NULL, _("Connection problem"), _("Unable to fetch room list.")); + yahoo_roomlist_cleanup(list, yrl); + return; + } + + gaim_roomlist_set_in_progress(list, TRUE); + gaim_roomlist_ref(list); +} +
--- a/src/protocols/yahoo/yahoochat.h Thu Jan 15 17:08:38 2004 +0000 +++ b/src/protocols/yahoo/yahoochat.h Thu Jan 15 22:20:29 2004 +0000 @@ -25,6 +25,8 @@ #ifndef _YAHOOCHAT_H_ #define _YAHOOCHAT_H_ +#include "roomlist.h" + void yahoo_process_conference_invite(GaimConnection *gc, struct yahoo_packet *pkt); void yahoo_process_conference_decline(GaimConnection *gc, struct yahoo_packet *pkt); void yahoo_process_conference_logon(GaimConnection *gc, struct yahoo_packet *pkt); @@ -47,4 +49,9 @@ void yahoo_chat_goto(GaimConnection *gc, const char *name); +/* room listing functions */ +GaimRoomlist *yahoo_roomlist_get_list(GaimConnection *gc); +void yahoo_roomlist_cancel(GaimRoomlist *list); +void yahoo_roomlist_expand_catagory(GaimRoomlist *list, GaimRoomlistRoom *catagory); + #endif /* _YAHOO_CHAT_H_ */
--- a/src/prpl.h Thu Jan 15 17:08:38 2004 +0000 +++ b/src/prpl.h Thu Jan 15 22:20:29 2004 +0000 @@ -188,6 +188,10 @@ /** Custom away message. */ #define GAIM_AWAY_CUSTOM _("Custom") +/** Some structs defined in roomlist.h */ +struct _GaimRoomlist; +struct _GaimRoomlistRoom; + /** * A protocol plugin information structure. * @@ -318,6 +322,11 @@ void (*set_chat_topic)(GaimConnection *gc, int id, const char *topic); GaimChat *(*find_blist_chat)(GaimAccount *account, const char *name); + + /* room listing prpl callbacks */ + struct _GaimRoomlist *(*roomlist_get_list)(GaimConnection *gc); + void (*roomlist_cancel)(struct _GaimRoomlist *list); + void (*roomlist_expand_catagory)(struct _GaimRoomlist *list, struct _GaimRoomlistRoom *catagory); }; #define GAIM_IS_PROTOCOL_PLUGIN(plugin) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/roomlist.c Thu Jan 15 22:20:29 2004 +0000 @@ -0,0 +1,312 @@ +/** + * @file roomlist.c Room List API + * @ingroup core + * + * gaim + * + * Copyright (C) 2003, Timothy Ringenbach <omarvo@hotmail.com> + * + * 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 <glib.h> + +#include "account.h" +#include "connection.h" +#include "debug.h" +#include "roomlist.h" + + +static GaimRoomlistUiOps *ops = NULL; + +/**************************************************************************/ +/** @name Room List API */ +/**************************************************************************/ +/*@{*/ + +GaimRoomlist *gaim_roomlist_new(GaimAccount *account) +{ + GaimRoomlist *list; + + g_return_val_if_fail(account != NULL, NULL); + + list = g_new0(GaimRoomlist, 1); + list->account = account; + list->rooms = NULL; + list->fields = NULL; + list->ref = 1; + + if (ops && ops->new) + ops->new(list); + + return list; +} + +void gaim_roomlist_ref(GaimRoomlist *list) +{ + g_return_if_fail(list != NULL); + + list->ref++; + gaim_debug_misc("roomlist", "reffing list, ref count now %d\n", list->ref); +} + +static void gaim_roomlist_room_destroy(GaimRoomlist *list, GaimRoomlistRoom *r) +{ + GList *l, *j; + + for (l = list->fields, j = r->fields; l && j; l = l->next, j = j->next) { + GaimRoomlistField *f = l->data; + if (f->type == GAIM_ROOMLIST_FIELD_STRING) + g_free(j->data); + } + + g_list_free(r->fields); + g_free(r->name); + g_free(r); +} + +static void gaim_roomlist_field_destroy(GaimRoomlistField *f) +{ + g_free(f->label); + g_free(f->name); + g_free(f); +} + +static void gaim_roomlist_destroy(GaimRoomlist *list) +{ + GList *l; + + gaim_debug_misc("roomlist", "destroying list %p\n", list); + + if (ops && ops->destroy) + ops->destroy(list); + + if (list->rooms) { + for (l = list->rooms; l; l = l->next) { + GaimRoomlistRoom *r = l->data; + gaim_roomlist_room_destroy(list, r); + } + g_list_free(list->rooms); + } + + if (list->fields) { + for (l = list->fields; l; l = l->next) { + GaimRoomlistField *f = l->data; + gaim_roomlist_field_destroy(f); + } + g_list_free(list->fields); + } + + g_free(list); +} + +void gaim_roomlist_unref(GaimRoomlist *list) +{ + g_return_if_fail(list != NULL); + + list->ref--; + + gaim_debug_misc("roomlist", "unreffing list, ref count now %d\n", list->ref); + if (list->ref == 0) + gaim_roomlist_destroy(list); +} + +void gaim_roomlist_set_fields(GaimRoomlist *list, GList *fields) +{ + g_return_if_fail(list != NULL); + + list->fields = fields; + + if (ops && ops->set_fields) + ops->set_fields(list, fields); +} + +void gaim_roomlist_set_in_progress(GaimRoomlist *list, gboolean in_progress) +{ + g_return_if_fail(list != NULL); + + if (ops && ops->in_progress) + ops->in_progress(list, in_progress); +} + +void gaim_roomlist_room_add(GaimRoomlist *list, GaimRoomlistRoom *room) +{ + g_return_if_fail(list != NULL); + g_return_if_fail(room != NULL); + + list->rooms = g_list_append(list->rooms, room); + + if (ops && ops->add_room) + ops->add_room(list, room); +} + +gboolean gaim_roomlist_is_possible(GaimConnection *gc) +{ + GaimPluginProtocolInfo *prpl_info = NULL; + + g_return_val_if_fail(gc != NULL, FALSE); + + if (gc->prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info && prpl_info->roomlist_get_list) + return TRUE; + return FALSE; +} + +GaimRoomlist *gaim_roomlist_get_list(GaimConnection *gc) +{ + GaimPluginProtocolInfo *prpl_info = NULL; + + g_return_val_if_fail(gc != NULL, NULL); + + if (gc->prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info && prpl_info->roomlist_get_list) + return prpl_info->roomlist_get_list(gc); + return NULL; +} + +void gaim_roomlist_cancel_get_list(GaimRoomlist *list) +{ + GaimPluginProtocolInfo *prpl_info = NULL; + GaimConnection *gc; + + g_return_if_fail(list != NULL); + + gc = gaim_account_get_connection(list->account); + + g_return_if_fail(gc != NULL); + + if (gc != NULL && gc->prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info && prpl_info->roomlist_cancel) + prpl_info->roomlist_cancel(list); +} + +void gaim_roomlist_expand_catagory(GaimRoomlist *list, GaimRoomlistRoom *catagory) +{ + GaimPluginProtocolInfo *prpl_info = NULL; + GaimConnection *gc; + + g_return_if_fail(list != NULL); + g_return_if_fail(catagory != NULL); + g_return_if_fail(catagory->type & GAIM_ROOMLIST_ROOMTYPE_CATAGORY); + + gc = gaim_account_get_connection(list->account); + g_return_if_fail(gc != NULL); + + if (gc->prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info && prpl_info->roomlist_expand_catagory) + prpl_info->roomlist_expand_catagory(list, catagory); +} + +/*@}*/ + +/**************************************************************************/ +/** @name Room API */ +/**************************************************************************/ +/*@{*/ + +GaimRoomlistRoom *gaim_roomlist_room_new(GaimRoomlistRoomType type, const gchar *name, + GaimRoomlistRoom *parent) +{ + GaimRoomlistRoom *room; + + g_return_val_if_fail(name != NULL, NULL); + + room = g_new0(GaimRoomlistRoom, 1); + room->type = type; + room->name = g_strdup(name); + room->parent = parent; + + return room; +} + +void gaim_roomlist_room_add_field(GaimRoomlist *list, GaimRoomlistRoom *room, gconstpointer field) +{ + GaimRoomlistField *f; + + g_return_if_fail(list != NULL); + g_return_if_fail(room != NULL); + g_return_if_fail(list->fields != NULL); + + if (!room->fields) + f = list->fields->data; + else + f = g_list_nth_data(list->fields, g_list_length(room->fields)); + + g_return_if_fail(f != NULL); + + switch(f->type) { + case GAIM_ROOMLIST_FIELD_STRING: + room->fields = g_list_append(room->fields, g_strdup(field)); + break; + case GAIM_ROOMLIST_FIELD_BOOL: + case GAIM_ROOMLIST_FIELD_INT: + room->fields = g_list_append(room->fields, GINT_TO_POINTER(field)); + break; + } +} + +/*@}*/ + +/**************************************************************************/ +/** @name Room Field API */ +/**************************************************************************/ +/*@{*/ + +GaimRoomlistField *gaim_roomlist_field_new(GaimRoomlistFieldType type, + const gchar *label, const gchar *name, + gboolean hidden) +{ + GaimRoomlistField *f; + + g_return_val_if_fail(label != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + f = g_new0(GaimRoomlistField, 1); + + f->type = type; + f->label = g_strdup(label); + f->name = g_strdup(name); + f->hidden = hidden; + + return f; +} + +/*@}*/ + +/**************************************************************************/ +/** @name UI Registration Functions */ +/**************************************************************************/ +/*@{*/ + + +void gaim_roomlist_set_ui_ops(GaimRoomlistUiOps *ui_ops) +{ + ops = ui_ops; +} + +GaimRoomlistUiOps *gaim_roomlist_get_ui_ops(void) +{ + return ops; +} + +/*@}*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/roomlist.h Thu Jan 15 22:20:29 2004 +0000 @@ -0,0 +1,287 @@ +/** + * @file roomlist.h Room List API + * @ingroup core + * + * gaim + * + * Copyright (C) 2003, Timothy Ringenbach <omarvo@hotmail.com> + * + * 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 + */ + +#ifndef _GAIM_ROOMLIST_H_ +#define _GAIM_ROOMLIST_H_ + +/**************************************************************************/ +/** Data Structures */ +/**************************************************************************/ + +typedef struct _GaimRoomlist GaimRoomlist; +typedef struct _GaimRoomlistRoom GaimRoomlistRoom; +typedef enum _GaimRoomlistRoomType GaimRoomlistRoomType; +typedef struct _GaimRoomlistField GaimRoomlistField; +typedef enum _GaimRoomlistFieldType GaimRoomlistFieldType; +typedef struct _GaimRoomlistUiOps GaimRoomlistUiOps; + +/** + * Represents a list of rooms for a given connection on a given protocol. + */ +struct _GaimRoomlist { + GaimAccount *account; /**< The account this list belongs to. */ + GList *fields; /**< The fields. */ + GList *rooms; /**< The list of rooms. */ + gpointer ui_data; /**< UI private data. */ + gpointer proto_data; /** Prpl private data. */ + guint ref; /**< The reference count. */ +}; + +/** + * The types of rooms. + * + * These are ORable flags. + */ +enum _GaimRoomlistRoomType { + GAIM_ROOMLIST_ROOMTYPE_CATAGORY = 0x01, /**< It's a catagory, but not a room you can join. */ + GAIM_ROOMLIST_ROOMTYPE_ROOM = 0x02, /**< It's a room, like the kind you can join. */ +}; + +/** + * Represents a room. + */ +struct _GaimRoomlistRoom { + GaimRoomlistRoomType type; /**< The type of room. */ + gchar *name; /**< The name of the room. */ + GList *fields; /**< Other fields. */ + GaimRoomlistRoom *parent; /**< The parent room, or NULL. */ + gboolean expanded_once; /**< A flag the UI uses to avoid multiple expand prpl cbs. */ +}; + +/** + * The types of fields. + */ +enum _GaimRoomlistFieldType { + GAIM_ROOMLIST_FIELD_BOOL, + GAIM_ROOMLIST_FIELD_INT, + GAIM_ROOMLIST_FIELD_STRING, /**< We do a g_strdup on the passed value if it's this type. */ +}; + +/** + * A field a room might have. + */ +struct _GaimRoomlistField { + GaimRoomlistFieldType type; /**< The type of field. */ + gchar *label; /**< The i18n user displayed name of the field. */ + gchar *name; /**< The internal name of the field. */ + gboolean hidden; /**< Hidden? */ +}; + +/** + * The room list ops to be filled out by the UI. + */ +struct _GaimRoomlistUiOps { + void (*new)(GaimRoomlist *list); /**< A new list was created. */ + void (*set_fields)(GaimRoomlist *list, GList *fields); /**< Sets the columns. */ + void (*add_room)(GaimRoomlist *list, GaimRoomlistRoom *room); /**< Add a room to the list. */ + void (*in_progress)(GaimRoomlist *list, gboolean flag); /**< Are we fetching stuff still? */ + void (*destroy)(GaimRoomlist *list); /**< We're destroying list. */ +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/** @name Room List API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns a newly created room list object. + * + * It has an inital reference count of 1. + * + * @param account The account that's listing rooms. + * @return The new room list handle. + */ +GaimRoomlist *gaim_roomlist_new(GaimAccount *account); + +/** + * Increases the reference count on the room list. + * + * @param list The object to ref. + */ +void gaim_roomlist_ref(GaimRoomlist *list); + +/** + * Decreases the reference count on the room list. + * + * The room list will be destroyed when this reaches 0. + * + * @param list The room list object to unref and possibly + * destroy. + */ +void gaim_roomlist_unref(GaimRoomlist *list); + +/** + * Set the different field types and their names for this protocol. + * + * This must be called before gaim_roomlist_room_add(). + * + * @param list The room list. + * @param fields A GList of GaimRoomlistField's. UI's are encourged + * to default to displaying them in the order given. + */ +void gaim_roomlist_set_fields(GaimRoomlist *list, GList *fields); + +/** + * Set the "in progress" state of the room list. + * + * The UI is encourged to somehow hint to the user + * whether or not we're busy downloading a room list or not. + * + * @param list The room list. + * @param in_progress We're downloading it, or we're not. + */ +void gaim_roomlist_set_in_progress(GaimRoomlist *list, gboolean in_progress); + +/** + * Adds a room to the list of them. + * + * @param list The room list. + * @param room The room to add to the list. The GList of fields must be in the same + order as was given in gaim_roomlist_set_fields(). +*/ +void gaim_roomlist_room_add(GaimRoomlist *list, GaimRoomlistRoom *room); + +/** + * Do we support room listing? + * + * @param gc The GaimConnection we're asking. + * @return @c TRUE if it's possible to get a room list. + */ +gboolean gaim_roomlist_is_possible(GaimConnection *gc); + +/** + * Returns a GaimRoomlist structure from the prpl, and + * instructs the prpl to start fetching the list. + * + * @param gc The GaimConnection to have get a list. + * + * @return A GaimRoomlist* or @c NULL if the protocol + * doesn't support that. + */ +GaimRoomlist *gaim_roomlist_get_list(GaimConnection *gc); + +/** + * Tells the prpl to stop fetching the list. + * If this is possible and done, the prpl will + * call set_in_progress with @c FALSE and possibly + * unref the list if it took a reference. + * + * @param list The room list to cancel a get_list on. + */ +void gaim_roomlist_cancel_get_list(GaimRoomlist *list); + +/** + * Tells the prpl that a catagory was expanded. + * + * On some protocols, the rooms in the catagory + * won't be fetched until this is called. + * + * @param list The room list. + * @param room The catagory that was expanded. The expression + * (catagory->type & GAIM_ROOMLIST_ROOMTYPE_CATAGORY) + * must be true. + */ +void gaim_roomlist_expand_catagory(GaimRoomlist *list, GaimRoomlistRoom *catagory); + +/*@}*/ + +/**************************************************************************/ +/** @name Room API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new room, to be added to the list. + * + * @param type The type of room. + * @param name The name of the room. + * @param parent The room's parent, if any. + * + * @return A new room. + */ +GaimRoomlistRoom *gaim_roomlist_room_new(GaimRoomlistRoomType type, const gchar *name, + GaimRoomlistRoom *parent); + +/** + * Adds a field to a room. + * + * @param list The room list the room belongs to. + * @param room The room. + * @param field The field to append. Strings get g_strdup'd internally. + */ +void gaim_roomlist_room_add_field(GaimRoomlist *list, GaimRoomlistRoom *room, gconstpointer field); + +/*@}*/ + +/**************************************************************************/ +/** @name Room Field API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new field. + * + * @param type The type of the field. + * @param label The i18n'ed, user displayable name. + * @param name The internal name of the field. + * + * @return A new GaimRoomlistField, ready to be added to a GList and passed to + * gaim_roomlist_set_fields(). + */ +GaimRoomlistField *gaim_roomlist_field_new(GaimRoomlistFieldType type, + const gchar *label, const gchar *name, + gboolean hidden); +/*@}*/ + +/**************************************************************************/ +/** @name UI Registration Functions */ +/**************************************************************************/ +/*@{*/ + +/** + * Sets the UI operations structure to be used in all gaim room lists. + * + * @param ops The UI operations structure. + */ +void gaim_roomlist_set_ui_ops(GaimRoomlistUiOps *ops); + +/** + * Returns the gaim window UI operations structure to be used in + * new windows. + * + * @return A filled-out GaimRoomlistUiOps structure. + */ +GaimRoomlistUiOps *gaim_roomlist_get_ui_ops(void); + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_ROOMLIST_H_ */