# HG changeset patch # User Mark Doliner # Date 1131952822 0 # Node ID 3b52d94437f382c07792f1a96a0441232310d5b9 # Parent 15732b01ea3d48bd9a1fda0b0dde5dd885dcf49e [gaim-migrate @ 14377] The rest of sf patch #1354886, from Sadrul Habib Chowdhury, with an EXTREME amount of changes from me. Come to me, first, if something doesn't work. This allows you to edit the substatuses of a saved status. committer: Tailor Script diff -r 15732b01ea3d -r 3b52d94437f3 src/core.c --- a/src/core.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/core.c Mon Nov 14 07:20:22 2005 +0000 @@ -106,9 +106,9 @@ gaim_plugins_init(); gaim_plugins_probe(G_MODULE_SUFFIX); - gaim_savedstatuses_init(); gaim_status_init(); gaim_accounts_init(); + gaim_savedstatuses_init(); gaim_ciphers_init(); gaim_connections_init(); gaim_conversations_init(); diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkblist.c --- a/src/gtkblist.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkblist.c Mon Nov 14 07:20:22 2005 +0000 @@ -4841,49 +4841,6 @@ } /********************************************************************* - * Public utility functions * - *********************************************************************/ - -GdkPixbuf * -gaim_gtk_create_prpl_icon(GaimAccount *account) -{ - GaimPlugin *prpl; - GaimPluginProtocolInfo *prpl_info = NULL; - GdkPixbuf *status = NULL; - char *filename = NULL; - const char *protoname = NULL; - char buf[256]; - - g_return_val_if_fail(account != NULL, NULL); - - prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); - - if (prpl != NULL) { - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->list_icon != NULL) - protoname = prpl_info->list_icon(account, NULL); - } - - if (protoname == NULL) - return NULL; - - /* - * Status icons will be themeable too, and then it will look up - * protoname from the theme - */ - g_snprintf(buf, sizeof(buf), "%s.png", protoname); - - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", - "default", buf, NULL); - status = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - - return status; -} - - -/********************************************************************* * Buddy List sorting functions * *********************************************************************/ diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkblist.h --- a/src/gtkblist.h Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkblist.h Mon Nov 14 07:20:22 2005 +0000 @@ -138,15 +138,6 @@ GaimGtkBuddyList *gaim_gtk_blist_get_default_gtk_blist(); /** - * Returns the base image to represent the account, based on the currently selected theme - * - * @param account The account. - * - * @return The icon - */ -GdkPixbuf *gaim_gtk_create_prpl_icon(GaimAccount *account); - -/** * Populates a menu with the items shown on the buddy list for a buddy. * * @param menu The menu to populate diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkconv.c --- a/src/gtkconv.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkconv.c Mon Nov 14 07:20:22 2005 +0000 @@ -6399,9 +6399,12 @@ void gaim_gtk_conv_window_raise(GaimGtkWindow *win) { + gtk_window_present(win->window); +/* gtk_widget_show(win->window); gtk_window_deiconify(GTK_WINDOW(win->window)); gdk_window_raise(win->window->window); +*/ } void diff -r 15732b01ea3d -r 3b52d94437f3 src/gtksavedstatuses.c --- a/src/gtksavedstatuses.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtksavedstatuses.c Mon Nov 14 07:20:22 2005 +0000 @@ -40,25 +40,59 @@ #include "gtkstock.h" #include "gtkutils.h" +/* + * TODO: Should attach to the account-deleted and account-added signals + * and update the GtkListStores in any StatusEditor windows that + * may be open. + */ + +/** + * These are used for the GtkTreeView when you're scrolling through + * all your saved statuses. + */ enum { - /* A hidden column containing a pointer to the GaimStatusType */ - STATUS_WINDOW_COLUMN_STATUS, - STATUS_WINDOW_COLUMN_TITLE, STATUS_WINDOW_COLUMN_TYPE, STATUS_WINDOW_COLUMN_MESSAGE, STATUS_WINDOW_NUM_COLUMNS }; +/** + * These is used for the GtkTreeView containing the list of accounts + * at the bottom of the window when you're editing a particular + * saved status. + */ enum { - STATUS_EDITOR_COLUMN_CUSTOM_STATUS, + /** A hidden column containing a pointer to the GaimAccount */ + STATUS_EDITOR_COLUMN_ACCOUNT, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, STATUS_EDITOR_COLUMN_ICON, STATUS_EDITOR_COLUMN_SCREENNAME, + /** A hidden column containing the ID of this GaimStatusType. */ + STATUS_EDITOR_COLUMN_STATUS_ID, + STATUS_EDITOR_COLUMN_STATUS_NAME, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, STATUS_EDITOR_NUM_COLUMNS }; +/** + * These are used in the GtkComboBox to select the specific + * GaimStatusType when setting a substatus for a particular saved + * status. + */ +enum +{ + /** A hidden column containing a pointer to the GaimAccount. */ + SUBSTATUS_COLUMN_ACCOUNT, + SUBSTATUS_COLUMN_ICON, + /** A hidden column containing the ID of this GaimStatusType. */ + SUBSTATUS_COLUMN_STATUS_ID, + SUBSTATUS_COLUMN_STATUS_NAME, + SUBSTATUS_NUM_COLUMNS +}; + typedef struct { GtkWidget *window; @@ -82,6 +116,18 @@ GtkIMHtml *message; } StatusEditor; +typedef struct +{ + StatusEditor *status_editor; + GaimAccount *account; + + GtkWidget *window; + GtkListStore *model; + GtkComboBox *box; + GtkIMHtml *message; + GtkIMHtmlToolbar *toolbar; +} SubStatusEditor; + static StatusWindow *status_window = NULL; @@ -153,7 +199,6 @@ { GtkTreeSelection *selection; GtkTreeIter iter; - GaimSavedStatus *saved_status; GList *list = NULL; int num_selected = 0; @@ -180,10 +225,13 @@ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, list->data)) { + gchar *title; + GaimSavedStatus *saved_status; gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, - STATUS_WINDOW_COLUMN_STATUS, &saved_status, + STATUS_WINDOW_COLUMN_TITLE, &title, -1); - + saved_status = gaim_savedstatus_find(title); + g_free(title); gaim_savedstatus_activate(saved_status); } @@ -201,10 +249,13 @@ status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { - GaimSavedStatus *status; + gchar *title; + GaimSavedStatus *saved_status; - gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_STATUS, &status, -1); - gaim_gtk_status_editor_show(status); + gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); + saved_status = gaim_savedstatus_find(title); + g_free(title); + gaim_gtk_status_editor_show(saved_status); } static void @@ -300,7 +351,6 @@ gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, - STATUS_WINDOW_COLUMN_STATUS, saved_status, STATUS_WINDOW_COLUMN_TITLE, title, STATUS_WINDOW_COLUMN_TYPE, type, STATUS_WINDOW_COLUMN_MESSAGE, message, @@ -357,7 +407,6 @@ /* Create the list model */ dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS, - G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); @@ -587,7 +636,9 @@ const char *title; GaimStatusPrimitive type; char *message, *unformatted; - GaimSavedStatus *status; + GaimSavedStatus *saved_status = NULL; + GtkTreeModel *model; + GtkTreeIter iter; title = gtk_entry_get_text(dialog->title); @@ -608,14 +659,15 @@ unformatted = gaim_markup_strip_html(message); /* - * If we're editing an existing status, remove the old one to - * make way for the modified one. + * If we're editing an old status, then lookup the old status (it's + * possible that it has been deleted or renamed or something, and + * no longer exists). */ if (dialog->original_title != NULL) { GtkTreeIter iter; - gaim_savedstatus_delete(dialog->original_title); + saved_status = gaim_savedstatus_find(dialog->original_title); if (status_window_find_savedstatus(&iter, dialog->original_title)) { @@ -623,9 +675,64 @@ } } - status = gaim_savedstatus_new(title, type); + if (saved_status == NULL) + { + /* This is a new status */ + saved_status = gaim_savedstatus_new(title, type); + } + else + { + /* Modify the old status */ + if (strcmp(title, dialog->original_title)) + gaim_savedstatus_set_title(saved_status, title); + gaim_savedstatus_set_type(saved_status, type); + } + if (*unformatted != '\0') - gaim_savedstatus_set_message(status, message); + gaim_savedstatus_set_message(saved_status, message); + + /* Set any substatuses */ + model = GTK_TREE_MODEL(dialog->model); + if (gtk_tree_model_get_iter_first(model, &iter)) + { + GaimAccount *account; + gboolean enabled; + char *id; + char *message; + GaimStatusType *type; + + gtk_tree_model_get(model, &iter, + STATUS_EDITOR_COLUMN_ACCOUNT, &account, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled, + STATUS_EDITOR_COLUMN_STATUS_ID, &id, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, + -1); + if (enabled) + { + type = gaim_account_get_status_type(account, id); + gaim_savedstatus_set_substatus(saved_status, account, type, message); + } + g_free(id); + g_free(message); + + while (gtk_tree_model_iter_next(model, &iter)) + { + gtk_tree_model_get(model, &iter, + STATUS_EDITOR_COLUMN_ACCOUNT, &account, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled, + STATUS_EDITOR_COLUMN_STATUS_ID, &id, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, + -1); + if (enabled) + { + type = gaim_account_get_status_type(account, id); + gaim_savedstatus_set_substatus(saved_status, account, type, message); + } + g_free(id); + g_free(message); + } + } + g_free(message); g_free(unformatted); @@ -633,15 +740,8 @@ g_free(dialog->original_title); g_free(dialog); - add_status_to_saved_status_list(status_window->model, status); -} - -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 */ + if (status_window != NULL) + add_status_to_saved_status_list(status_window->model, saved_status); } static void @@ -680,22 +780,75 @@ return dropdown; } +static void edit_substatus(StatusEditor *status_editor, GaimAccount *account); + +static void +edit_substatus_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data) +{ + StatusEditor *dialog = user_data; + GtkTreeIter iter; + GaimAccount *account; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, + STATUS_EDITOR_COLUMN_ACCOUNT, &account, + -1); + + edit_substatus(dialog, account); +} + +static void +status_editor_substatus_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) +{ + StatusEditor *dialog = (StatusEditor *)data; + GtkTreeIter iter; + gboolean enabled; + GaimAccount *account; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->model), &iter, path_str); + gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, + STATUS_EDITOR_COLUMN_ACCOUNT, &account, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled, + -1); + + enabled = !enabled; + + if (enabled) + { + edit_substatus(dialog, account); + } + else + { + GaimSavedStatus *saved_status; + + /* Remove the substatus */ + saved_status = gaim_savedstatus_find(dialog->original_title); + gaim_savedstatus_unset_substatus(saved_status, account); + gtk_list_store_set(dialog->model, &iter, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, enabled, + STATUS_EDITOR_COLUMN_STATUS_ID, NULL, + STATUS_EDITOR_COLUMN_STATUS_NAME, NULL, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, NULL, + -1); + } +} + static void status_editor_add_columns(StatusEditor *dialog) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; - /* Custom status column */ + /* Enable Different status column */ renderer = gtk_cell_renderer_toggle_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview), - -1, _("Custom status"), + -1, _("Different"), renderer, - "active", STATUS_EDITOR_COLUMN_CUSTOM_STATUS, + "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, 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); + G_CALLBACK(status_editor_substatus_cb), dialog); /* Screen Name column */ column = gtk_tree_view_column_new(); @@ -715,13 +868,40 @@ gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", STATUS_EDITOR_COLUMN_SCREENNAME); + + /* Status column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_title(column, _("Status")); + gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + 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_STATUS_NAME); + + /* Message column */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_title(column, _("Message")); + gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); + gtk_tree_view_column_set_resizable(column, TRUE); + 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_STATUS_MESSAGE); + + g_signal_connect(G_OBJECT(dialog->treeview), "row-activated", + G_CALLBACK(edit_substatus_cb), dialog); } static void -status_editor_set_account(GtkListStore *store, GaimAccount *account, GtkTreeIter *iter) +status_editor_set_account(GtkListStore *store, GaimAccount *account, + GtkTreeIter *iter, GaimSavedStatusSub *substatus) { GdkPixbuf *pixbuf; GdkPixbuf *scale; + const char *id = NULL, *name = NULL, *message = NULL; scale = NULL; @@ -735,9 +915,25 @@ gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); } + if (substatus != NULL) + { + const GaimStatusType *type; + + type = gaim_savedstatus_substatus_get_type(substatus); + id = gaim_status_type_get_id(type); + name = gaim_status_type_get_name(type); + if (gaim_status_type_get_attr(type, "message")) + message = gaim_savedstatus_substatus_get_message(substatus); + } + gtk_list_store_set(store, iter, + STATUS_EDITOR_COLUMN_ACCOUNT, account, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, (substatus != NULL), STATUS_EDITOR_COLUMN_ICON, scale, STATUS_EDITOR_COLUMN_SCREENNAME, gaim_account_get_username(account), + STATUS_EDITOR_COLUMN_STATUS_ID, id, + STATUS_EDITOR_COLUMN_STATUS_NAME, name, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message, -1); if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); @@ -745,28 +941,39 @@ } static void -status_editor_add_account(StatusEditor *dialog, GaimAccount *account) +status_editor_add_account(StatusEditor *dialog, GaimAccount *account, + GaimSavedStatusSub *substatus) { GtkTreeIter iter; gtk_list_store_append(dialog->model, &iter); - status_editor_set_account(dialog->model, account, &iter); + status_editor_set_account(dialog->model, account, &iter, substatus); } static void -status_editor_populate_list(StatusEditor *dialog) +status_editor_populate_list(StatusEditor *dialog, GaimSavedStatus *saved_status) { - GList *l; + GList *iter; + GaimSavedStatusSub *substatus; 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); + for (iter = gaim_accounts_get_all(); iter != NULL; iter = iter->next) + { + GaimAccount *account = (GaimAccount *)iter->data; + + if (saved_status != NULL) + substatus = gaim_savedstatus_get_substatus(saved_status, account); + else + substatus = NULL; + + status_editor_add_account(dialog, account, substatus); + } } void -gaim_gtk_status_editor_show(GaimSavedStatus *status) +gaim_gtk_status_editor_show(GaimSavedStatus *saved_status) { StatusEditor *dialog; GtkSizeGroup *sg; @@ -787,8 +994,8 @@ dialog = g_new0(StatusEditor, 1); - if (status != NULL) - dialog->original_title = g_strdup(gaim_savedstatus_get_title(status)); + if (saved_status != NULL) + dialog->original_title = g_strdup(gaim_savedstatus_get_title(saved_status)); dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_role(GTK_WINDOW(win), "status"); @@ -819,7 +1026,7 @@ entry = gtk_entry_new(); dialog->title = GTK_ENTRY(entry); - if (status != NULL) + if (dialog->original_title != NULL) gtk_entry_set_text(GTK_ENTRY(entry), dialog->original_title); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); gtk_widget_show(entry); @@ -837,8 +1044,8 @@ gtk_widget_show(label); gtk_size_group_add_widget(sg, label); - if (status != NULL) - dropdown = create_status_type_menu(gaim_savedstatus_get_type(status)); + if (saved_status != NULL) + dropdown = create_status_type_menu(gaim_savedstatus_get_type(saved_status)); else dropdown = create_status_type_menu(GAIM_STATUS_AWAY); dialog->type = GTK_OPTION_MENU(dropdown); @@ -861,11 +1068,11 @@ gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); - if ((status != NULL) && (gaim_savedstatus_get_message(status) != NULL)) + if ((saved_status != NULL) && (gaim_savedstatus_get_message(saved_status) != NULL)) gtk_imhtml_append_text(GTK_IMHTML(text), - gaim_savedstatus_get_message(status), 0); + gaim_savedstatus_get_message(saved_status), 0); - /* Custom status message expander */ + /* Different status message expander */ expander = gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts")); gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0); gtk_widget_show(expander); @@ -875,7 +1082,7 @@ gtk_container_add(GTK_CONTAINER(expander), dbox); gtk_widget_show(dbox); - /* Custom status message treeview */ + /* Different 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); @@ -886,8 +1093,13 @@ /* Create the list model */ dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS, + G_TYPE_POINTER, G_TYPE_BOOLEAN, - GDK_TYPE_PIXBUF, G_TYPE_STRING); + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); /* Create the treeview */ dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); @@ -900,7 +1112,7 @@ status_editor_add_columns(dialog); /* Populate list */ - status_editor_populate_list(dialog); + status_editor_populate_list(dialog, saved_status); /* Button box */ bbox = gtk_hbutton_box_new(); @@ -921,6 +1133,8 @@ button = gtk_button_new_from_stock(GTK_STOCK_SAVE); dialog->save_button = button; gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + if (dialog->title == NULL) + gtk_widget_set_sensitive(button, FALSE); gtk_widget_show(button); g_signal_connect(G_OBJECT(button), "clicked", @@ -931,6 +1145,305 @@ /************************************************************************** +* SubStatus editor +**************************************************************************/ + +static void +substatus_selection_changed_cb(GtkComboBox *box, gpointer user_data) +{ + SubStatusEditor *select = user_data; + GtkTreeIter iter; + GaimAccount *account; + char *id; + GaimStatusType *type; + + if (!gtk_combo_box_get_active_iter(box, &iter)) + return; + gtk_tree_model_get(GTK_TREE_MODEL(select->model), &iter, + SUBSTATUS_COLUMN_ACCOUNT, &account, + SUBSTATUS_COLUMN_STATUS_ID, &id, + -1); + type = gaim_account_get_status_type(account, id); + g_free(id); + + if (gaim_status_type_get_attr(type, "message") == NULL) + { + gtk_widget_set_sensitive(GTK_WIDGET(select->message), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), FALSE); + } + else + { + gtk_widget_set_sensitive(GTK_WIDGET(select->message), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), TRUE); + } +} + +static gboolean +substatus_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + SubStatusEditor *dialog = user_data; + + g_free(dialog); + + return FALSE; +} + +void +substatus_editor_cancel_cb(GtkButton *button, gpointer user_data) +{ + SubStatusEditor *dialog = user_data; + + gtk_widget_destroy(dialog->window); + + g_free(dialog); +} + +static gboolean +status_editor_find_account_in_treemodel(GtkTreeIter *iter, + StatusEditor *status_editor, + GaimAccount *account) +{ + GtkTreeModel *model; + GaimAccount *cur; + + g_return_val_if_fail(status_editor != NULL, FALSE); + g_return_val_if_fail(account != NULL, FALSE); + + model = GTK_TREE_MODEL(status_editor->model); + + if (!gtk_tree_model_get_iter_first(model, iter)) + return FALSE; + + gtk_tree_model_get(model, iter, STATUS_EDITOR_COLUMN_ACCOUNT, &cur, -1); + if (cur == account) + return TRUE; + + while (gtk_tree_model_iter_next(model, iter)) + { + gtk_tree_model_get(model, iter, STATUS_EDITOR_COLUMN_ACCOUNT, &cur, -1); + if (cur == account) + return TRUE; + } + + return FALSE; +} + +void +substatus_editor_ok_cb(GtkButton *button, gpointer user_data) +{ + SubStatusEditor *dialog = user_data; + StatusEditor *status_editor; + GtkTreeIter iter; + GaimAccount *account; + GaimStatusType *type; + char *id = NULL; + char *message = NULL; + const char *name = NULL; + + gtk_combo_box_get_active_iter(dialog->box, &iter); + gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, + SUBSTATUS_COLUMN_ACCOUNT, &account, + SUBSTATUS_COLUMN_STATUS_ID, &id, + -1); + type = gaim_account_get_status_type(account, id); + if (gaim_status_type_get_attr(type, "message") != NULL) + message = gtk_imhtml_get_text(GTK_IMHTML(dialog->message), NULL, NULL); + name = gaim_status_type_get_name(type); + + status_editor = dialog->status_editor; + + if (status_editor_find_account_in_treemodel(&iter, status_editor, account)) + { + gtk_list_store_set(status_editor->model, &iter, + STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, TRUE, + STATUS_EDITOR_COLUMN_STATUS_ID, id, + STATUS_EDITOR_COLUMN_STATUS_NAME, name, + STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message, + -1); + } + + gtk_widget_destroy(dialog->window); + g_free(id); + g_free(message); + g_free(dialog); +} + +static void +edit_substatus(StatusEditor *status_editor, GaimAccount *account) +{ + char *tmp; + SubStatusEditor *dialog; + GtkSizeGroup *sg; + GtkWidget *bbox; + GtkWidget *button; + GtkWidget *combo; + GtkWidget *hbox; + GtkWidget *frame; + GtkWidget *label; + GtkWidget *text; + GtkWidget *toolbar; + GtkWidget *vbox; + GtkWidget *win; + GtkTreeIter iter; + GtkCellRenderer *rend; + const char *id = NULL; + const GList *list; + gboolean select = FALSE; + + g_return_if_fail(status_editor != NULL); + g_return_if_fail(account != NULL); + + dialog = g_new0(SubStatusEditor, 1); + dialog->status_editor = status_editor; + + dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(win), "substatus"); + tmp = g_strdup_printf(_("Status for %s"), gaim_account_get_username(account)); + gtk_window_set_title(GTK_WINDOW(win), tmp); + g_free(tmp); + gtk_window_set_resizable(GTK_WINDOW(win), FALSE); + gtk_container_set_border_width(GTK_CONTAINER(win), GAIM_HIG_BORDER); + + g_signal_connect(G_OBJECT(win), "delete_event", + G_CALLBACK(substatus_editor_destroy_cb), dialog); + + /* Setup the vbox */ + vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(win), vbox); + gtk_widget_show(vbox); + + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* Status type */ + hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); + 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); + + dialog->model = gtk_list_store_new(SUBSTATUS_NUM_COLUMNS, + G_TYPE_POINTER, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING); + combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog->model)); + dialog->box = GTK_COMBO_BOX(combo); + + rend = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new()); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, + "pixbuf", SUBSTATUS_COLUMN_ICON, NULL); + + rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, + "text", SUBSTATUS_COLUMN_STATUS_NAME, NULL); + + g_signal_connect(G_OBJECT(combo), "changed", + G_CALLBACK(substatus_selection_changed_cb), dialog); + + gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); + gtk_widget_show(combo); + + /* Status mesage */ + hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); + 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); + dialog->message = GTK_IMHTML(text); + dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar); + gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); + gtk_widget_show(frame); + + /* Button box */ + bbox = gtk_hbutton_box_new(); + gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE); + 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(substatus_editor_cancel_cb), dialog); + + /* OK button */ + button = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(substatus_editor_ok_cb), dialog); + + /* Seed the input widgets with the current values */ + /* TODO: Get the current values from our parent's list store, not the saved_status! */ + if (status_editor->original_title != NULL) + { + GaimSavedStatus *saved_status = NULL; + GaimSavedStatusSub *substatus = NULL; + + saved_status = gaim_savedstatus_find(status_editor->original_title); + if (saved_status != NULL) + substatus = gaim_savedstatus_get_substatus(saved_status, account); + + if (substatus != NULL) + { + gtk_imhtml_append_text(dialog->message, + gaim_savedstatus_substatus_get_message(substatus), + 0); + id = gaim_status_type_get_id(gaim_savedstatus_substatus_get_type(substatus)); + } + } + + for (list = gaim_account_get_status_types(account); list; list = list->next) + { + GaimStatusType *status_type; + GdkPixbuf *pixbuf, *scale = NULL; + const char *id, *name; + + status_type = list->data; + id = gaim_status_type_get_id(status_type); + pixbuf = gaim_gtk_create_prpl_icon_with_status(account, status_type); + if (pixbuf != NULL) + scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); + name = gaim_status_type_get_name(status_type); + + gtk_list_store_append(dialog->model, &iter); + gtk_list_store_set(dialog->model, &iter, + SUBSTATUS_COLUMN_ACCOUNT, account, + SUBSTATUS_COLUMN_ICON, scale, + SUBSTATUS_COLUMN_STATUS_ID, id, + SUBSTATUS_COLUMN_STATUS_NAME, name, + -1); + if (id && !strcmp(id, gaim_status_type_get_id(status_type))) + { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); + select = TRUE; + } + } + + if (!select) + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + + gtk_widget_show(win); +} + + +/************************************************************************** * GTK+ saved status glue **************************************************************************/ diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkstatusbox.c --- a/src/gtkstatusbox.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkstatusbox.c Mon Nov 14 07:20:22 2005 +0000 @@ -33,6 +33,7 @@ #include "gtksavedstatuses.h" #include "gtkstock.h" #include "gtkstatusbox.h" +#include "gtkutils.h" static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data); static void remove_typing_cb(GtkGaimStatusBox *box); @@ -292,68 +293,6 @@ g_free(text); } -static GdkPixbuf * -load_icon(GaimAccount *account, GaimStatusType *status_type) -{ - char basename2[BUFSIZ]; - char *filename; - GaimPluginProtocolInfo *prpl_info = NULL; - GaimPlugin *plugin; - const char *proto_name = NULL, *type_name; - GdkPixbuf *pixbuf, *scale = NULL, *emblem; - - - plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); - - if (plugin != NULL) { - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); - proto_name = prpl_info->list_icon(account, NULL); - } else { - return NULL; - } - - g_snprintf(basename2, sizeof(basename2), "%s.png", - proto_name); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", - basename2, NULL); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - - if (pixbuf != NULL) { - scale = gdk_pixbuf_scale_simple(pixbuf, 32, 32, - GDK_INTERP_BILINEAR); - g_object_unref(G_OBJECT(pixbuf)); - } - - - /* TODO: let the prpl pick the emblem on a per status bases, and only - * use the primitive as a fallback */ - type_name = gaim_primitive_get_id_from_type(gaim_status_type_get_primitive(status_type)); - if (!strcmp(type_name, "hidden")) - type_name = "invisible"; - if (!strcmp(type_name, "unavailable")) - type_name = "na"; - g_snprintf(basename2, sizeof(basename2), "%s.png", - type_name); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", - basename2, NULL); - emblem = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - - if (emblem) { - gdk_pixbuf_composite(emblem, - scale, 32-15, 32-15, - 15, 15, - 32-15, 32-15, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - - g_object_unref(emblem); - } - return scale; -} - /** * This updates the GtkTreeView so that it correctly shows the state * we are currently using. It is used when the current state is @@ -400,7 +339,7 @@ (primitive != GAIM_STATUS_AWAY) && (primitive != GAIM_STATUS_HIDDEN))) { - gtk_combo_box_set_active(GTK_COMBO_BOX(status_box), 4); + gtk_combo_box_set_active(GTK_COMBO_BOX(status_box), 5); } else { @@ -486,7 +425,7 @@ gtk_gaim_status_box_add(GTK_GAIM_STATUS_BOX(status_box), gaim_status_type_get_primitive(status_type), - load_icon(account, status_type), + gaim_gtk_create_prpl_icon_with_status(account, status_type), gaim_status_type_get_name(status_type), NULL); } diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkutils.c --- a/src/gtkutils.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkutils.c Mon Nov 14 07:20:22 2005 +0000 @@ -1609,3 +1609,88 @@ if(*height > 100) *height = 100; } + +GdkPixbuf * +gaim_gtk_create_prpl_icon(GaimAccount *account) +{ + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info = NULL; + GdkPixbuf *status = NULL; + char *filename = NULL; + const char *protoname = NULL; + char buf[256]; /* TODO: We should use a define for max file length */ + + g_return_val_if_fail(account != NULL, NULL); + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + + if (prpl != NULL) { + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info->list_icon != NULL) + protoname = prpl_info->list_icon(account, NULL); + } + + if (protoname == NULL) + return NULL; + + /* + * Status icons will be themeable too, and then it will look up + * protoname from the theme + */ + g_snprintf(buf, sizeof(buf), "%s.png", protoname); + + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", + "default", buf, NULL); + status = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + + return status; +} + +GdkPixbuf * +gaim_gtk_create_prpl_icon_with_status(GaimAccount *account, GaimStatusType *status_type) +{ + char basename2[BUFSIZ]; + char *filename; + const char *type_name; + GdkPixbuf *pixbuf, *scale = NULL, *emblem; + + pixbuf = gaim_gtk_create_prpl_icon(account); + + if (pixbuf != NULL) { + scale = gdk_pixbuf_scale_simple(pixbuf, 32, 32, + GDK_INTERP_BILINEAR); + g_object_unref(G_OBJECT(pixbuf)); + } else { + return NULL; + } + + /* TODO: let the prpl pick the emblem on a per status basis, and only + * use the primitive as a fallback */ + type_name = gaim_primitive_get_id_from_type(gaim_status_type_get_primitive(status_type)); + if (!strcmp(type_name, "hidden")) + type_name = "invisible"; + else if (!strcmp(type_name, "unavailable")) + type_name = "na"; + g_snprintf(basename2, sizeof(basename2), "%s.png", + type_name); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", + basename2, NULL); + emblem = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + + if (emblem) { + gdk_pixbuf_composite(emblem, + scale, 32-15, 32-15, + 15, 15, + 32-15, 32-15, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + + g_object_unref(emblem); + } + return scale; +} + diff -r 15732b01ea3d -r 3b52d94437f3 src/gtkutils.h --- a/src/gtkutils.h Mon Nov 14 06:50:07 2005 +0000 +++ b/src/gtkutils.h Mon Nov 14 07:20:22 2005 +0000 @@ -362,5 +362,24 @@ */ void gaim_gtk_buddy_icon_get_scale_size(GdkPixbuf *buf, GaimBuddyIconSpec *spec, int *width, int *height); +/** + * Returns the base image to represent the account, based on + * the currently selected theme. + * + * @param account The account. + * + * @return The icon. + */ +GdkPixbuf *gaim_gtk_create_prpl_icon(GaimAccount *account); + +/** + * Create a protocol-icon with the status emblem. + * + * @param account The account. + * @param status_type The status type to set the emblem for. + * + * @return The icon. + */ +GdkPixbuf * gaim_gtk_create_prpl_icon_with_status(GaimAccount *account, GaimStatusType *status_type); #endif /* _GAIM_GTKUTILS_H_ */ diff -r 15732b01ea3d -r 3b52d94437f3 src/savedstatuses.c --- a/src/savedstatuses.c Mon Nov 14 06:50:07 2005 +0000 +++ b/src/savedstatuses.c Mon Nov 14 07:20:22 2005 +0000 @@ -65,8 +65,6 @@ }; /* - * TODO: If an account is deleted, need to also delete any associated - * GaimSavedStatusSub's. * TODO: If a GaimStatusType is deleted, need to also delete any * associated GaimSavedStatusSub's? */ @@ -446,10 +444,10 @@ } void -gaim_savedstatus_set_substatus_for_account(GaimSavedStatus *saved_status, - const GaimAccount *account, - const GaimStatusType *type, - const char *message) +gaim_savedstatus_set_substatus(GaimSavedStatus *saved_status, + const GaimAccount *account, + const GaimStatusType *type, + const char *message) { GaimSavedStatusSub *substatus; @@ -458,7 +456,7 @@ g_return_if_fail(type != NULL); /* Find an existing substatus or create a new one */ - substatus = gaim_savedstatus_get_substatus_for_account(saved_status, account); + substatus = gaim_savedstatus_get_substatus(saved_status, account); if (substatus == NULL) { substatus = g_new0(GaimSavedStatusSub, 1); @@ -474,8 +472,8 @@ } void -gaim_savedstatus_unset_substatus_for_account(GaimSavedStatus *saved_status, - const GaimAccount *account) +gaim_savedstatus_unset_substatus(GaimSavedStatus *saved_status, + const GaimAccount *account) { GList *iter; GaimSavedStatusSub *substatus; @@ -569,8 +567,8 @@ } GaimSavedStatusSub * -gaim_savedstatus_get_substatus_for_account(const GaimSavedStatus *saved_status, - const GaimAccount *account) +gaim_savedstatus_get_substatus(const GaimSavedStatus *saved_status, + const GaimAccount *account) { GList *iter; GaimSavedStatusSub *substatus; @@ -638,7 +636,7 @@ g_return_if_fail(saved_status != NULL); g_return_if_fail(account != NULL); - substatus = gaim_savedstatus_get_substatus_for_account(saved_status, account); + substatus = gaim_savedstatus_get_substatus(saved_status, account); if (substatus != NULL) { status_type = substatus->type; diff -r 15732b01ea3d -r 3b52d94437f3 src/savedstatuses.h --- a/src/savedstatuses.h Mon Nov 14 06:50:07 2005 +0000 +++ b/src/savedstatuses.h Mon Nov 14 07:20:22 2005 +0000 @@ -99,10 +99,10 @@ * status. * @param message The message for the account in the substatus. */ -void gaim_savedstatus_set_substatus_for_account(GaimSavedStatus *status, - const GaimAccount *account, - const GaimStatusType *type, - const char *message); +void gaim_savedstatus_set_substatus(GaimSavedStatus *status, + const GaimAccount *account, + const GaimStatusType *type, + const char *message); /** * Unset a substatus for an account in a saved status. This clears @@ -113,7 +113,7 @@ * @param status The saved status. * @param account The account. */ -void gaim_savedstatus_unset_substatus_for_account(GaimSavedStatus *saved_status, +void gaim_savedstatus_unset_substatus(GaimSavedStatus *saved_status, const GaimAccount *account); /** @@ -212,7 +212,7 @@ * the given account does not have a substatus that * differs from the default status of this GaimSavedStatus. */ -GaimSavedStatusSub *gaim_savedstatus_get_substatus_for_account( +GaimSavedStatusSub *gaim_savedstatus_get_substatus( const GaimSavedStatus *saved_status, const GaimAccount *account);