2 * @file gtksavedstatus.c GTK+ Saved Status Editor UI
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
32 #include "savedstatuses.h"
38 #include "gtkimhtml.h"
39 #include "gtkimhtmltoolbar.h"
40 #include "gtksavedstatuses.h"
41 #include "pidginstock.h"
45 * TODO: Should attach to the account-deleted and account-added signals
46 * and update the GtkListStores in any StatusEditor windows that
51 * These are used for the GtkTreeView when you're scrolling through
52 * all your saved statuses.
56 STATUS_WINDOW_COLUMN_TITLE
,
57 STATUS_WINDOW_COLUMN_TYPE
,
58 STATUS_WINDOW_COLUMN_MESSAGE
,
59 /** A hidden column containing a pointer to the editor for this saved status. */
60 STATUS_WINDOW_COLUMN_WINDOW
,
61 STATUS_WINDOW_COLUMN_ICON
,
62 STATUS_WINDOW_NUM_COLUMNS
66 * These are used for the GtkTreeView containing the list of accounts
67 * at the bottom of the window when you're editing a particular
72 /** A hidden column containing a pointer to the PurpleAccount. */
73 STATUS_EDITOR_COLUMN_ACCOUNT
,
74 /** A hidden column containing a pointer to the editor for this substatus. */
75 STATUS_EDITOR_COLUMN_WINDOW
,
76 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
,
77 STATUS_EDITOR_COLUMN_ICON
,
78 STATUS_EDITOR_COLUMN_USERNAME
,
79 /** A hidden column containing the ID of this PurpleStatusType. */
80 STATUS_EDITOR_COLUMN_STATUS_ID
,
81 STATUS_EDITOR_COLUMN_STATUS_NAME
,
82 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
,
83 STATUS_EDITOR_COLUMN_STATUS_ICON
,
84 STATUS_EDITOR_NUM_COLUMNS
88 * These are used in the GtkComboBox to select the specific PurpleStatusType
89 * when setting a (sub)status for a particular saved status.
94 /** A hidden column containing the ID of this PurpleStatusType. */
95 STATUS_COLUMN_STATUS_ID
,
96 STATUS_COLUMN_STATUS_NAME
,
105 GtkWidget
*use_button
;
106 GtkWidget
*modify_button
;
107 GtkWidget
*delete_button
;
115 GtkButton
*saveanduse_button
;
116 GtkButton
*save_button
;
118 gchar
*original_title
;
126 StatusEditor
*status_editor
;
127 PurpleAccount
*account
;
133 GtkIMHtmlToolbar
*toolbar
;
136 static StatusWindow
*status_window
= NULL
;
139 /**************************************************************************
141 **************************************************************************/
144 status_window_find_savedstatus(GtkTreeIter
*iter
, const char *title
)
149 if ((status_window
== NULL
) || (title
== NULL
))
152 model
= GTK_TREE_MODEL(status_window
->model
);
154 if (!gtk_tree_model_get_iter_first(model
, iter
))
158 gtk_tree_model_get(model
, iter
, STATUS_WINDOW_COLUMN_TITLE
, &cur
, -1);
159 if (purple_strequal(title
, cur
))
165 } while (gtk_tree_model_iter_next(model
, iter
));
171 status_window_destroy_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
173 StatusWindow
*dialog
= user_data
;
175 dialog
->window
= NULL
;
176 pidgin_status_window_hide();
182 status_window_use_cb(GtkButton
*button
, StatusWindow
*dialog
)
184 GtkTreeSelection
*selection
;
187 int num_selected
= 0;
189 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
191 num_selected
= gtk_tree_selection_count_selected_rows(selection
);
192 if (num_selected
!= 1)
194 * This shouldn't happen because the "Use" button should have
195 * been grayed out. Oh well.
199 list
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
201 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog
->model
),
205 PurpleSavedStatus
*saved_status
;
206 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
207 STATUS_WINDOW_COLUMN_TITLE
, &title
,
209 saved_status
= purple_savedstatus_find(title
);
211 purple_savedstatus_activate(saved_status
);
214 g_list_foreach(list
, (GFunc
)gtk_tree_path_free
, NULL
);
219 status_window_add_cb(GtkButton
*button
, gpointer user_data
)
221 pidgin_status_editor_show(FALSE
, NULL
);
225 status_window_modify_foreach(GtkTreeModel
*model
, GtkTreePath
*path
,
226 GtkTreeIter
*iter
, gpointer user_data
)
229 PurpleSavedStatus
*saved_status
;
231 gtk_tree_model_get(model
, iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
232 saved_status
= purple_savedstatus_find(title
);
234 pidgin_status_editor_show(TRUE
, saved_status
);
238 status_window_modify_cb(GtkButton
*button
, gpointer user_data
)
240 StatusWindow
*dialog
= user_data
;
241 GtkTreeSelection
*selection
;
243 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
245 gtk_tree_selection_selected_foreach(selection
, status_window_modify_foreach
, user_data
);
249 status_window_delete_cancel_cb(gpointer data
)
251 GList
*sel_titles
= data
;
252 g_list_foreach(sel_titles
, (GFunc
) g_free
, NULL
);
253 g_list_free(sel_titles
);
257 status_window_delete_confirm_cb(gpointer data
)
260 GList
*sel_titles
= data
, *l
;
263 for (l
= sel_titles
; l
!= NULL
; l
= l
->next
) {
265 if (purple_savedstatus_find(title
) != purple_savedstatus_get_current()) {
266 if (status_window_find_savedstatus(&iter
, title
))
267 gtk_list_store_remove(status_window
->model
, &iter
);
268 purple_savedstatus_delete(title
);
272 g_list_free(sel_titles
);
276 status_window_delete_cb(GtkButton
*button
, gpointer user_data
)
278 StatusWindow
*dialog
= user_data
;
280 GtkTreeSelection
*selection
;
281 GList
*sel_paths
, *l
, *sel_titles
= NULL
;
282 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
286 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
287 sel_paths
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
289 /* This is ugly because we're not allowed to modify the model from within
290 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid
291 * when something is removed from the model. The selection can also change while
292 * the request dialog is displayed, so we need to capture the selected rows at this time. */
294 for (l
= sel_paths
; l
!= NULL
; l
= l
->next
) {
295 if (gtk_tree_model_get_iter(model
, &iter
, l
->data
)) {
296 gtk_tree_model_get(model
, &iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
297 sel_titles
= g_list_prepend(sel_titles
, title
);
299 gtk_tree_path_free(l
->data
);
301 g_list_free(sel_paths
);
303 g_return_if_fail(sel_titles
!= NULL
);
304 if (!sel_titles
->next
) {
305 title
= g_strdup_printf(_("Are you sure you want to delete %s?"),
306 (const gchar
*)sel_titles
->data
);
307 handle
= purple_savedstatus_find(sel_titles
->data
);
309 title
= g_strdup(_("Are you sure you want to delete the selected saved statuses?"));
313 purple_request_action(handle
, NULL
, title
, NULL
, 0,
316 _("Delete"), status_window_delete_confirm_cb
,
317 _("Cancel"), status_window_delete_cancel_cb
);
323 status_window_close_cb(GtkButton
*button
, gpointer user_data
)
325 pidgin_status_window_hide();
329 status_selected_cb(GtkTreeSelection
*sel
, gpointer user_data
)
331 StatusWindow
*dialog
= user_data
;
332 GList
*sel_paths
, *tmp
;
333 gboolean can_use
= TRUE
, can_delete
= TRUE
;
335 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
337 sel_paths
= gtk_tree_selection_get_selected_rows(sel
, NULL
);
339 for (tmp
= sel_paths
, num_selected
= 0; tmp
; tmp
= tmp
->next
, num_selected
++) {
343 if (gtk_tree_model_get_iter(model
, &iter
, tmp
->data
)) {
344 gtk_tree_model_get(model
, &iter
,
345 STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
346 if (purple_savedstatus_find(title
) == purple_savedstatus_get_current()) {
347 can_use
= can_delete
= FALSE
;
353 gtk_tree_path_free(tmp
->data
);
356 gtk_widget_set_sensitive(dialog
->use_button
, (num_selected
== 1) && can_use
);
357 gtk_widget_set_sensitive(dialog
->modify_button
, (num_selected
> 0));
358 gtk_widget_set_sensitive(dialog
->delete_button
, num_selected
> 0 && can_delete
);
360 g_list_free(sel_paths
);
364 get_stock_icon_from_primitive(PurpleStatusPrimitive type
)
366 return pidgin_stock_id_from_status_primitive(type
);
370 add_status_to_saved_status_list(GtkListStore
*model
, PurpleSavedStatus
*saved_status
)
378 if (purple_savedstatus_is_transient(saved_status
))
381 title
= purple_savedstatus_get_title(saved_status
);
382 type
= purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status
));
383 message
= purple_markup_strip_html(purple_savedstatus_get_message(saved_status
));
384 icon
= get_stock_icon_from_primitive(purple_savedstatus_get_type(saved_status
));
386 gtk_list_store_append(model
, &iter
);
387 gtk_list_store_set(model
, &iter
,
388 STATUS_WINDOW_COLUMN_ICON
, icon
,
389 STATUS_WINDOW_COLUMN_TITLE
, title
,
390 STATUS_WINDOW_COLUMN_TYPE
, type
,
391 STATUS_WINDOW_COLUMN_MESSAGE
, message
,
397 populate_saved_status_list(StatusWindow
*dialog
)
399 GList
*saved_statuses
;
401 gtk_list_store_clear(dialog
->model
);
403 for (saved_statuses
= purple_savedstatuses_get_all(); saved_statuses
!= NULL
;
404 saved_statuses
= g_list_next(saved_statuses
))
406 add_status_to_saved_status_list(dialog
->model
, saved_statuses
->data
);
411 search_func(GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer search_data
)
416 gtk_tree_model_get(model
, iter
, column
, &haystack
, -1);
418 result
= (purple_strcasestr(haystack
, key
) == NULL
);
426 savedstatus_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, StatusWindow
*dialog
)
428 status_window_use_cb(NULL
, dialog
);
429 status_window_close_cb(NULL
, dialog
);
433 saved_status_updated_cb(PurpleSavedStatus
*status
, StatusWindow
*sw
)
435 populate_saved_status_list(sw
);
439 create_saved_status_list(StatusWindow
*dialog
)
442 GtkTreeSelection
*sel
;
443 GtkTreeViewColumn
*column
;
444 GtkCellRenderer
*renderer
;
446 /* Create the list model */
447 dialog
->model
= gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS
,
454 /* Create the treeview */
455 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog
->model
));
456 dialog
->treeview
= treeview
;
457 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview
), TRUE
);
458 g_signal_connect(G_OBJECT(treeview
), "row-activated",
459 G_CALLBACK(savedstatus_activated_cb
), dialog
);
461 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
462 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
463 g_signal_connect(G_OBJECT(sel
), "changed",
464 G_CALLBACK(status_selected_cb
), dialog
);
467 column
= gtk_tree_view_column_new();
468 gtk_tree_view_column_set_title(column
, _("Title"));
469 gtk_tree_view_column_set_resizable(column
, TRUE
);
470 gtk_tree_view_column_set_min_width(column
, 100);
471 gtk_tree_view_column_set_sort_column_id(column
,
472 STATUS_WINDOW_COLUMN_TITLE
);
473 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
474 renderer
= gtk_cell_renderer_text_new();
475 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
476 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
477 STATUS_WINDOW_COLUMN_TITLE
);
478 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
480 column
= gtk_tree_view_column_new();
481 gtk_tree_view_column_set_title(column
, _("Type"));
482 gtk_tree_view_column_set_resizable(column
, TRUE
);
483 gtk_tree_view_column_set_sort_column_id(column
,
484 STATUS_WINDOW_COLUMN_TYPE
);
485 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
486 renderer
= gtk_cell_renderer_pixbuf_new();
487 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
488 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
489 STATUS_WINDOW_COLUMN_ICON
);
490 renderer
= gtk_cell_renderer_text_new();
491 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
492 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
493 STATUS_WINDOW_COLUMN_TYPE
);
495 column
= gtk_tree_view_column_new();
496 gtk_tree_view_column_set_title(column
, _("Message"));
497 gtk_tree_view_column_set_resizable(column
, TRUE
);
498 gtk_tree_view_column_set_sort_column_id(column
,
499 STATUS_WINDOW_COLUMN_MESSAGE
);
500 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
501 renderer
= gtk_cell_renderer_text_new();
502 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
503 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
504 STATUS_WINDOW_COLUMN_MESSAGE
);
505 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
507 /* Enable CTRL+F searching */
508 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview
), STATUS_WINDOW_COLUMN_TITLE
);
509 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview
), search_func
, NULL
, NULL
);
511 /* Sort the title column by default */
512 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog
->model
),
513 STATUS_WINDOW_COLUMN_TITLE
,
517 populate_saved_status_list(dialog
);
519 gtk_widget_show_all(treeview
);
521 return pidgin_make_scrollable(treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1);
525 configure_cb(GtkWidget
*widget
, GdkEventConfigure
*event
, StatusWindow
*dialog
)
527 if (GTK_WIDGET_VISIBLE(widget
))
529 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/width", event
->width
);
530 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/height", event
->height
);
537 current_status_changed(PurpleSavedStatus
*old
, PurpleSavedStatus
*new_status
,
538 StatusWindow
*dialog
)
540 status_selected_cb(gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
)), dialog
);
544 pidgin_status_window_show(void)
546 StatusWindow
*dialog
;
554 if (status_window
!= NULL
)
556 gtk_window_present(GTK_WINDOW(status_window
->window
));
560 status_window
= dialog
= g_new0(StatusWindow
, 1);
562 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/width");
563 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/height");
565 dialog
->window
= win
= pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER
, "statuses", TRUE
);
566 gtk_window_set_default_size(GTK_WINDOW(win
), width
, height
);
568 g_signal_connect(G_OBJECT(win
), "delete_event",
569 G_CALLBACK(status_window_destroy_cb
), dialog
);
570 g_signal_connect(G_OBJECT(win
), "configure_event",
571 G_CALLBACK(configure_cb
), dialog
);
574 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
576 /* List of saved status states */
577 list
= create_saved_status_list(dialog
);
578 gtk_box_pack_start(GTK_BOX(vbox
), list
, TRUE
, TRUE
, 0);
581 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
584 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
585 PIDGIN_BUTTON_HORIZONTAL
);
586 dialog
->use_button
= button
;
587 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
588 gtk_widget_set_sensitive(button
, FALSE
);
590 g_signal_connect(G_OBJECT(button
), "clicked",
591 G_CALLBACK(status_window_use_cb
), dialog
);
594 pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_ADD
,
595 G_CALLBACK(status_window_add_cb
), dialog
);
598 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_MODIFY
,
599 G_CALLBACK(status_window_modify_cb
), dialog
);
600 dialog
->modify_button
= button
;
601 gtk_widget_set_sensitive(button
, FALSE
);
604 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_DELETE
,
605 G_CALLBACK(status_window_delete_cb
), dialog
);
606 dialog
->delete_button
= button
;
607 gtk_widget_set_sensitive(button
, FALSE
);
610 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CLOSE
,
611 G_CALLBACK(status_window_close_cb
), dialog
);
613 purple_signal_connect(purple_savedstatuses_get_handle(),
614 "savedstatus-changed", status_window
,
615 PURPLE_CALLBACK(current_status_changed
), dialog
);
616 purple_signal_connect(purple_savedstatuses_get_handle(),
617 "savedstatus-added", status_window
,
618 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
619 purple_signal_connect(purple_savedstatuses_get_handle(),
620 "savedstatus-deleted", status_window
,
621 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
622 purple_signal_connect(purple_savedstatuses_get_handle(),
623 "savedstatus-modified", status_window
,
624 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
626 gtk_widget_show_all(win
);
630 pidgin_status_window_hide(void)
632 if (status_window
== NULL
)
635 if (status_window
->window
!= NULL
)
636 gtk_widget_destroy(status_window
->window
);
638 purple_request_close_with_handle(status_window
);
639 purple_notify_close_with_handle(status_window
);
640 purple_signals_disconnect_by_handle(status_window
);
641 g_object_unref(G_OBJECT(status_window
->model
));
642 g_free(status_window
);
643 status_window
= NULL
;
647 /**************************************************************************
649 **************************************************************************/
651 static void substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
);
654 status_editor_remove_dialog(StatusEditor
*dialog
)
659 /* Remove the reference to this dialog from our parent's list store */
660 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
662 gtk_list_store_set(status_window
->model
, &iter
,
663 STATUS_WINDOW_COLUMN_WINDOW
, NULL
,
667 /* Close any substatus editors that may be open */
668 model
= GTK_TREE_MODEL(dialog
->model
);
669 if (gtk_tree_model_get_iter_first(model
, &iter
))
672 SubStatusEditor
*substatus_dialog
;
674 gtk_tree_model_get(model
, &iter
,
675 STATUS_EDITOR_COLUMN_WINDOW
, &substatus_dialog
,
677 if (substatus_dialog
!= NULL
)
679 gtk_list_store_set(dialog
->model
, &iter
,
680 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
682 substatus_editor_cancel_cb(NULL
, substatus_dialog
);
684 } while (gtk_tree_model_iter_next(model
, &iter
));
690 status_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
692 StatusEditor
*dialog
= user_data
;
694 status_editor_remove_dialog(dialog
);
695 g_free(dialog
->original_title
);
696 g_object_unref(G_OBJECT(dialog
->model
));
701 status_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
703 StatusEditor
*dialog
= user_data
;
704 gtk_widget_destroy(dialog
->window
);
708 status_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
710 StatusEditor
*dialog
= user_data
;
712 PurpleStatusPrimitive type
;
713 char *message
, *unformatted
;
714 PurpleSavedStatus
*saved_status
= NULL
;
718 title
= gtk_entry_get_text(dialog
->title
);
721 * If we're saving this status, and the title is already taken
722 * then show an error dialog and don't do anything.
724 if (((button
== dialog
->saveanduse_button
) || (button
== dialog
->save_button
)) &&
725 (purple_savedstatus_find(title
) != NULL
) &&
726 ((dialog
->original_title
== NULL
) || (!purple_strequal(title
, dialog
->original_title
))))
728 purple_notify_error(status_window
, NULL
, _("Title already in use. You must "
729 "choose a unique title."), NULL
);
733 type
= gtk_combo_box_get_active(dialog
->type
) + (PURPLE_STATUS_UNSET
+ 1);
734 message
= gtk_imhtml_get_markup(dialog
->message
);
735 unformatted
= purple_markup_strip_html(message
);
738 * If we're editing an old status, then lookup the old status.
739 * Note: It is possible that it has been deleted or renamed
740 * or something, and no longer exists.
742 if (dialog
->original_title
!= NULL
)
746 saved_status
= purple_savedstatus_find(dialog
->original_title
);
748 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
749 gtk_list_store_remove(status_window
->model
, &iter
);
752 if (saved_status
== NULL
)
754 /* This is a new status */
755 if ((button
== dialog
->saveanduse_button
)
756 || (button
== dialog
->save_button
))
757 saved_status
= purple_savedstatus_new(title
, type
);
759 saved_status
= purple_savedstatus_new(NULL
, type
);
763 /* Modify the old status */
764 if (!purple_strequal(title
, dialog
->original_title
))
765 purple_savedstatus_set_title(saved_status
, title
);
766 purple_savedstatus_set_type(saved_status
, type
);
769 if (*unformatted
== '\0')
770 purple_savedstatus_set_message(saved_status
, NULL
);
772 purple_savedstatus_set_message(saved_status
, message
);
774 /* Set any substatuses */
775 model
= GTK_TREE_MODEL(dialog
->model
);
776 if (gtk_tree_model_get_iter_first(model
, &iter
))
779 PurpleAccount
*account
;
783 PurpleStatusType
*type
;
785 gtk_tree_model_get(model
, &iter
,
786 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
787 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
788 STATUS_EDITOR_COLUMN_STATUS_ID
, &id
,
789 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, &message
,
793 type
= purple_account_get_status_type(account
, id
);
794 purple_savedstatus_set_substatus(saved_status
, account
, type
, message
);
798 purple_savedstatus_unset_substatus(saved_status
, account
);
802 } while (gtk_tree_model_iter_next(model
, &iter
));
808 /* If they clicked on "Save and Use" or "Use," then activate the status */
809 if (button
!= dialog
->save_button
)
810 purple_savedstatus_activate(saved_status
);
812 gtk_widget_destroy(dialog
->window
);
816 editor_title_changed_cb(GtkWidget
*widget
, gpointer user_data
)
818 StatusEditor
*dialog
= user_data
;
821 text
= gtk_entry_get_text(dialog
->title
);
823 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->saveanduse_button
), (*text
!= '\0'));
824 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->save_button
), (*text
!= '\0'));
828 create_status_type_menu(PurpleStatusPrimitive type
)
834 GtkCellRenderer
*renderer
;
836 store
= gtk_list_store_new(STATUS_NUM_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
838 for (i
= PURPLE_STATUS_UNSET
+ 1; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
840 /* Someone should fix this for 3.0.0. The independent boolean
841 * should probably be set on the status type, not the status.
842 * I guess that would prevent third party plugins from creating
843 * independent statuses?
845 if (i
== PURPLE_STATUS_MOBILE
||
846 i
== PURPLE_STATUS_MOOD
||
847 i
== PURPLE_STATUS_TUNE
)
849 * Special-case these. They're intended to be independent
850 * status types, so don't show them in the list.
854 gtk_list_store_append(store
, &iter
);
855 gtk_list_store_set(store
, &iter
,
856 STATUS_COLUMN_ICON
, get_stock_icon_from_primitive(i
),
857 STATUS_COLUMN_STATUS_ID
, purple_primitive_get_id_from_type(i
),
858 STATUS_COLUMN_STATUS_NAME
, purple_primitive_get_name_from_type(i
),
862 dropdown
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(store
));
864 renderer
= gtk_cell_renderer_pixbuf_new();
865 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, FALSE
);
866 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
867 "stock-id", STATUS_COLUMN_ICON
,
870 renderer
= gtk_cell_renderer_text_new();
871 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, TRUE
);
872 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
873 "text", STATUS_COLUMN_STATUS_NAME
,
876 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown
),
877 type
- (PURPLE_STATUS_UNSET
+ 1));
882 static void edit_substatus(StatusEditor
*status_editor
, PurpleAccount
*account
);
885 edit_substatus_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, gpointer user_data
)
887 StatusEditor
*dialog
= user_data
;
889 PurpleAccount
*account
;
891 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog
->model
), &iter
, path
);
892 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
893 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
896 edit_substatus(dialog
, account
);
900 status_editor_substatus_cb(GtkCellRendererToggle
*renderer
, gchar
*path_str
, gpointer data
)
902 StatusEditor
*dialog
= (StatusEditor
*)data
;
905 PurpleAccount
*account
;
907 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog
->model
), &iter
, path_str
);
908 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
909 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
910 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
917 edit_substatus(dialog
, account
);
921 /* Remove the substatus */
922 gtk_list_store_set(dialog
->model
, &iter
,
923 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, enabled
,
924 STATUS_EDITOR_COLUMN_STATUS_ID
, NULL
,
925 STATUS_EDITOR_COLUMN_STATUS_NAME
, NULL
,
926 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, NULL
,
927 STATUS_EDITOR_COLUMN_STATUS_ICON
, NULL
,
933 status_editor_add_columns(StatusEditor
*dialog
)
935 GtkCellRenderer
*renderer
;
936 GtkTreeViewColumn
*column
;
938 /* Enable Different status column */
939 renderer
= gtk_cell_renderer_toggle_new();
940 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog
->treeview
),
943 "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
,
945 g_signal_connect(G_OBJECT(renderer
), "toggled",
946 G_CALLBACK(status_editor_substatus_cb
), dialog
);
948 /* Username column */
949 column
= gtk_tree_view_column_new();
950 gtk_tree_view_column_set_resizable(column
, TRUE
);
951 gtk_tree_view_column_set_title(column
, _("Username"));
952 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
953 gtk_tree_view_column_set_resizable(column
, TRUE
);
956 renderer
= gtk_cell_renderer_pixbuf_new();
957 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
958 gtk_tree_view_column_add_attribute(column
, renderer
, "pixbuf",
959 STATUS_EDITOR_COLUMN_ICON
);
962 renderer
= gtk_cell_renderer_text_new();
963 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
964 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
965 STATUS_EDITOR_COLUMN_USERNAME
);
968 column
= gtk_tree_view_column_new();
969 gtk_tree_view_column_set_resizable(column
, TRUE
);
970 gtk_tree_view_column_set_title(column
, _("Status"));
971 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
972 gtk_tree_view_column_set_resizable(column
, TRUE
);
973 renderer
= gtk_cell_renderer_pixbuf_new();
974 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
975 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
976 STATUS_EDITOR_COLUMN_STATUS_ICON
);
977 renderer
= gtk_cell_renderer_text_new();
978 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
979 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
980 STATUS_EDITOR_COLUMN_STATUS_NAME
);
983 column
= gtk_tree_view_column_new();
984 gtk_tree_view_column_set_resizable(column
, TRUE
);
985 gtk_tree_view_column_set_title(column
, _("Message"));
986 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
987 gtk_tree_view_column_set_resizable(column
, TRUE
);
988 renderer
= gtk_cell_renderer_text_new();
989 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
990 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
991 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
);
993 g_signal_connect(G_OBJECT(dialog
->treeview
), "row-activated",
994 G_CALLBACK(edit_substatus_cb
), dialog
);
998 status_editor_set_account(GtkListStore
*store
, PurpleAccount
*account
,
999 GtkTreeIter
*iter
, PurpleSavedStatusSub
*substatus
)
1002 const char *id
= NULL
, *name
= NULL
, *message
= NULL
;
1003 PurpleStatusPrimitive prim
= PURPLE_STATUS_UNSET
;
1005 pixbuf
= pidgin_create_prpl_icon(account
, PIDGIN_PRPL_ICON_MEDIUM
);
1006 if ((pixbuf
!= NULL
) && !purple_account_is_connected(account
))
1008 gdk_pixbuf_saturate_and_pixelate(pixbuf
, pixbuf
, 0.0, FALSE
);
1011 if (substatus
!= NULL
)
1013 const PurpleStatusType
*type
;
1015 type
= purple_savedstatus_substatus_get_type(substatus
);
1016 id
= purple_status_type_get_id(type
);
1017 name
= purple_status_type_get_name(type
);
1018 prim
= purple_status_type_get_primitive(type
);
1019 if (purple_status_type_get_attr(type
, "message"))
1020 message
= purple_savedstatus_substatus_get_message(substatus
);
1023 gtk_list_store_set(store
, iter
,
1024 STATUS_EDITOR_COLUMN_ACCOUNT
, account
,
1025 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, (substatus
!= NULL
),
1026 STATUS_EDITOR_COLUMN_ICON
, pixbuf
,
1027 STATUS_EDITOR_COLUMN_USERNAME
, purple_account_get_username(account
),
1028 STATUS_EDITOR_COLUMN_STATUS_ID
, id
,
1029 STATUS_EDITOR_COLUMN_STATUS_NAME
, name
,
1030 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, message
,
1031 STATUS_EDITOR_COLUMN_STATUS_ICON
, get_stock_icon_from_primitive(prim
),
1035 g_object_unref(G_OBJECT(pixbuf
));
1039 status_editor_add_account(StatusEditor
*dialog
, PurpleAccount
*account
,
1040 PurpleSavedStatusSub
*substatus
)
1044 gtk_list_store_append(dialog
->model
, &iter
);
1046 status_editor_set_account(dialog
->model
, account
, &iter
, substatus
);
1050 status_editor_populate_list(StatusEditor
*dialog
, PurpleSavedStatus
*saved_status
)
1053 PurpleSavedStatusSub
*substatus
;
1055 gtk_list_store_clear(dialog
->model
);
1057 for (iter
= purple_accounts_get_all(); iter
!= NULL
; iter
= iter
->next
)
1059 PurpleAccount
*account
= (PurpleAccount
*)iter
->data
;
1061 if (saved_status
!= NULL
)
1062 substatus
= purple_savedstatus_get_substatus(saved_status
, account
);
1066 status_editor_add_account(dialog
, account
, substatus
);
1071 pidgin_status_editor_show(gboolean edit
, PurpleSavedStatus
*saved_status
)
1074 StatusEditor
*dialog
;
1079 GtkWidget
*expander
;
1080 GtkWidget
*dropdown
;
1088 GList
*focus_chain
= NULL
;
1092 g_return_if_fail(saved_status
!= NULL
);
1093 g_return_if_fail(!purple_savedstatus_is_transient(saved_status
));
1096 /* Find a possible window for this saved status and present it */
1097 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1099 gtk_tree_model_get(GTK_TREE_MODEL(status_window
->model
), &iter
,
1100 STATUS_WINDOW_COLUMN_WINDOW
, &dialog
,
1104 gtk_window_present(GTK_WINDOW(dialog
->window
));
1109 dialog
= g_new0(StatusEditor
, 1);
1110 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1112 gtk_list_store_set(status_window
->model
, &iter
,
1113 STATUS_WINDOW_COLUMN_WINDOW
, dialog
,
1118 dialog
->original_title
= g_strdup(purple_savedstatus_get_title(saved_status
));
1120 dialog
->window
= win
= pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER
, "status", TRUE
);
1122 g_signal_connect(G_OBJECT(win
), "destroy",
1123 G_CALLBACK(status_editor_destroy_cb
), dialog
);
1125 /* Setup the vbox */
1126 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1128 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1131 entry
= gtk_entry_new();
1132 dialog
->title
= GTK_ENTRY(entry
);
1133 if ((saved_status
!= NULL
)
1134 && !purple_savedstatus_is_transient(saved_status
)
1135 && (purple_savedstatus_get_title(saved_status
) != NULL
))
1136 gtk_entry_set_text(GTK_ENTRY(entry
), purple_savedstatus_get_title(saved_status
));
1137 g_signal_connect(G_OBJECT(entry
), "changed",
1138 G_CALLBACK(editor_title_changed_cb
), dialog
);
1139 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Title:"), sg
, entry
, TRUE
, NULL
);
1142 if (saved_status
!= NULL
)
1143 dropdown
= create_status_type_menu(purple_savedstatus_get_type(saved_status
));
1145 dropdown
= create_status_type_menu(PURPLE_STATUS_AWAY
);
1146 dialog
->type
= GTK_COMBO_BOX(dropdown
);
1147 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Status:"), sg
, dropdown
, TRUE
, NULL
);
1149 /* Status message */
1150 frame
= pidgin_create_imhtml(TRUE
, &text
, &toolbar
, NULL
);
1151 dialog
->message
= GTK_IMHTML(text
);
1152 hbox
= pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Message:"), sg
, frame
, TRUE
, NULL
);
1153 gtk_container_child_set(GTK_CONTAINER(vbox
), hbox
, "expand", TRUE
, "fill", TRUE
, NULL
);
1154 focus_chain
= g_list_prepend(focus_chain
, dialog
->message
);
1155 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), focus_chain
);
1156 g_list_free(focus_chain
);
1158 gtk_imhtml_set_return_inserts_newline(dialog
->message
);
1160 if ((saved_status
!= NULL
) && (purple_savedstatus_get_message(saved_status
) != NULL
))
1161 gtk_imhtml_append_text(GTK_IMHTML(text
),
1162 purple_savedstatus_get_message(saved_status
), 0);
1164 /* Different status message expander */
1165 expander
= gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts"));
1166 gtk_box_pack_start(GTK_BOX(vbox
), expander
, FALSE
, FALSE
, 0);
1168 /* Setup the box that the expander will cover */
1169 dbox
= gtk_vbox_new(FALSE
, PIDGIN_HIG_CAT_SPACE
);
1170 gtk_container_add(GTK_CONTAINER(expander
), dbox
);
1172 /* Create the list model */
1173 dialog
->model
= gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS
,
1184 /* Create the treeview */
1185 dialog
->treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog
->model
));
1186 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog
->treeview
), TRUE
);
1187 gtk_widget_set_size_request(dialog
->treeview
, -1, 150);
1188 gtk_box_pack_start(GTK_BOX(dbox
),
1189 pidgin_make_scrollable(dialog
->treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
1193 status_editor_add_columns(dialog
);
1196 status_editor_populate_list(dialog
, saved_status
);
1198 /* Expand the treeview if we have substatuses */
1199 gtk_expander_set_expanded(GTK_EXPANDER(expander
),
1200 (saved_status
!= NULL
) && purple_savedstatus_has_substatuses(saved_status
));
1203 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
1204 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
1205 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
1208 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CANCEL
,
1209 G_CALLBACK(status_editor_cancel_cb
), dialog
);
1212 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
1213 PIDGIN_BUTTON_HORIZONTAL
);
1214 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1215 g_signal_connect(G_OBJECT(button
), "clicked",
1216 G_CALLBACK(status_editor_ok_cb
), dialog
);
1218 /* Save and Use button */
1219 button
= pidgin_pixbuf_button_from_stock(_("Sa_ve and Use"), GTK_STOCK_OK
,
1220 PIDGIN_BUTTON_HORIZONTAL
);
1221 dialog
->saveanduse_button
= GTK_BUTTON(button
);
1222 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1223 if (dialog
->original_title
== NULL
)
1224 gtk_widget_set_sensitive(button
, FALSE
);
1225 g_signal_connect(G_OBJECT(button
), "clicked",
1226 G_CALLBACK(status_editor_ok_cb
), dialog
);
1229 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_SAVE
,
1230 G_CALLBACK(status_editor_ok_cb
), dialog
);
1231 if (dialog
->original_title
== NULL
)
1232 gtk_widget_set_sensitive(button
, FALSE
);
1233 dialog
->save_button
= GTK_BUTTON(button
);
1235 gtk_widget_show_all(win
);
1240 /**************************************************************************
1242 **************************************************************************/
1245 substatus_selection_changed_cb(GtkComboBox
*box
, gpointer user_data
)
1247 SubStatusEditor
*select
= user_data
;
1250 PurpleStatusType
*type
;
1252 if (!gtk_combo_box_get_active_iter(box
, &iter
))
1254 gtk_tree_model_get(GTK_TREE_MODEL(select
->model
), &iter
,
1255 STATUS_COLUMN_STATUS_ID
, &id
,
1257 type
= purple_account_get_status_type(select
->account
, id
);
1260 if (purple_status_type_get_attr(type
, "message") == NULL
)
1262 gtk_widget_set_sensitive(GTK_WIDGET(select
->message
), FALSE
);
1263 gtk_widget_set_sensitive(GTK_WIDGET(select
->toolbar
), FALSE
);
1267 gtk_widget_set_sensitive(GTK_WIDGET(select
->message
), TRUE
);
1268 gtk_widget_set_sensitive(GTK_WIDGET(select
->toolbar
), TRUE
);
1273 status_editor_find_account_in_treemodel(GtkTreeIter
*iter
,
1274 StatusEditor
*status_editor
,
1275 PurpleAccount
*account
)
1277 GtkTreeModel
*model
;
1280 g_return_val_if_fail(status_editor
!= NULL
, FALSE
);
1281 g_return_val_if_fail(account
!= NULL
, FALSE
);
1283 model
= GTK_TREE_MODEL(status_editor
->model
);
1285 if (!gtk_tree_model_get_iter_first(model
, iter
))
1289 gtk_tree_model_get(model
, iter
, STATUS_EDITOR_COLUMN_ACCOUNT
, &cur
, -1);
1292 } while (gtk_tree_model_iter_next(model
, iter
));
1298 substatus_editor_remove_dialog(SubStatusEditor
*dialog
)
1302 if (status_editor_find_account_in_treemodel(&iter
, dialog
->status_editor
, dialog
->account
))
1304 /* Remove the reference to this dialog from our parent's list store */
1305 gtk_list_store_set(dialog
->status_editor
->model
, &iter
,
1306 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
1312 substatus_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
1314 SubStatusEditor
*dialog
= user_data
;
1316 substatus_editor_remove_dialog(dialog
);
1321 substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
1323 SubStatusEditor
*dialog
= user_data
;
1324 gtk_widget_destroy(dialog
->window
);
1329 substatus_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
1331 SubStatusEditor
*dialog
= user_data
;
1332 StatusEditor
*status_editor
;
1334 PurpleStatusType
*type
;
1336 char *message
= NULL
;
1337 const char *name
= NULL
, *stock
= NULL
;
1339 if (!gtk_combo_box_get_active_iter(dialog
->box
, &iter
))
1341 gtk_widget_destroy(dialog
->window
);
1345 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
1346 STATUS_COLUMN_STATUS_ID
, &id
,
1348 type
= purple_account_get_status_type(dialog
->account
, id
);
1349 if (purple_status_type_get_attr(type
, "message") != NULL
)
1350 message
= gtk_imhtml_get_markup(GTK_IMHTML(dialog
->message
));
1351 name
= purple_status_type_get_name(type
);
1352 stock
= get_stock_icon_from_primitive(purple_status_type_get_primitive(type
));
1354 status_editor
= dialog
->status_editor
;
1356 if (status_editor_find_account_in_treemodel(&iter
, status_editor
, dialog
->account
))
1358 gtk_list_store_set(status_editor
->model
, &iter
,
1359 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, TRUE
,
1360 STATUS_EDITOR_COLUMN_STATUS_ID
, id
,
1361 STATUS_EDITOR_COLUMN_STATUS_NAME
, name
,
1362 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, message
,
1363 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
1364 STATUS_EDITOR_COLUMN_STATUS_ICON
, stock
,
1368 gtk_widget_destroy(dialog
->window
);
1374 edit_substatus(StatusEditor
*status_editor
, PurpleAccount
*account
)
1377 SubStatusEditor
*dialog
;
1388 GtkCellRenderer
*rend
;
1389 char *status_id
= NULL
;
1390 char *message
= NULL
;
1391 gboolean parent_dialog_has_substatus
= FALSE
;
1393 gboolean select
= FALSE
;
1395 g_return_if_fail(status_editor
!= NULL
);
1396 g_return_if_fail(account
!= NULL
);
1398 status_editor_find_account_in_treemodel(&iter
, status_editor
, account
);
1399 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1400 STATUS_EDITOR_COLUMN_WINDOW
, &dialog
,
1404 gtk_window_present(GTK_WINDOW(dialog
->window
));
1408 dialog
= g_new0(SubStatusEditor
, 1);
1409 gtk_list_store_set(status_editor
->model
, &iter
,
1410 STATUS_EDITOR_COLUMN_WINDOW
, dialog
,
1412 dialog
->status_editor
= status_editor
;
1413 dialog
->account
= account
;
1415 tmp
= g_strdup_printf(_("Status for %s"), purple_account_get_username(account
));
1416 dialog
->window
= win
= pidgin_create_dialog(tmp
, PIDGIN_HIG_BORDER
, "substatus", TRUE
);
1419 g_signal_connect(G_OBJECT(win
), "destroy",
1420 G_CALLBACK(substatus_editor_destroy_cb
), dialog
);
1422 /* Setup the vbox */
1423 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1425 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1428 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
1429 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1431 label
= gtk_label_new_with_mnemonic(_("_Status:"));
1432 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
1433 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1434 gtk_size_group_add_widget(sg
, label
);
1436 dialog
->model
= gtk_list_store_new(STATUS_NUM_COLUMNS
,
1440 combo
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog
->model
));
1441 dialog
->box
= GTK_COMBO_BOX(combo
);
1443 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
1444 g_object_set(G_OBJECT(rend
),
1445 "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
),
1447 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, FALSE
);
1448 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1449 "stock-id", STATUS_COLUMN_ICON
, NULL
);
1451 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
1452 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, TRUE
);
1453 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1454 "text", STATUS_COLUMN_STATUS_NAME
, NULL
);
1456 g_signal_connect(G_OBJECT(combo
), "changed",
1457 G_CALLBACK(substatus_selection_changed_cb
), dialog
);
1459 gtk_box_pack_start(GTK_BOX(hbox
), combo
, FALSE
, FALSE
, 0);
1462 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
1463 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
1465 label
= gtk_label_new_with_mnemonic(_("_Message:"));
1466 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
1467 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1468 gtk_size_group_add_widget(sg
, label
);
1470 frame
= pidgin_create_imhtml(TRUE
, &text
, &toolbar
, NULL
);
1471 dialog
->message
= GTK_IMHTML(text
);
1472 dialog
->toolbar
= GTK_IMHTMLTOOLBAR(toolbar
);
1473 gtk_box_pack_start(GTK_BOX(hbox
), frame
, TRUE
, TRUE
, 0);
1476 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CANCEL
,
1477 G_CALLBACK(substatus_editor_cancel_cb
), dialog
);
1480 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_OK
,
1481 G_CALLBACK(substatus_editor_ok_cb
), dialog
);
1483 /* Seed the input widgets with the current values */
1485 /* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
1486 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1487 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &parent_dialog_has_substatus
, -1);
1488 if (parent_dialog_has_substatus
) {
1489 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1490 STATUS_EDITOR_COLUMN_STATUS_ID
, &status_id
,
1491 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, &message
, -1);
1492 } else if (status_editor
->original_title
!= NULL
) {
1493 PurpleSavedStatus
*saved_status
= NULL
;
1494 PurpleSavedStatusSub
*substatus
= NULL
;
1496 if ((saved_status
= purple_savedstatus_find(status_editor
->original_title
)) != NULL
) {
1497 if ((substatus
= purple_savedstatus_get_substatus(saved_status
, account
)) != NULL
) {
1498 message
= (char *)purple_savedstatus_substatus_get_message(substatus
);
1499 status_id
= (char *)purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus
));
1503 /* TODO: Else get the generic status type from our parent */
1506 gtk_imhtml_append_text(dialog
->message
, message
, 0);
1508 for (list
= purple_account_get_status_types(account
); list
; list
= list
->next
)
1510 PurpleStatusType
*status_type
;
1511 const char *id
, *name
;
1512 PurpleStatusPrimitive prim
;
1514 status_type
= list
->data
;
1517 * Only allow users to select statuses that are flagged as
1518 * "user settable" and that aren't independent.
1520 if (!purple_status_type_is_user_settable(status_type
) ||
1521 purple_status_type_is_independent(status_type
))
1524 id
= purple_status_type_get_id(status_type
);
1525 prim
= purple_status_type_get_primitive(status_type
);
1526 name
= purple_status_type_get_name(status_type
);
1528 gtk_list_store_append(dialog
->model
, &iter
);
1529 gtk_list_store_set(dialog
->model
, &iter
,
1530 STATUS_COLUMN_ICON
, pidgin_stock_id_from_status_primitive(prim
),
1531 STATUS_COLUMN_STATUS_ID
, id
,
1532 STATUS_COLUMN_STATUS_NAME
, name
,
1534 if ((status_id
!= NULL
) && purple_strequal(status_id
, id
))
1536 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo
), &iter
);
1542 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
1544 if (parent_dialog_has_substatus
) {
1545 /* These two were gotten from the parent tree model, so they need to be freed */
1550 gtk_widget_show_all(win
);
1555 /**************************************************************************
1557 **************************************************************************/
1560 SS_MENU_ENTRY_TYPE_PRIMITIVE
,
1561 SS_MENU_ENTRY_TYPE_SAVEDSTATUS
1565 /** _SSMenuEntryType */
1566 SS_MENU_TYPE_COLUMN
,
1569 * This is a GdkPixbuf (the other columns are strings).
1570 * This column is visible.
1572 SS_MENU_ICON_COLUMN
,
1574 /** The text displayed on the status box. This column is visible. */
1575 SS_MENU_TEXT_COLUMN
,
1578 * This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types,
1579 * this is the creation time. For _PRIMITIVE types,
1580 * this is the PurpleStatusPrimitive.
1582 SS_MENU_DATA_COLUMN
,
1585 * This is the emblem to use for this status
1587 SS_MENU_EMBLEM_COLUMN
,
1590 * And whether or not that emblem is visible
1592 SS_MENU_EMBLEM_VISIBLE_COLUMN
,
1598 status_menu_cb(GtkComboBox
*widget
, void(*callback
)(PurpleSavedStatus
*))
1603 PurpleSavedStatus
*status
= NULL
;
1605 if (!gtk_combo_box_get_active_iter(widget
, &iter
))
1608 gtk_tree_model_get(gtk_combo_box_get_model(widget
), &iter
,
1609 SS_MENU_TYPE_COLUMN
, &type
,
1610 SS_MENU_DATA_COLUMN
, &data
,
1613 if (type
== SS_MENU_ENTRY_TYPE_PRIMITIVE
)
1615 PurpleStatusPrimitive primitive
= GPOINTER_TO_INT(data
);
1616 status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, NULL
);
1618 status
= purple_savedstatus_new(NULL
, primitive
);
1620 else if (type
== SS_MENU_ENTRY_TYPE_SAVEDSTATUS
)
1621 status
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
1627 saved_status_sort_alphabetically_func(gconstpointer a
, gconstpointer b
)
1629 const PurpleSavedStatus
*saved_status_a
= a
;
1630 const PurpleSavedStatus
*saved_status_b
= b
;
1631 return g_utf8_collate(purple_savedstatus_get_title(saved_status_a
),
1632 purple_savedstatus_get_title(saved_status_b
));
1635 static gboolean
pidgin_status_menu_add_primitive(GtkListStore
*model
, GtkWidget
*w
, PurpleStatusPrimitive primitive
,
1636 PurpleSavedStatus
*current_status
)
1639 gboolean currently_selected
= FALSE
;
1641 gtk_list_store_append(model
, &iter
);
1642 gtk_list_store_set(model
, &iter
,
1643 SS_MENU_TYPE_COLUMN
, SS_MENU_ENTRY_TYPE_PRIMITIVE
,
1644 SS_MENU_ICON_COLUMN
, pidgin_stock_id_from_status_primitive(primitive
),
1645 SS_MENU_TEXT_COLUMN
, purple_primitive_get_name_from_type(primitive
),
1646 SS_MENU_DATA_COLUMN
, GINT_TO_POINTER(primitive
),
1647 SS_MENU_EMBLEM_VISIBLE_COLUMN
, FALSE
,
1650 if (purple_savedstatus_is_transient(current_status
)
1651 && !purple_savedstatus_has_substatuses(current_status
)
1652 && purple_savedstatus_get_type(current_status
) == primitive
)
1653 currently_selected
= TRUE
;
1655 return currently_selected
;
1659 pidgin_status_menu_update_iter(GtkWidget
*combobox
, GtkListStore
*store
, GtkTreeIter
*iter
,
1660 PurpleSavedStatus
*status
)
1662 PurpleStatusPrimitive primitive
;
1665 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1667 primitive
= purple_savedstatus_get_type(status
);
1668 gtk_list_store_set(store
, iter
,
1669 SS_MENU_TYPE_COLUMN
, SS_MENU_ENTRY_TYPE_SAVEDSTATUS
,
1670 SS_MENU_ICON_COLUMN
, pidgin_stock_id_from_status_primitive(primitive
),
1671 SS_MENU_TEXT_COLUMN
, purple_savedstatus_get_title(status
),
1672 SS_MENU_DATA_COLUMN
, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status
)),
1673 SS_MENU_EMBLEM_COLUMN
, GTK_STOCK_SAVE
,
1674 SS_MENU_EMBLEM_VISIBLE_COLUMN
, TRUE
,
1679 pidgin_status_menu_find_iter(GtkListStore
*store
, GtkTreeIter
*iter
, PurpleSavedStatus
*find
)
1683 time_t creation_time
= purple_savedstatus_get_creation_time(find
);
1684 GtkTreeModel
*model
= GTK_TREE_MODEL(store
);
1686 if (!gtk_tree_model_get_iter_first(model
, iter
))
1690 gtk_tree_model_get(model
, iter
,
1691 SS_MENU_TYPE_COLUMN
, &type
,
1692 SS_MENU_DATA_COLUMN
, &data
,
1694 if (type
== SS_MENU_ENTRY_TYPE_PRIMITIVE
)
1696 if (GPOINTER_TO_INT(data
) == creation_time
)
1698 } while (gtk_tree_model_iter_next(model
, iter
));
1704 savedstatus_added_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1706 GtkListStore
*store
;
1709 if (purple_savedstatus_is_transient(status
))
1712 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1713 gtk_list_store_append(store
, &iter
);
1714 pidgin_status_menu_update_iter(combobox
, store
, &iter
, status
);
1718 savedstatus_deleted_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1720 GtkListStore
*store
;
1723 if (purple_savedstatus_is_transient(status
))
1726 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1727 if (pidgin_status_menu_find_iter(store
, &iter
, status
))
1728 gtk_list_store_remove(store
, &iter
);
1732 savedstatus_modified_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1734 GtkListStore
*store
;
1737 if (purple_savedstatus_is_transient(status
))
1740 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1741 if (pidgin_status_menu_find_iter(store
, &iter
, status
))
1742 pidgin_status_menu_update_iter(combobox
, store
, &iter
, status
);
1745 GtkWidget
*pidgin_status_menu(PurpleSavedStatus
*current_status
, GCallback callback
)
1747 GtkWidget
*combobox
;
1748 GtkListStore
*model
;
1749 GList
*sorted
, *cur
;
1753 GtkCellRenderer
*text_rend
;
1754 GtkCellRenderer
*icon_rend
;
1755 GtkCellRenderer
*emblem_rend
;
1757 model
= gtk_list_store_new(SS_MENU_NUM_COLUMNS
, G_TYPE_INT
, G_TYPE_STRING
,
1758 G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1760 combobox
= gtk_combo_box_new();
1762 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_AVAILABLE
, current_status
))
1766 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_AWAY
, current_status
))
1770 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_INVISIBLE
, current_status
))
1774 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_OFFLINE
, current_status
))
1778 sorted
= g_list_copy((GList
*)purple_savedstatuses_get_all());
1779 sorted
= g_list_sort(sorted
, saved_status_sort_alphabetically_func
);
1780 for (cur
= sorted
; cur
; cur
= cur
->next
)
1782 PurpleSavedStatus
*status
= (PurpleSavedStatus
*) cur
->data
;
1783 if (!purple_savedstatus_is_transient(status
))
1785 gtk_list_store_append(model
, &iter
);
1787 pidgin_status_menu_update_iter(combobox
, model
, &iter
, status
);
1789 if (status
== current_status
)
1794 g_list_free(sorted
);
1796 gtk_combo_box_set_model(GTK_COMBO_BOX(combobox
), GTK_TREE_MODEL(model
));
1798 text_rend
= gtk_cell_renderer_text_new();
1799 icon_rend
= gtk_cell_renderer_pixbuf_new();
1800 emblem_rend
= gtk_cell_renderer_pixbuf_new();
1801 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), icon_rend
, FALSE
);
1802 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), text_rend
, TRUE
);
1803 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), emblem_rend
, FALSE
);
1804 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), icon_rend
, "stock-id", SS_MENU_ICON_COLUMN
, NULL
);
1805 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), text_rend
, "markup", SS_MENU_TEXT_COLUMN
, NULL
);
1806 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), emblem_rend
,
1807 "stock-id", SS_MENU_EMBLEM_COLUMN
, "visible", SS_MENU_EMBLEM_VISIBLE_COLUMN
, NULL
);
1808 g_object_set(G_OBJECT(icon_rend
),
1809 "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
),
1811 g_object_set(text_rend
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1813 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox
), index
);
1814 g_signal_connect(G_OBJECT(combobox
), "changed", G_CALLBACK(status_menu_cb
), callback
);
1816 /* Make sure the list is updated dynamically when a substatus is changed/deleted
1817 * or a new one is added. */
1818 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added",
1819 combobox
, G_CALLBACK(savedstatus_added_cb
), combobox
);
1820 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted",
1821 combobox
, G_CALLBACK(savedstatus_deleted_cb
), combobox
);
1822 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified",
1823 combobox
, G_CALLBACK(savedstatus_modified_cb
), combobox
);
1824 g_signal_connect(G_OBJECT(combobox
), "destroy",
1825 G_CALLBACK(purple_signals_disconnect_by_handle
), NULL
);
1831 /**************************************************************************
1832 * GTK+ saved status glue
1833 **************************************************************************/
1836 pidgin_status_get_handle(void)
1844 pidgin_status_init(void)
1846 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/status");
1847 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/status/dialog");
1848 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/status/dialog/width", 550);
1849 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/status/dialog/height", 250);
1853 pidgin_status_uninit(void)
1855 pidgin_status_window_hide();