Mercurial > pidgin
changeset 10418:bed2c96bc1fb
[gaim-migrate @ 11669]
I split the status-saving code into it's own little API, because it
really is separate from the other status.c
savedstatuses.c sits on top of the rest of the status API.
And you can delete saved statuses now.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 26 Dec 2004 00:46:26 +0000 |
parents | 823ad21cd95a |
children | c9b1f3fac753 |
files | src/Makefile.am src/Makefile.mingw src/account.c src/core.c src/gtkaccount.c src/gtkblist.c src/gtkmain.c src/gtkrequest.c src/gtksavedstatuses.c src/gtksavedstatuses.h src/gtkstatus.c src/gtkstatus.h src/pounce.c src/savedstatuses.c src/savedstatuses.h src/status.c src/status.h |
diffstat | 17 files changed, 1544 insertions(+), 1172 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Makefile.am Sat Dec 25 20:24:17 2004 +0000 +++ b/src/Makefile.am Sun Dec 26 00:46:26 2004 +0000 @@ -88,6 +88,7 @@ prpl.c \ request.c \ roomlist.c \ + savedstatuses.c \ server.c \ sha.c \ signals.c \ @@ -127,6 +128,7 @@ prpl.h \ request.h \ roomlist.h \ + savedstatuses.h \ server.h \ sha.h \ signals.h \ @@ -166,9 +168,9 @@ gtkpounce.c \ gtkrequest.c \ gtkroomlist.c \ + gtksavedstatuses.c \ gtksound.c \ gtksourceiter.c \ - gtkstatus.c \ gtkstatusselector.c \ gtkstock.c \ gtkthemes.c \ @@ -201,9 +203,9 @@ gtkpounce.h \ gtkrequest.h \ gtkroomlist.h \ + gtksavedstatuses.h \ gtksound.h \ gtksourceiter.h \ - gtkstatus.h \ gtkstatusselector.h \ gtkstock.h \ gtkutils.h \
--- a/src/Makefile.mingw Sat Dec 25 20:24:17 2004 +0000 +++ b/src/Makefile.mingw Sun Dec 26 00:46:26 2004 +0000 @@ -111,9 +111,9 @@ gtkprivacy.c \ gtkrequest.c \ gtkroomlist.c \ + gtksavedstatuses.c \ gtksound.c \ gtksourceiter.c \ - gtkstatus.c \ gtkstatusselector.c \ gtkstock.c \ gtkthemes.c \ @@ -133,6 +133,7 @@ prpl.c \ request.c \ roomlist.c \ + savedstatuses.c \ server.c \ sha.c \ signals.c \
--- a/src/account.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/account.c Sun Dec 26 00:46:26 2004 +0000 @@ -463,7 +463,7 @@ account->buddy_icon = (icon == NULL ? NULL : g_strdup(icon)); if (account->gc) serv_set_buddyicon(account->gc, icon); - + schedule_accounts_save(); }
--- a/src/core.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/core.c Sun Dec 26 00:46:26 2004 +0000 @@ -33,6 +33,7 @@ #include "prefs.h" #include "privacy.h" #include "proxy.h" +#include "savedstatuses.h" #include "signals.h" #include "sslconn.h" #include "status.h" @@ -86,7 +87,8 @@ ops->debug_ui_init(); } - gaim_statuses_init(); + gaim_status_init(); + gaim_savedstatuses_init(); gaim_accounts_init(); gaim_connections_init(); gaim_conversations_init(); @@ -129,7 +131,8 @@ gaim_connections_uninit(); gaim_buddy_icons_uninit(); gaim_accounts_uninit(); - gaim_statuses_uninit(); + gaim_savedstatuses_uninit(); + gaim_status_uninit(); gaim_prefs_uninit(); gaim_sound_uninit();
--- a/src/gtkaccount.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/gtkaccount.c Sun Dec 26 00:46:26 2004 +0000 @@ -2164,9 +2164,11 @@ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); dialog->treeview = treeview; gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); - gtk_tree_selection_set_mode( - gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), - GTK_SELECTION_MULTIPLE); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(account_selected_cb), dialog); gtk_container_add(GTK_CONTAINER(sw), treeview); gtk_widget_show(treeview); @@ -2175,10 +2177,6 @@ populate_accounts_list(dialog); - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); - g_signal_connect(G_OBJECT(sel), "changed", - G_CALLBACK(account_selected_cb), dialog); - /* Setup DND. I wanna be an orc! */ gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte,
--- a/src/gtkblist.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/gtkblist.c Sun Dec 26 00:46:26 2004 +0000 @@ -51,8 +51,8 @@ #include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkroomlist.h" +#include "gtksavedstatuses.h" #include "gtksound.h" -#include "gtkstatus.h" #include "gtkstatusselector.h" #include "gtkutils.h"
--- a/src/gtkmain.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/gtkmain.c Sun Dec 26 00:46:26 2004 +0000 @@ -640,7 +640,6 @@ gaim_plugins_load_saved("/gaim/gtk/plugins/loaded"); gaim_pounces_load(); - gaim_statuses_load(); ui_main();
--- a/src/gtkrequest.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/gtkrequest.c Sun Dec 26 00:46:26 2004 +0000 @@ -604,8 +604,8 @@ if (default_action == GAIM_DEFAULT_ACTION_NONE) { - GTK_WIDGET_SET_FLAGS(img, GTK_CAN_DEFAULT); - GTK_WIDGET_SET_FLAGS(img, GTK_CAN_FOCUS); + GTK_WIDGET_SET_FLAGS(img, GTK_CAN_DEFAULT); + GTK_WIDGET_SET_FLAGS(img, GTK_CAN_FOCUS); gtk_widget_grab_focus(img); gtk_widget_grab_default(img); } else
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtksavedstatuses.c Sun Dec 26 00:46:26 2004 +0000 @@ -0,0 +1,749 @@ +/** + * @file gtksavedstatus.c GTK+ Saved Status Editor UI + * @ingroup gtkui + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 "account.h" +#include "internal.h" +#include "request.h" +#include "savedstatuses.h" +#include "status.h" +#include "util.h" + +#include "gtkblist.h" +#include "gtkgaim.h" +#include "gtkgaim-disclosure.h" +#include "gtkimhtml.h" +#include "gtkimhtmltoolbar.h" +#include "gtksavedstatuses.h" +#include "gtkstock.h" +#include "gtkutils.h" + +enum +{ + STATUS_EDITOR_COLUMN_CUSTOM_STATUS, + STATUS_EDITOR_COLUMN_ICON, + STATUS_EDITOR_COLUMN_SCREENNAME, + STATUS_EDITOR_NUM_COLUMNS +}; + +enum +{ + STATUS_WINDOW_COLUMN_TITLE, + STATUS_WINDOW_COLUMN_MESSAGE, + STATUS_WINDOW_NUM_COLUMNS +}; + +typedef struct +{ + GtkWidget *window; + GtkListStore *model; + GtkWidget *treeview; +} StatusEditor; + +typedef struct +{ + GtkWidget *window; + GtkListStore *model; + GtkWidget *treeview; + GtkWidget *modify_button; + GtkWidget *delete_button; +} StatusWindow; + +static StatusWindow *status_window = NULL; + +static gboolean +status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + StatusWindow *dialog = user_data; + + g_free(dialog); + + return FALSE; +} + +static void +status_editor_cancel_cb(GtkButton *button, gpointer user_data) +{ + StatusWindow *dialog = user_data; + + gtk_widget_destroy(dialog->window); + + g_free(dialog); +} + +static void +status_editor_save_cb(GtkButton *button, gpointer user_data) +{ + /* TODO: Save the status */ +} + +/* TODO: Can this menu be created more automatically? */ +static GtkWidget * +create_status_type_menu() +{ + GtkWidget *dropdown; + GtkWidget *menu; + GtkWidget *item; + + dropdown = gtk_option_menu_new(); + menu = gtk_menu_new(); + + /* GAIM_STATUS_AVAILABLE */ + item = gtk_menu_item_new_with_label(_("Available")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* GAIM_STATUS_UNAVAILABLE */ + item = gtk_menu_item_new_with_label(_("Unavailable")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* GAIM_STATUS_HIDDEN */ + item = gtk_menu_item_new_with_label(_("Hidden")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* GAIM_STATUS_AWAY */ + item = gtk_menu_item_new_with_label(_("Away")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* GAIM_STATUS_EXTENDED_AWAY */ + item = gtk_menu_item_new_with_label(_("Extended Away")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); + gtk_widget_show(menu); + + return dropdown; +} + +static void +status_editor_custom_status_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) +{ + /* StatusEditor *dialog = (StatusEditor *)data; */ + + /* TODO: Need to allow user to set a custom status for the highlighted account, somehow */ +} + +static void +status_editor_add_columns(StatusEditor *dialog) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* Custom status column */ + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview), + -1, _("Custom status"), + renderer, + "active", STATUS_EDITOR_COLUMN_CUSTOM_STATUS, + NULL); + column = gtk_tree_view_get_column(GTK_TREE_VIEW(dialog->treeview), 1); + g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(status_editor_custom_status_cb), dialog); + + /* Screen Name column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_title(column, _("Screen Name")); + gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + + /* Icon */ + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", + STATUS_EDITOR_COLUMN_ICON); + + /* Screen Name */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "text", + STATUS_EDITOR_COLUMN_SCREENNAME); +} + +static void +status_editor_set_account(GtkListStore *store, GaimAccount *account, GtkTreeIter *iter) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scale; + + scale = NULL; + + pixbuf = create_prpl_icon(account); + + if (pixbuf != NULL) + { + scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); + + if (!gaim_account_is_connected(account)) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); + } + + gtk_list_store_set(store, iter, + STATUS_EDITOR_COLUMN_ICON, scale, + STATUS_EDITOR_COLUMN_SCREENNAME, gaim_account_get_username(account), + -1); + + if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); + if (scale != NULL) g_object_unref(G_OBJECT(scale)); +} + +static void +status_editor_add_account(StatusEditor *dialog, GaimAccount *account) +{ + GtkTreeIter iter; + + gtk_list_store_append(dialog->model, &iter); + + status_editor_set_account(dialog->model, account, &iter); +} + +static void +status_editor_populate_list(StatusEditor *dialog) +{ + GList *l; + + gtk_list_store_clear(dialog->model); + + for (l = gaim_accounts_get_all(); l != NULL; l = l->next) + status_editor_add_account(dialog, (GaimAccount *)l->data); +} + +void +gaim_gtk_status_editor_show(void) +{ + StatusEditor *dialog; + GtkSizeGroup *sg; + GtkWidget *bbox; + GtkWidget *button; + GtkWidget *dbox; + GtkWidget *disclosure; + GtkWidget *dropdown; + GtkWidget *entry; + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *sep; + GtkWidget *sw; + GtkWidget *text; + GtkWidget *toolbar; + GtkWidget *vbox; + GtkWidget *win; + + dialog = g_new0(StatusEditor, 1); + + dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(win), "status"); + gtk_window_set_title(GTK_WINDOW(win), _("Status")); + gtk_window_set_resizable(GTK_WINDOW(win), FALSE); + gtk_container_set_border_width(GTK_CONTAINER(win), 12); + + g_signal_connect(G_OBJECT(win), "delete_event", + G_CALLBACK(status_editor_destroy_cb), dialog); + + /* Setup the vbox */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(win), vbox); + gtk_widget_show(vbox); + + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* Title */ + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new_with_mnemonic(_("_Title:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + gtk_size_group_add_widget(sg, label); + + entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(entry), _("Out of the office")); + gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); + gtk_widget_show(entry); + + /* Status type */ + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new_with_mnemonic(_("_Status:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + gtk_size_group_add_widget(sg, label); + + dropdown = create_status_type_menu(); + gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0); + gtk_widget_show(dropdown); + + /* Status message */ + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new_with_mnemonic(_("_Message:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + gtk_size_group_add_widget(sg, label); + + frame = gaim_gtk_create_imhtml(TRUE, &text, &toolbar); + gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); + gtk_widget_show(frame); + + /* Custom status message disclosure */ + disclosure = gaim_disclosure_new(_("Use a different status for some accounts"), + _("Use a different status for some accounts")); + gtk_box_pack_start(GTK_BOX(vbox), disclosure, FALSE, FALSE, 0); + gtk_widget_show(disclosure); + + /* Setup the box that the disclosure will cover */ + dbox = gtk_vbox_new(FALSE, 18); + gtk_box_pack_start(GTK_BOX(vbox), dbox, FALSE, FALSE, 0); + gaim_disclosure_set_container(GAIM_DISCLOSURE(disclosure), dbox); + + /* Custom status message treeview */ + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(dbox), sw, TRUE, TRUE, 0); + gtk_widget_show(sw); + + /* Create the list model */ + dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS, + G_TYPE_BOOLEAN, + GDK_TYPE_PIXBUF, G_TYPE_STRING); + + /* Create the treeview */ + dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE); + gtk_widget_set_size_request(dialog->treeview, 400, 250); + gtk_container_add(GTK_CONTAINER(sw), dialog->treeview); + gtk_widget_show(dialog->treeview); + + /* Add columns */ + status_editor_add_columns(dialog); + + /* Populate list */ + status_editor_populate_list(dialog); + + /* Separator */ + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); + gtk_widget_show(sep); + + /* Button box */ + bbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(bbox), 6); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); + gtk_widget_show(bbox); + + /* Cancel button */ + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_editor_cancel_cb), dialog); + + /* Save button */ + /* TODO: This button needs to be made sensitive when the title box contains text */ + button = gtk_button_new_from_stock(GTK_STOCK_SAVE); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_sensitive(button, FALSE); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_editor_save_cb), dialog); + + gtk_widget_show(win); +} + +static gboolean +status_window_find_savedstatus(GtkTreeIter *iter, const char *title) +{ + GtkTreeModel *model = GTK_TREE_MODEL(status_window->model); + const char *cur; + + if (!gtk_tree_model_get_iter_first(model, iter)) + return FALSE; + + gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); + if (!strcmp(title, cur)) + return TRUE; + + while (gtk_tree_model_iter_next(model, iter)) + { + gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); + if (!strcmp(title, cur)) + return TRUE; + } + + return FALSE; +} + +static gboolean +status_window_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + StatusWindow *dialog = user_data; + + dialog->window = NULL; + gaim_gtk_status_window_hide(); + + return FALSE; +} + +static void +status_window_add_cb(GtkButton *button, gpointer user_data) +{ + gaim_gtk_status_editor_show(); +} + +static void +status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer user_data) +{ + const char *title; + GaimStatusSaved *status; + + gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); + + status = gaim_savedstatuses_find(title); + /* TODO: Need to show a status editor pre-populated with this status */ +} + +static void +status_window_modify_cb(GtkButton *button, gpointer user_data) +{ + StatusWindow *dialog = user_data; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); + + gtk_tree_selection_selected_foreach(selection, status_window_modify_foreach, user_data); +} + +static void +status_window_delete_confirm_cb(char *title) +{ + GtkTreeIter iter; + + if (status_window_find_savedstatus(&iter, title)) + gtk_list_store_remove(status_window->model, &iter); + + gaim_savedstatuses_delete(title); + + g_free(title); +} + +static void +status_window_delete_foreach(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer user_data) +{ + const char *title; + char *buf; + + gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); + + buf = g_strdup_printf(_("Are you sure you want to delete %s?"), title); + gaim_request_action(NULL, NULL, buf, NULL, 0, g_strdup(title), 2, + _("Delete"), status_window_delete_confirm_cb, + _("Cancel"), g_free); + g_free(buf); +} + +static void +status_window_delete_cb(GtkButton *button, gpointer user_data) +{ + StatusWindow *dialog = user_data; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); + + gtk_tree_selection_selected_foreach(selection, status_window_delete_foreach, user_data); +} + +static void +status_window_close_cb(GtkButton *button, gpointer user_data) +{ + gaim_gtk_status_window_hide(); +} + +static void +status_selected_cb(GtkTreeSelection *sel, gpointer user_data) +{ + StatusWindow *dialog = user_data; + gboolean selected = FALSE; + +#if GTK_CHECK_VERSION(2,2,0) + selected = (gtk_tree_selection_count_selected_rows(sel) > 0); +#else + gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected); +#endif + + gtk_widget_set_sensitive(dialog->modify_button, selected); + gtk_widget_set_sensitive(dialog->delete_button, selected); +} + +static void +populate_saved_status_list(StatusWindow *dialog) +{ + GtkTreeIter iter; + const GList *saved_statuses; + GaimStatusSaved *saved_status; + char *message; + + gtk_list_store_clear(dialog->model); + + for (saved_statuses = gaim_savedstatuses_get_all(); saved_statuses != NULL; + saved_statuses = g_list_next(saved_statuses)) + { + saved_status = (GaimStatusSaved *)saved_statuses->data; + message = gaim_markup_strip_html(gaim_savedstatuses_get_message(saved_status)); + if (strlen(message) > 70) + strcpy(&message[68], "..."); + + gtk_list_store_append(dialog->model, &iter); + gtk_list_store_set(dialog->model, &iter, + STATUS_WINDOW_COLUMN_TITLE, gaim_savedstatuses_get_title(saved_status), + STATUS_WINDOW_COLUMN_MESSAGE, message, + -1); + free(message); + } +} + +static GtkWidget * +create_saved_status_list(StatusWindow *dialog) +{ + GtkWidget *sw; + GtkWidget *treeview; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + /* Create the scrolled window */ + sw = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_widget_show(sw); + + /* Create the list model */ + dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING); + + /* Create the treeview */ + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); + dialog->treeview = treeview; + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(status_selected_cb), dialog); + + gtk_container_add(GTK_CONTAINER(sw), treeview); + gtk_widget_show(treeview); + + + /* Add columns */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Title")); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, + STATUS_WINDOW_COLUMN_TITLE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "text", + STATUS_WINDOW_COLUMN_TITLE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Message")); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, + STATUS_WINDOW_COLUMN_MESSAGE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "text", + STATUS_WINDOW_COLUMN_MESSAGE); + + /* Populate list */ + populate_saved_status_list(dialog); + + return sw; +} + +static gboolean +configure_cb(GtkWidget *widget, GdkEventConfigure *event, StatusWindow *dialog) +{ + if (GTK_WIDGET_VISIBLE(widget)) { + gaim_prefs_set_int("/gaim/gtk/status/dialog/width", event->width); + gaim_prefs_set_int("/gaim/gtk/status/dialog/height", event->height); + } + + return FALSE; +} + +void +gaim_gtk_status_window_show(void) +{ + StatusWindow *dialog; + GtkWidget *bbox; + GtkWidget *button; + GtkWidget *list; + GtkWidget *sep; + GtkWidget *vbox; + GtkWidget *win; + int width, height; + + if (status_window != NULL) { + gtk_window_present(GTK_WINDOW(status_window->window)); + return; + } + + status_window = dialog = g_new0(StatusWindow, 1); + + width = gaim_prefs_get_int("/gaim/gtk/status/dialog/width"); + height = gaim_prefs_get_int("/gaim/gtk/status/dialog/height"); + + dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(win), width, height); + gtk_window_set_role(GTK_WINDOW(win), "statuses"); + gtk_window_set_title(GTK_WINDOW(win), _("Saved Statuses")); + gtk_container_set_border_width(GTK_CONTAINER(win), 12); + + g_signal_connect(G_OBJECT(win), "delete_event", + G_CALLBACK(status_window_destroy_cb), dialog); + g_signal_connect(G_OBJECT(win), "configure_event", + G_CALLBACK(configure_cb), dialog); + + /* Setup the vbox */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(win), vbox); + gtk_widget_show(vbox); + + /* List of saved status states */ + list = create_saved_status_list(dialog); + gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); + + /* Separator... */ + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); + gtk_widget_show(sep); + + /* Button box. */ + bbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(bbox), 6); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); + gtk_widget_show(bbox); + + /* Add button */ + button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_window_add_cb), dialog); + + /* Modify button */ + button = gtk_button_new_from_stock(GAIM_STOCK_MODIFY); + dialog->modify_button = button; + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_sensitive(button, FALSE); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_window_modify_cb), dialog); + + /* Delete button */ + button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + dialog->delete_button = button; + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_sensitive(button, FALSE); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_window_delete_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); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(status_window_close_cb), dialog); + + gtk_widget_show(win); +} + +void +gaim_gtk_status_window_hide(void) +{ + if (status_window == NULL) + return; + + if (status_window->window != NULL) + gtk_widget_destroy(status_window->window); + + g_free(status_window); + status_window = NULL; +} + +void * +gaim_gtk_status_get_handle() +{ + static int handle; + + return &handle; +} + +void +gaim_gtk_status_init(void) +{ + gaim_prefs_add_none("/gaim/gtk/status"); + gaim_prefs_add_none("/gaim/gtk/status/dialog"); + gaim_prefs_add_int("/gaim/gtk/status/dialog/width", 550); + gaim_prefs_add_int("/gaim/gtk/status/dialog/height", 250); +} + +void +gaim_gtk_status_uninit(void) +{ + gaim_gtk_status_window_hide(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtksavedstatuses.h Sun Dec 26 00:46:26 2004 +0000 @@ -0,0 +1,57 @@ +/** + * @file gtksavedstatus.h GTK+ Saved Status Editor UI + * @ingroup gtkui + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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_GTKSAVEDSTATUSES_H_ +#define _GAIM_GTKSAVEDSTATUSES_H_ + +#include "status.h" + +/** + * Shows the status window. + */ +void gaim_gtk_status_window_show(void); + +/** + * Hides the status window. + */ +void gaim_gtk_status_window_hide(void); + +/** + * Returns the gtkstatus handle. + * + * @return The handle to the GTK+ status system. + */ +void *gaim_gtk_status_get_handle(void); + +/** + * Initializes the GTK+ status system. + */ +void gaim_gtk_status_init(void); + +/** + * Uninitializes the GTK+ status system. + */ +void gaim_gtk_status_uninit(void); + +#endif /* _GAIM_GTKSAVEDSTATUSES_H_ */
--- a/src/gtkstatus.c Sat Dec 25 20:24:17 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,621 +0,0 @@ -/** - * @file gtkstatus.c GTK+ Status Editor UI - * @ingroup gtkui - * - * gaim - * - * Gaim is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 "account.h" -#include "internal.h" -#include "status.h" - -#include "gtkblist.h" -#include "gtkgaim.h" -#include "gtkgaim-disclosure.h" -#include "gtkimhtml.h" -#include "gtkimhtmltoolbar.h" -#include "gtkstatus.h" -#include "gtkstock.h" -#include "gtkutils.h" - -enum -{ - STATUS_EDITOR_COLUMN_CUSTOM_STATUS, - STATUS_EDITOR_COLUMN_ICON, - STATUS_EDITOR_COLUMN_SCREENNAME, - STATUS_EDITOR_NUM_COLUMNS -}; - -typedef struct -{ - GtkWidget *window; - GtkListStore *model; - GtkWidget *treeview; -} StatusEditor; - -typedef struct -{ - GtkWidget *window; - GtkListStore *model; - GtkWidget *treeview; - GtkWidget *modify_button; - GtkWidget *delete_button; -} StatusWindow; - -static StatusWindow *status_window = NULL; - -static gboolean -status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) -{ - StatusWindow *dialog = user_data; - - g_free(dialog); - - return FALSE; -} - -static void -status_editor_cancel_cb(GtkButton *button, gpointer user_data) -{ - StatusWindow *dialog = user_data; - - gtk_widget_destroy(dialog->window); - - g_free(dialog); -} - -static void -status_editor_save_cb(GtkButton *button, gpointer user_data) -{ - /* TODO: Save the status */ -} - -/* TODO: Can this menu be created more automatically? */ -static GtkWidget * -create_status_type_menu() -{ - GtkWidget *dropdown; - GtkWidget *menu; - GtkWidget *item; - - dropdown = gtk_option_menu_new(); - menu = gtk_menu_new(); - - /* GAIM_STATUS_AVAILABLE */ - item = gtk_menu_item_new_with_label(_("Available")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - /* GAIM_STATUS_UNAVAILABLE */ - item = gtk_menu_item_new_with_label(_("Unavailable")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - /* GAIM_STATUS_HIDDEN */ - item = gtk_menu_item_new_with_label(_("Hidden")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - /* GAIM_STATUS_AWAY */ - item = gtk_menu_item_new_with_label(_("Away")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - /* GAIM_STATUS_EXTENDED_AWAY */ - item = gtk_menu_item_new_with_label(_("Extended Away")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); - gtk_widget_show(menu); - - return dropdown; -} - -static void -status_editor_custom_status_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) -{ - /* StatusEditor *dialog = (StatusEditor *)data; */ - - /* TODO: Need to allow user to set a custom status for the highlighted account, somehow */ -} - -static void -status_editor_add_columns(StatusEditor *dialog) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - /* Custom status column */ - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview), - -1, _("Custom status"), - renderer, - "active", STATUS_EDITOR_COLUMN_CUSTOM_STATUS, - NULL); - column = gtk_tree_view_get_column(GTK_TREE_VIEW(dialog->treeview), 1); - g_signal_connect(G_OBJECT(renderer), "toggled", - G_CALLBACK(status_editor_custom_status_cb), dialog); - - /* Screen Name column */ - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_resizable(column, TRUE); - gtk_tree_view_column_set_title(column, _("Screen Name")); - gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); - gtk_tree_view_column_set_resizable(column, TRUE); - - /* Icon */ - renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, renderer, FALSE); - gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", - STATUS_EDITOR_COLUMN_ICON); - - /* Screen Name */ - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "text", - STATUS_EDITOR_COLUMN_SCREENNAME); -} - -static void -status_editor_set_account(GtkListStore *store, GaimAccount *account, GtkTreeIter *iter) -{ - GdkPixbuf *pixbuf; - GdkPixbuf *scale; - - scale = NULL; - - pixbuf = create_prpl_icon(account); - - if (pixbuf != NULL) - { - scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); - - if (!gaim_account_is_connected(account)) - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); - } - - gtk_list_store_set(store, iter, - STATUS_EDITOR_COLUMN_ICON, scale, - STATUS_EDITOR_COLUMN_SCREENNAME, gaim_account_get_username(account), - -1); - - if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); - if (scale != NULL) g_object_unref(G_OBJECT(scale)); -} - -static void -status_editor_add_account(StatusEditor *dialog, GaimAccount *account) -{ - GtkTreeIter iter; - - gtk_list_store_append(dialog->model, &iter); - - status_editor_set_account(dialog->model, account, &iter); -} - -static void -status_editor_populate_list(StatusEditor *dialog) -{ - GList *l; - - gtk_list_store_clear(dialog->model); - - for (l = gaim_accounts_get_all(); l != NULL; l = l->next) - status_editor_add_account(dialog, (GaimAccount *)l->data); -} - -void -gaim_gtk_status_editor_show(void) -{ - StatusEditor *dialog; - GtkSizeGroup *sg; - GtkWidget *bbox; - GtkWidget *button; - GtkWidget *dbox; - GtkWidget *disclosure; - GtkWidget *dropdown; - GtkWidget *entry; - GtkWidget *frame; - GtkWidget *hbox; - GtkWidget *label; - GtkWidget *sep; - GtkWidget *sw; - GtkWidget *text; - GtkWidget *toolbar; - GtkWidget *vbox; - GtkWidget *win; - - dialog = g_new0(StatusEditor, 1); - - dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_role(GTK_WINDOW(win), "status"); - gtk_window_set_title(GTK_WINDOW(win), _("Status")); - gtk_window_set_resizable(GTK_WINDOW(win), FALSE); - gtk_container_set_border_width(GTK_CONTAINER(win), 12); - - g_signal_connect(G_OBJECT(win), "delete_event", - G_CALLBACK(status_editor_destroy_cb), dialog); - - /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, 12); - gtk_container_add(GTK_CONTAINER(win), vbox); - gtk_widget_show(vbox); - - sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - - /* Title */ - hbox = gtk_hbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(_("_Title:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - gtk_size_group_add_widget(sg, label); - - entry = gtk_entry_new(); - gtk_entry_set_text(GTK_ENTRY(entry), _("Out of the office")); - gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); - gtk_widget_show(entry); - - /* Status type */ - hbox = gtk_hbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(_("_Status:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - gtk_size_group_add_widget(sg, label); - - dropdown = create_status_type_menu(); - gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0); - gtk_widget_show(dropdown); - - /* Status message */ - hbox = gtk_hbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); - - label = gtk_label_new_with_mnemonic(_("_Message:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); - gtk_size_group_add_widget(sg, label); - - frame = gaim_gtk_create_imhtml(TRUE, &text, &toolbar); - gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); - gtk_widget_show(frame); - - /* Custom status message disclosure */ - disclosure = gaim_disclosure_new(_("Use a different status for some accounts"), - _("Use a different status for some accounts")); - gtk_box_pack_start(GTK_BOX(vbox), disclosure, FALSE, FALSE, 0); - gtk_widget_show(disclosure); - - /* Setup the box that the disclosure will cover */ - dbox = gtk_vbox_new(FALSE, 18); - gtk_box_pack_start(GTK_BOX(vbox), dbox, FALSE, FALSE, 0); - gaim_disclosure_set_container(GAIM_DISCLOSURE(disclosure), dbox); - - /* Custom status message treeview */ - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_IN); - gtk_box_pack_start(GTK_BOX(dbox), sw, TRUE, TRUE, 0); - gtk_widget_show(sw); - - /* Create the list model */ - dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS, - G_TYPE_BOOLEAN, - GDK_TYPE_PIXBUF, G_TYPE_STRING); - - /* Create the treeview */ - dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE); - gtk_widget_set_size_request(dialog->treeview, 400, 250); - gtk_container_add(GTK_CONTAINER(sw), dialog->treeview); - gtk_widget_show(dialog->treeview); - - /* Add columns */ - status_editor_add_columns(dialog); - - /* Populate list */ - status_editor_populate_list(dialog); - - /* Separator */ - sep = gtk_hseparator_new(); - gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); - gtk_widget_show(sep); - - /* Button box */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), 6); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - - /* Cancel button */ - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_editor_cancel_cb), dialog); - - /* Save button */ - /* TODO: This button needs to be made sensitive when the title box contains text */ - button = gtk_button_new_from_stock(GTK_STOCK_SAVE); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_editor_save_cb), dialog); - - gtk_widget_show(win); -} - -static gboolean -status_window_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) -{ - StatusWindow *dialog = user_data; - - dialog->window = NULL; - gaim_gtk_status_window_hide(); - - return FALSE; -} - -static void -status_window_add_cb(GtkButton *button, gpointer user_data) -{ - gaim_gtk_status_editor_show(); -} - -static void -status_window_modify_cb(GtkButton *button, gpointer user_data) -{ - /* TODO: Need to show a pre-populated status editor */ -} - -static void -status_window_delete_cb(GtkButton *button, gpointer user_data) -{ - /* TODO: Need to delete a status from the list */ -} - -static void -status_window_close_cb(GtkButton *button, gpointer user_data) -{ - gaim_gtk_status_window_hide(); -} - -/* - * TODO: This needs to use the user's saved statuses. - */ -static void -populate_saved_status_list(StatusWindow *dialog) -{ - GtkTreeIter iter; - const GList *saved_statuses; - GaimStatusSaved *saved_status; - - gtk_list_store_clear(dialog->model); - - for (saved_statuses = gaim_statuses_get_saved(); saved_statuses != NULL; - saved_statuses = g_list_next(saved_statuses)) - { - saved_status = (GaimStatusSaved *)saved_statuses->data; - gtk_list_store_append(dialog->model, &iter); - gtk_list_store_set(dialog->model, &iter, 0, - gaim_statuses_saved_get_title(saved_status), -1); - } -} - -static GtkWidget * -create_saved_status_list(StatusWindow *dialog) -{ - GtkWidget *sw; - GtkWidget *treeview; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - /* Create the scrolled window */ - sw = gtk_scrolled_window_new(0, 0); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_ALWAYS); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_IN); - gtk_widget_show(sw); - - /* Create the list model */ - dialog->model = gtk_list_store_new(1, G_TYPE_STRING); - - /* Create the treeview */ - treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); - dialog->treeview = treeview; - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); - - gtk_container_add(GTK_CONTAINER(sw), treeview); - gtk_widget_show(treeview); - - /* Add columns */ - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, _("Title")); - gtk_tree_view_column_set_resizable(column, TRUE); - gtk_tree_view_column_set_sort_column_id(column, 0); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "text", 0); - - /* Populate list */ - populate_saved_status_list(dialog); - - return sw; -} - -static gboolean -configure_cb(GtkWidget *widget, GdkEventConfigure *event, StatusWindow *dialog) -{ - if (GTK_WIDGET_VISIBLE(widget)) { - gaim_prefs_set_int("/gaim/gtk/status/dialog/width", event->width); - gaim_prefs_set_int("/gaim/gtk/status/dialog/height", event->height); - } - - return FALSE; -} - -void -gaim_gtk_status_window_show(void) -{ - StatusWindow *dialog; - GtkWidget *bbox; - GtkWidget *button; - GtkWidget *list; - GtkWidget *sep; - GtkWidget *vbox; - GtkWidget *win; - int width, height; - - if (status_window != NULL) { - gtk_window_present(GTK_WINDOW(status_window->window)); - return; - } - - status_window = dialog = g_new0(StatusWindow, 1); - - width = gaim_prefs_get_int("/gaim/gtk/status/dialog/width"); - height = gaim_prefs_get_int("/gaim/gtk/status/dialog/height"); - - dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW(win), width, height); - gtk_window_set_role(GTK_WINDOW(win), "statuses"); - gtk_window_set_title(GTK_WINDOW(win), _("Saved Statuses")); - gtk_container_set_border_width(GTK_CONTAINER(win), 12); - - g_signal_connect(G_OBJECT(win), "delete_event", - G_CALLBACK(status_window_destroy_cb), dialog); - g_signal_connect(G_OBJECT(win), "configure_event", - G_CALLBACK(configure_cb), dialog); - - /* Setup the vbox */ - vbox = gtk_vbox_new(FALSE, 12); - gtk_container_add(GTK_CONTAINER(win), vbox); - gtk_widget_show(vbox); - - /* List of saved status states */ - list = create_saved_status_list(dialog); - gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); - - /* Separator... */ - sep = gtk_hseparator_new(); - gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); - gtk_widget_show(sep); - - /* Button box. */ - bbox = gtk_hbutton_box_new(); - gtk_box_set_spacing(GTK_BOX(bbox), 6); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); - gtk_widget_show(bbox); - - /* Add button */ - button = gtk_button_new_from_stock(GTK_STOCK_ADD); - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_add_cb), dialog); - - /* Modify button */ - button = gtk_button_new_from_stock(GAIM_STOCK_MODIFY); - dialog->modify_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_modify_cb), dialog); - - /* Delete button */ - button = gtk_button_new_from_stock(GTK_STOCK_DELETE); - dialog->delete_button = button; - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_sensitive(button, FALSE); - gtk_widget_show(button); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_delete_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); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(status_window_close_cb), dialog); - - gtk_widget_show(win); -} - -void -gaim_gtk_status_window_hide(void) -{ - if (status_window == NULL) - return; - - if (status_window->window != NULL) - gtk_widget_destroy(status_window->window); - - g_free(status_window); - status_window = NULL; -} - -void * -gaim_gtk_status_get_handle() -{ - static int handle; - - return &handle; -} - -void -gaim_gtk_status_init(void) -{ - gaim_prefs_add_none("/gaim/gtk/status"); - gaim_prefs_add_none("/gaim/gtk/status/dialog"); - gaim_prefs_add_int("/gaim/gtk/status/dialog/width", 550); - gaim_prefs_add_int("/gaim/gtk/status/dialog/height", 250); -} - -void -gaim_gtk_status_uninit(void) -{ - gaim_gtk_status_window_hide(); -}
--- a/src/gtkstatus.h Sat Dec 25 20:24:17 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * @file gtkstatus.h GTK+ Status Editor UI - * @ingroup gtkui - * - * gaim - * - * Gaim is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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_GTKSTATUS_H_ -#define _GAIM_GTKSTATUS_H_ - -#include "status.h" - -/** - * Shows the status window. - */ -void gaim_gtk_status_window_show(void); - -/** - * Hides the status window. - */ -void gaim_gtk_status_window_hide(void); - -/** - * Returns the gtkstatus handle. - * - * @return The handle to the GTK+ status system. - */ -void *gaim_gtk_status_get_handle(void); - -/** - * Initializes the GTK+ status system. - */ -void gaim_gtk_status_init(void); - -/** - * Uninitializes the GTK+ status system. - */ -void gaim_gtk_status_uninit(void); - -#endif /* _GAIM_GTKSTATUS_H_ */
--- a/src/pounce.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/pounce.c Sun Dec 26 00:46:26 2004 +0000 @@ -178,13 +178,12 @@ GaimPounce *pounce; GList *l, *l_next; - g_return_if_fail(account != NULL); - + for (l = gaim_pounces_get_all(); l != NULL; l = l_next) { pounce = (GaimPounce *)l->data; l_next = l->next; - + pouncer = gaim_pounce_get_pouncer(pounce); if (pouncer == account) gaim_pounce_destroy(pounce);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/savedstatuses.c Sun Dec 26 00:46:26 2004 +0000 @@ -0,0 +1,569 @@ +/** + * @file savedstatus.c Saved Status API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 "internal.h" + +#include "debug.h" +#include "notify.h" +#include "savedstatuses.h" +#include "status.h" +#include "util.h" +#include "xmlnode.h" + +/** + * The information of a snap-shot of the statuses of all + * your accounts. Basically these are your saved away messages. + * There is an overall status and message that applies to + * all your accounts, and then each individual account can + * optionally have a different custom status and message. + * + * The changes to status.xml caused by the new status API + * are fully backward compatible. The new status API just + * adds the optional sub-statuses to the XML file. + */ +struct _GaimStatusSaved +{ + char *title; + GaimStatusPrimitive type; + char *message; + + GList *substatuses; /**< A list of GaimStatusSavedSub's. */ +}; + +/* + * TODO: If an account is deleted, need to also delete any associated + * GaimStatusSavedSub's. + * TODO: If a GaimStatusType is deleted, need to also delete any + * associated GaimStatusSavedSub's? + */ +struct _GaimStatusSavedSub +{ + GaimAccount *account; + const GaimStatusType *type; + char *message; +}; + +static GList *saved_statuses = NULL; +gboolean have_read_saved_statuses = FALSE; +static guint statuses_save_timer = 0; + +/************************************************************************** +* Helper functions +**************************************************************************/ + +/** + * Elements of this array correspond to the GaimStatusPrimitive + * enumeration. + */ +static const char *primitive_names[] = +{ + "unset", + "offline", + "available", + "unavailable", + "hidden", + "away", + "extended_away" +}; + +static GaimStatusPrimitive +gaim_primitive_get_type(const char *name) +{ + int i; + + g_return_val_if_fail(name != NULL, GAIM_STATUS_UNSET); + + for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) + { + if (!strcmp(name, primitive_names[i])) + return i; + } + + return GAIM_STATUS_UNSET; +} + +static void +free_statussavedsub(GaimStatusSavedSub *substatus) +{ + g_return_if_fail(substatus != NULL); + + g_free(substatus->message); + g_free(substatus); +} + +static void +free_statussaved(GaimStatusSaved *status) +{ + g_return_if_fail(status != NULL); + + g_free(status->title); + g_free(status->message); + + while (status->substatuses != NULL) + { + GaimStatusSavedSub *substatus = status->substatuses->data; + status->substatuses = g_list_remove(status->substatuses, substatus); + free_statussavedsub(substatus); + } + + g_free(status); +} + + +/************************************************************************** +* Saved status writting to disk +**************************************************************************/ + +static xmlnode * +substatus_to_xmlnode(GaimStatusSavedSub *substatus) +{ + xmlnode *node, *child; + + node = xmlnode_new("substatus"); + + child = xmlnode_new("account"); + xmlnode_set_attrib(node, "protocol", + gaim_account_get_protocol_id(substatus->account)); + xmlnode_insert_data(child, + gaim_account_get_username(substatus->account), -1); + xmlnode_insert_child(node, child); + + child = xmlnode_new("state"); + xmlnode_insert_data(child, gaim_status_type_get_id(substatus->type), -1); + xmlnode_insert_child(node, child); + + if (substatus->message != NULL) + { + child = xmlnode_new("message"); + xmlnode_insert_data(child, substatus->message, -1); + xmlnode_insert_child(node, child); + } + + return node; +} + +static xmlnode * +status_to_xmlnode(GaimStatusSaved *status) +{ + xmlnode *node, *child; + GList *cur; + + node = xmlnode_new("status"); + xmlnode_set_attrib(node, "name", status->title); + + child = xmlnode_new("state"); + xmlnode_insert_data(child, primitive_names[status->type], -1); + xmlnode_insert_child(node, child); + + child = xmlnode_new("message"); + xmlnode_insert_data(child, status->message, -1); + xmlnode_insert_child(node, child); + + for (cur = status->substatuses; cur != NULL; cur = cur->next) + { + child = substatus_to_xmlnode(cur->data); + xmlnode_insert_child(node, child); + } + + return node; +} + +static xmlnode * +statuses_to_xmlnode(void) +{ + xmlnode *node, *child; + GList *cur; + + node = xmlnode_new("statuses"); + xmlnode_set_attrib(node, "version", "1"); + + for (cur = saved_statuses; cur != NULL; cur = cur->next) + { + child = status_to_xmlnode(cur->data); + xmlnode_insert_child(node, child); + } + + return node; +} + +static void +sync_statuses(void) +{ + xmlnode *statuses; + char *data; + + if (!have_read_saved_statuses) { + gaim_debug_error("status", "Attempted to save statuses before they " + "were read!\n"); + return; + } + + statuses = statuses_to_xmlnode(); + data = xmlnode_to_formatted_str(statuses, NULL); + gaim_util_write_data_to_file("status.xml", data, -1); + g_free(data); + xmlnode_free(statuses); +} + +static gboolean +save_callback(gpointer data) +{ + sync_statuses(); + statuses_save_timer = 0; + return FALSE; +} + +static void +schedule_save(void) +{ + if (statuses_save_timer != 0) + gaim_timeout_remove(statuses_save_timer); + statuses_save_timer = gaim_timeout_add(1000, save_callback, NULL); +} + + +/************************************************************************** +* Saved status reading from disk +**************************************************************************/ +static GaimStatusSavedSub * +parse_substatus(xmlnode *substatus) +{ + GaimStatusSavedSub *ret; + xmlnode *node; + char *data = NULL; + + ret = g_new0(GaimStatusSavedSub, 1); + + /* Read the account */ + node = xmlnode_get_child(substatus, "account"); + if (node != NULL) + { + char *acct_name; + const char *protocol; + acct_name = xmlnode_get_data(node); + protocol = xmlnode_get_attrib(node, "protocol"); + if ((acct_name != NULL) && (protocol != NULL)) + ret->account = gaim_accounts_find(acct_name, protocol); + g_free(acct_name); + } + + if (ret->account == NULL) + { + g_free(ret); + return NULL; + } + + /* Read the state */ + node = xmlnode_get_child(substatus, "state"); + if (node != NULL) + data = xmlnode_get_data(node); + if (data != NULL) { + ret->type = gaim_status_type_find_with_id( + ret->account->status_types, data); + g_free(data); + data = NULL; + } + + /* Read the message */ + node = xmlnode_get_child(substatus, "message"); + if (node != NULL) + data = xmlnode_get_data(node); + if (data != NULL) + ret->message = data; + + return ret; +} + +/** + * Parse a saved status and add it to the saved_statuses linked list. + * + * Here's an example of the XML for a saved status: + * <status name="Girls"> + * <state>away</state> + * <message>I like the way that they walk + * And it's chill to hear them talk + * And I can always make them smile + * From White Castle to the Nile</message> + * <substatus> + * <account protocol='prpl-oscar'>markdoliner</account> + * <state>available</state> + * <message>The ladies man is here to answer your queries.</message> + * </substatus> + * <substatus> + * <account protocol='prpl-oscar'>giantgraypanda</account> + * <state>away</state> + * <message>A.C. ain't in charge no more.</message> + * </substatus> + * </status> + * + * I know. Moving, huh? + */ +static GaimStatusSaved * +parse_status(xmlnode *status) +{ + GaimStatusSaved *ret; + xmlnode *node; + const char *attrib; + char *data = NULL; + int i; + + ret = g_new0(GaimStatusSaved, 1); + + /* Read the title */ + attrib = xmlnode_get_attrib(status, "name"); + if (attrib == NULL) + attrib = "No Title"; + /* Ensure the title is unique */ + ret->title = g_strdup(attrib); + i = 2; + while (gaim_savedstatuses_find(ret->title) != NULL) + { + g_free(ret->title); + ret->title = g_strdup_printf("%s %d", attrib, i); + i++; + } + + /* Read the primitive status type */ + node = xmlnode_get_child(status, "state"); + if (node != NULL) + data = xmlnode_get_data(node); + if (data != NULL) { + ret->type = gaim_primitive_get_type(data); + g_free(data); + data = NULL; + } + + /* Read the message */ + node = xmlnode_get_child(status, "message"); + if (node != NULL) + data = xmlnode_get_data(node); + if (data != NULL) + ret->message = data; + + /* Read substatuses */ + for (node = xmlnode_get_child(status, "status"); node != NULL; + node = xmlnode_get_next_twin(node)) + { + GaimStatusSavedSub *new; + new = parse_substatus(node); + if (new != NULL) + ret->substatuses = g_list_append(ret->substatuses, new); + } + + return ret; +} + +/** + * Read the saved statuses from a file in the Gaim user dir. + * + * @return TRUE on success, FALSE on failure (if the file can not + * be opened, or if it contains invalid XML). + */ +static gboolean +read_statuses(const char *filename) +{ + GError *error; + gchar *contents = NULL; + gsize length; + xmlnode *statuses, *status; + + gaim_debug_info("status", "Reading %s\n", filename); + + if (!g_file_get_contents(filename, &contents, &length, &error)) + { + gaim_debug_error("status", "Error reading statuses: %s\n", + error->message); + g_error_free(error); + return FALSE; + } + + statuses = xmlnode_from_str(contents, length); + + if (statuses == NULL) + { + FILE *backup; + gchar *name; + gaim_debug_error("status", "Error parsing statuses\n"); + name = g_strdup_printf("%s~", filename); + if ((backup = fopen(name, "w"))) + { + fwrite(contents, length, 1, backup); + fclose(backup); + chmod(name, S_IRUSR | S_IWUSR); + } + else + { + gaim_debug_error("status", "Unable to write backup %s\n", name); + } + g_free(name); + g_free(contents); + return FALSE; + } + + g_free(contents); + + for (status = xmlnode_get_child(statuses, "status"); status != NULL; + status = xmlnode_get_next_twin(status)) + { + GaimStatusSaved *new; + new = parse_status(status); + saved_statuses = g_list_append(saved_statuses, new); + } + + gaim_debug_info("status", "Finished reading statuses\n"); + + xmlnode_free(statuses); + + return TRUE; +} + +static void +load_statuses(void) +{ + const char *user_dir = gaim_user_dir(); + gchar *filename; + gchar *msg; + + g_return_if_fail(user_dir != NULL); + + have_read_saved_statuses = TRUE; + + filename = g_build_filename(user_dir, "status.xml", NULL); + + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + { + if (!read_statuses(filename)) + { + msg = g_strdup_printf(_("An error was encountered parsing the " + "file containing your saved statuses (%s). They " + "have not been loaded, and the old file has been " + "renamed to status.xml~."), filename); + gaim_notify_error(NULL, NULL, _("Saved Statuses Error"), msg); + g_free(msg); + } + } + + g_free(filename); +} + + +/************************************************************************** +* Saved status API +**************************************************************************/ +GaimStatusSaved * +gaim_savedstatuses_new(const char *title, GaimStatusPrimitive type) +{ + GaimStatusSaved *status; + + status = g_new0(GaimStatusSaved, 1); + status->title = g_strdup(title); + status->type = type; + + saved_statuses = g_list_append(saved_statuses, status); + + schedule_save(); + + return status; +} + +gboolean +gaim_savedstatuses_delete(const char *title) +{ + GaimStatusSaved *status; + + status = gaim_savedstatuses_find(title); + + if (status == NULL) + return FALSE; + + saved_statuses = g_list_remove(saved_statuses, status); + free_statussaved(status); + + schedule_save(); + + return TRUE; +} + +const GList * +gaim_savedstatuses_get_all(void) +{ + return saved_statuses; +} + +GaimStatusSaved * +gaim_savedstatuses_find(const char *title) +{ + GList *l; + GaimStatusSaved *status; + + for (l = saved_statuses; l != NULL; l = g_list_next(l)) + { + status = (GaimStatusSaved *)l->data; + if (!strcmp(status->title, title)) + return status; + } + + return NULL; +} + +const char * +gaim_savedstatuses_get_title(const GaimStatusSaved *saved_status) +{ + return saved_status->title; +} + +GaimStatusPrimitive +gaim_savedstatuses_get_type(const GaimStatusSaved *saved_status) +{ + return saved_status->type; +} + +const char * +gaim_savedstatuses_get_message(const GaimStatusSaved *saved_status) +{ + return saved_status->message; +} + +void +gaim_savedstatuses_init(void) +{ + load_statuses(); +} + +void +gaim_savedstatuses_uninit(void) +{ + if (statuses_save_timer != 0) + { + gaim_timeout_remove(statuses_save_timer); + statuses_save_timer = 0; + sync_statuses(); + } + + while (saved_statuses != NULL) { + GaimStatusSaved *status = saved_statuses->data; + saved_statuses = g_list_remove(saved_statuses, status); + free_statussaved(status); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/savedstatuses.h Sun Dec 26 00:46:26 2004 +0000 @@ -0,0 +1,140 @@ +/** + * @file savedstatuses.h Saved Status API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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_SAVEDSTATUSES_H_ +#define _GAIM_SAVEDSTATUSES_H_ + +/** + * Saved statuses don't really interact much with the rest of Gaim. It + * could really be a plugin. It's just a list of away states. When + * a user chooses one of the saved states, their Gaim accounts are set + * to the settings of that state. + */ + +typedef struct _GaimStatusSaved GaimStatusSaved; +typedef struct _GaimStatusSavedSub GaimStatusSavedSub; + +/**************************************************************************/ +/** @name Saved status subsystem */ +/**************************************************************************/ +/*@{*/ + +/** + * Create a new saved status. This will add the saved status to the + * list of saved statuses and writes the revised list to status.xml. + * + * @param title The title of the saved status. This must be unique. + * @param type The type of saved status. + * + * @return The newly created saved status. + */ +GaimStatusSaved *gaim_savedstatuses_new(const char *title, GaimStatusPrimitive type); + +/** + * Delete a saved status. This removes the saved status from the list + * of saved statuses, and writes the revised list to status.xml. + * + * @param title The title of the saved status. + * + * @return TRUE if the status was successfully deleted. FALSE if the + * status could not be deleted because no saved status exists + * with the given title. + */ +gboolean gaim_savedstatuses_delete(const char *title); + +/** + * Returns all saved statuses. + * + * @return A list of saved statuses. + */ +const GList *gaim_savedstatuses_get_all(void); + +/** + * Finds a saved status with the specified title. + * + * @param title The name of the saved status. + * + * @return The saved status if found, or NULL. + */ +GaimStatusSaved *gaim_savedstatuses_find(const char *title); + +/** + * Create a new saved status and add it to the list + * of saved statuses. + * + * @param title The title for the new saved status. + * @param type The type of saved status. + * + * @return The newly created saved status. + */ +GaimStatusSaved *gaim_savedstatuses_new(const char *title, + GaimStatusPrimitive type); + +/** + * Return the name of a given saved status. + * + * @param saved_status The saved status. + * + * @return The title. + */ +const char *gaim_savedstatuses_get_title(const GaimStatusSaved *saved_status); + +/** + * Return the name of a given saved status. + * + * @param saved_status The saved status. + * + * @return The name. + */ +GaimStatusPrimitive gaim_savedstatuses_get_type(const GaimStatusSaved *saved_status); + +/** + * Return the name of a given saved status. + * + * @param saved_status The saved status. + * + * @return The name. + */ +const char *gaim_savedstatuses_get_message(const GaimStatusSaved *saved_status); + +/** + * Get the handle for the status subsystem. + * + * @return the handle to the status subsystem + */ +void *gaim_savedstatuses_get_handle(); + +/** + * Initializes the status subsystem. + */ +void gaim_savedstatuses_init(void); + +/** + * Uninitializes the status subsystem. + */ +void gaim_savedstatuses_uninit(void); + +/*@}*/ + +#endif /* _GAIM_SAVEDSTATUSES_H_ */
--- a/src/status.c Sat Dec 25 20:24:17 2004 +0000 +++ b/src/status.c Sun Dec 26 00:46:26 2004 +0000 @@ -30,8 +30,6 @@ #include "notify.h" #include "prefs.h" #include "status.h" -#include "util.h" -#include "xmlnode.h" /** * A type of status. @@ -123,33 +121,6 @@ char *name; } GaimStatusBuddyKey; -/** - * The information of a snap-shot of the statuses of all - * your accounts. Basically these are your saved away messages. - * There is an overall status and message that applies to - * all your accounts, and then each individual account can - * optionally have a different custom status and message. - * - * The changes to status.xml caused by the new status API - * are fully backward compatible. The new status API just - * adds the optional sub-statuses to the XML file. - */ -struct _GaimStatusSaved -{ - char *title; - GaimStatusPrimitive type; - char *message; - - GList *substatuses; /**< A list of GaimStatusSavedSub's. */ -}; - -struct _GaimStatusSavedSub -{ - GaimAccount *account; - const GaimStatusType *type; - char *message; -}; - static int primitive_scores[] = { 0, /* unset */ @@ -165,43 +136,10 @@ }; static GHashTable *buddy_presences = NULL; -static GList *saved_statuses = NULL; -gboolean have_read_saved_statuses = FALSE; #define SCORE_IDLE 5 #define SCORE_IDLE_TIME 6 -/** - * Elements of this array correspond to the GaimStatusPrimitive - * enumeration. - */ -static const char *primitive_names[] = -{ - "unset", - "offline", - "available", - "unavailable", - "hidden", - "away", - "extended_away" -}; - -static GaimStatusPrimitive -gaim_primitive_get_type(const char *name) -{ - int i; - - g_return_val_if_fail(name != NULL, GAIM_STATUS_UNSET); - - for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) - { - if (!strcmp(name, primitive_names[i])) - return i; - } - - return GAIM_STATUS_UNSET; -} - /************************************************************************** * GaimStatusType API **************************************************************************/ @@ -1700,57 +1638,17 @@ return FALSE; } -const GList * -gaim_statuses_get_saved(void) -{ - return saved_statuses; -} - -GaimStatusSaved * -gaim_statuses_find_saved(const char *title) -{ - GList *l; - GaimStatusSaved *status; - - for (l = saved_statuses; l != NULL; l = g_list_next(l)) - { - status = (GaimStatusSaved *)l->data; - if (!strcmp(status->title, title)) - return status; - } - - return NULL; -} - -const char * -gaim_statuses_saved_get_title(const GaimStatusSaved *saved_status) -{ - return saved_status->title; -} - -GaimStatusPrimitive -gaim_statuses_saved_get_type(const GaimStatusSaved *saved_status) -{ - return saved_status->type; -} - -const char * -gaim_statuses_saved_get_message(const GaimStatusSaved *saved_status) -{ - return saved_status->message; -} - void * -gaim_statuses_get_handle() { +gaim_status_get_handle(void) { static int handle; return &handle; } void -gaim_statuses_init(void) +gaim_status_init(void) { - void *handle = gaim_statuses_get_handle; + void *handle = gaim_status_get_handle; gaim_prefs_add_none("/core/status"); gaim_prefs_add_none("/core/status/scores"); @@ -1792,7 +1690,7 @@ } void -gaim_statuses_uninit(void) +gaim_status_uninit(void) { if (buddy_presences != NULL) { @@ -1801,313 +1699,3 @@ buddy_presences = NULL; } } - -static GaimStatusSavedSub * -gaim_statuses_read_parse_substatus(xmlnode *substatus) -{ - GaimStatusSavedSub *ret; - xmlnode *node; - char *data = NULL; - - ret = g_new0(GaimStatusSavedSub, 1); - - /* Read the account */ - node = xmlnode_get_child(substatus, "account"); - if (node != NULL) - { - char *acct_name; - const char *protocol; - acct_name = xmlnode_get_data(node); - protocol = xmlnode_get_attrib(node, "protocol"); - if ((acct_name != NULL) && (protocol != NULL)) - ret->account = gaim_accounts_find(acct_name, protocol); - g_free(acct_name); - } - - if (ret->account == NULL) - { - g_free(ret); - return NULL; - } - - /* Read the state */ - node = xmlnode_get_child(substatus, "state"); - if (node != NULL) - data = xmlnode_get_data(node); - if (data != NULL) { - ret->type = gaim_status_type_find_with_id(ret->account->status_types, - data); - g_free(data); - data = NULL; - } - - /* Read the message */ - node = xmlnode_get_child(substatus, "message"); - if (node != NULL) - data = xmlnode_get_data(node); - if (data != NULL) - ret->message = data; - - return ret; -} - -/** - * Parse a saved status and add it to the saved_statuses linked list. - * - * Here's an example of the XML for a saved status: - * <status name="Girls"> - * <state>away</state> - * <message>I like the way that they walk - * And it's chill to hear them talk - * And I can always make them smile - * From White Castle to the Nile</message> - * <substatus> - * <account protocol='prpl-oscar'>markdoliner</account> - * <state>available</state> - * <message>The ladies man is here to answer your queries.</message> - * </substatus> - * <substatus> - * <account protocol='prpl-oscar'>giantgraypanda</account> - * <state>away</state> - * <message>A.C. ain't in charge no more.</message> - * </substatus> - * </status> - * - * I know. Moving, huh? - */ -static GaimStatusSaved * -gaim_statuses_read_parse_status(xmlnode *status) -{ - GaimStatusSaved *ret; - xmlnode *node; - const char *attrib; - char *data = NULL; - int i; - - ret = g_new0(GaimStatusSaved, 1); - - /* Read the title */ - attrib = xmlnode_get_attrib(status, "name"); - if (attrib == NULL) - attrib = "No Title"; - /* Ensure the title is unique */ - ret->title = g_strdup(attrib); - i = 2; - while (gaim_statuses_find_saved(ret->title) != NULL) - { - g_free(ret->title); - ret->title = g_strdup_printf("%s %d", attrib, i); - i++; - } - - /* Read the primitive status type */ - node = xmlnode_get_child(status, "state"); - if (node != NULL) - data = xmlnode_get_data(node); - if (data != NULL) { - ret->type = gaim_primitive_get_type(data); - g_free(data); - data = NULL; - } - - /* Read the message */ - node = xmlnode_get_child(status, "message"); - if (node != NULL) - data = xmlnode_get_data(node); - if (data != NULL) - ret->message = data; - - /* Read substatuses */ - for (node = xmlnode_get_child(status, "status"); node != NULL; - node = xmlnode_get_next_twin(node)) - { - GaimStatusSavedSub *new; - new = gaim_statuses_read_parse_substatus(node); - if (new != NULL) - ret->substatuses = g_list_append(ret->substatuses, new); - } - - return ret; -} - -/** - * @return TRUE on success, FALSE on failure (if the file can not - * be opened, or if it contains invalid XML). - */ -static gboolean -gaim_statuses_read(const char *filename) -{ - GError *error; - gchar *contents = NULL; - gsize length; - xmlnode *statuses, *status; - - gaim_debug_info("status", "Reading %s\n", filename); - - if (!g_file_get_contents(filename, &contents, &length, &error)) - { - gaim_debug_error("status", "Error reading statuses: %s\n", - error->message); - g_error_free(error); - return FALSE; - } - - statuses = xmlnode_from_str(contents, length); - - if (statuses == NULL) - { - FILE *backup; - gchar *name; - gaim_debug_error("status", "Error parsing statuses\n"); - name = g_strdup_printf("%s~", filename); - if ((backup = fopen(name, "w"))) - { - fwrite(contents, length, 1, backup); - fclose(backup); - chmod(name, S_IRUSR | S_IWUSR); - } - else - { - gaim_debug_error("status", "Unable to write backup %s\n", name); - } - g_free(name); - g_free(contents); - return FALSE; - } - - g_free(contents); - - for (status = xmlnode_get_child(statuses, "status"); status != NULL; - status = xmlnode_get_next_twin(status)) - { - GaimStatusSaved *new; - new = gaim_statuses_read_parse_status(status); - saved_statuses = g_list_append(saved_statuses, new); - } - - gaim_debug_info("status", "Finished reading statuses\n"); - - xmlnode_free(statuses); - - return TRUE; -} - -void -gaim_statuses_load(void) -{ - const char *user_dir = gaim_user_dir(); - gchar *filename; - gchar *msg; - - g_return_if_fail(user_dir != NULL); - - have_read_saved_statuses = TRUE; - - filename = g_build_filename(user_dir, "status.xml", NULL); - - if (g_file_test(filename, G_FILE_TEST_EXISTS)) - { - if (!gaim_statuses_read(filename)) - { - msg = g_strdup_printf(_("An error was encountered parsing the " - "file containing your saved statuses (%s). They " - "have not been loaded, and the old file has been " - "renamed to status.xml~."), filename); - gaim_notify_error(NULL, NULL, _("Saved Statuses Error"), msg); - g_free(msg); - } - } - - g_free(filename); -} - -static xmlnode * -gaim_substatus_get_as_xmlnode(GaimStatusSavedSub *substatus) -{ - xmlnode *node, *child; - - node = xmlnode_new("substatus"); - - child = xmlnode_new("account"); - xmlnode_set_attrib(node, "protocol", - gaim_account_get_protocol_id(substatus->account)); - xmlnode_insert_data(child, - gaim_account_get_username(substatus->account), -1); - xmlnode_insert_child(node, child); - - child = xmlnode_new("state"); - xmlnode_insert_data(child, substatus->type->id, -1); - xmlnode_insert_child(node, child); - - if (substatus->message != NULL) - { - child = xmlnode_new("message"); - xmlnode_insert_data(child, substatus->message, -1); - xmlnode_insert_child(node, child); - } - - return node; -} - -static xmlnode * -gaim_status_get_as_xmlnode(GaimStatusSaved *status) -{ - xmlnode *node, *child; - GList *cur; - - node = xmlnode_new("status"); - xmlnode_set_attrib(node, "name", status->title); - - child = xmlnode_new("state"); - xmlnode_insert_data(child, primitive_names[status->type], -1); - xmlnode_insert_child(node, child); - - child = xmlnode_new("message"); - xmlnode_insert_data(child, status->message, -1); - xmlnode_insert_child(node, child); - - for (cur = status->substatuses; cur != NULL; cur = cur->next) - { - child = gaim_substatus_get_as_xmlnode(cur->data); - xmlnode_insert_child(node, child); - } - - return node; -} - -static xmlnode * -gaim_statuses_get_as_xmlnode() -{ - xmlnode *node, *child; - GList *cur; - - node = xmlnode_new("statuses"); - xmlnode_set_attrib(node, "version", "1"); - - for (cur = saved_statuses; cur != NULL; cur = cur->next) - { - child = gaim_status_get_as_xmlnode(cur->data); - xmlnode_insert_child(node, child); - } - - return node; -} - -void -gaim_statuses_sync(void) -{ - xmlnode *statuses; - char *data; - - if (!have_read_saved_statuses) { - gaim_debug_error("status", "Attempted to save statuses before they " - "were read!\n"); - return; - } - - statuses = gaim_statuses_get_as_xmlnode(); - data = xmlnode_to_formatted_str(statuses, NULL); - gaim_util_write_data_to_file("status.xml", data, -1); - g_free(data); - xmlnode_free(statuses); -}
--- a/src/status.h Sat Dec 25 20:24:17 2004 +0000 +++ b/src/status.h Sun Dec 26 00:46:26 2004 +0000 @@ -29,8 +29,6 @@ typedef struct _GaimStatusAttr GaimStatusAttr; typedef struct _GaimPresence GaimPresence; typedef struct _GaimStatus GaimStatus; -typedef struct _GaimStatusSavedSub GaimStatusSavedSub; -typedef struct _GaimStatusSaved GaimStatusSaved; /** * A context for a presence. @@ -900,74 +898,21 @@ /*@{*/ /** - * Returns all saved statuses. - * - * @return A list of saved statuses. - */ -const GList *gaim_statuses_get_saved(void); - -/** - * Finds a saved status with the specified title. - * - * @param title The name of the saved status. - * - * @return The saved status if found, or NULL. - */ -GaimStatusSaved *gaim_statuses_find_saved(const char *title); - -/** - * Return the name of a given saved status. - * - * @param saved_status The saved status. - * - * @return The title. - */ -const char *gaim_statuses_saved_get_title(const GaimStatusSaved *saved_status); - -/** - * Return the name of a given saved status. - * - * @param saved_status The saved status. - * - * @return The name. - */ -GaimStatusPrimitive gaim_statuses_saved_get_type(const GaimStatusSaved *saved_status); - -/** - * Return the name of a given saved status. - * - * @param saved_status The saved status. - * - * @return The name. - */ -const char *gaim_statuses_saved_get_message(const GaimStatusSaved *saved_status); - -/** * Get the handle for the status subsystem. * * @return the handle to the status subsystem */ -void *gaim_statuses_get_handle(); +void *gaim_status_get_handle(); /** * Initializes the status subsystem. */ -void gaim_statuses_init(void); +void gaim_status_init(void); /** * Uninitializes the status subsystem. */ -void gaim_statuses_uninit(void); - -/** - * Syncs status information from a file. - */ -void gaim_statuses_load(void); - -/** - * Syncs status information to the file. - */ -void gaim_statuses_sync(void); +void gaim_status_uninit(void); /*@}*/