3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29 #include "savedstatuses.h"
35 #include "gtksavedstatuses.h"
36 #include "pidginstock.h"
39 #include "gtk3compat.h"
42 * TODO: Should attach to the account-deleted and account-added signals
43 * and update the GtkListStores in any StatusEditor windows that
48 * These are used for the GtkTreeView when you're scrolling through
49 * all your saved statuses.
53 STATUS_WINDOW_COLUMN_TITLE
,
54 STATUS_WINDOW_COLUMN_TYPE
,
55 STATUS_WINDOW_COLUMN_MESSAGE
,
56 /** A hidden column containing a pointer to the editor for this saved status. */
57 STATUS_WINDOW_COLUMN_WINDOW
,
58 STATUS_WINDOW_COLUMN_ICON
,
59 STATUS_WINDOW_NUM_COLUMNS
63 * These are used for the GtkTreeView containing the list of accounts
64 * at the bottom of the window when you're editing a particular
69 /* A hidden column containing a pointer to the PurpleAccount. */
70 STATUS_EDITOR_COLUMN_ACCOUNT
,
71 /* A hidden column containing a pointer to the editor for this substatus. */
72 STATUS_EDITOR_COLUMN_WINDOW
,
73 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
,
74 STATUS_EDITOR_COLUMN_ICON
,
75 STATUS_EDITOR_COLUMN_USERNAME
,
76 /* A hidden column containing the ID of this PurpleStatusType. */
77 STATUS_EDITOR_COLUMN_STATUS_ID
,
78 STATUS_EDITOR_COLUMN_STATUS_NAME
,
79 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
,
80 STATUS_EDITOR_COLUMN_STATUS_ICON
,
81 STATUS_EDITOR_NUM_COLUMNS
85 * These are used in the GtkComboBox to select the specific PurpleStatusType
86 * when setting a (sub)status for a particular saved status.
91 /** A hidden column containing the ID of this PurpleStatusType. */
92 STATUS_COLUMN_STATUS_ID
,
93 STATUS_COLUMN_STATUS_NAME
,
102 GtkWidget
*use_button
;
103 GtkWidget
*modify_button
;
104 GtkWidget
*delete_button
;
112 GtkButton
*saveanduse_button
;
113 GtkButton
*save_button
;
115 gchar
*original_title
;
118 GtkWidget
*message_view
;
119 GtkTextBuffer
*message_buffer
;
124 StatusEditor
*status_editor
;
125 PurpleAccount
*account
;
130 GtkWidget
*message_view
;
131 GtkTextBuffer
*message_buffer
;
134 static StatusWindow
*status_window
= NULL
;
137 /**************************************************************************
139 **************************************************************************/
142 status_window_find_savedstatus(GtkTreeIter
*iter
, const char *title
)
147 if ((status_window
== NULL
) || (title
== NULL
))
150 model
= GTK_TREE_MODEL(status_window
->model
);
152 if (!gtk_tree_model_get_iter_first(model
, iter
))
156 gtk_tree_model_get(model
, iter
, STATUS_WINDOW_COLUMN_TITLE
, &cur
, -1);
157 if (purple_strequal(title
, cur
))
163 } while (gtk_tree_model_iter_next(model
, iter
));
169 status_window_destroy_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
171 StatusWindow
*dialog
= user_data
;
173 dialog
->window
= NULL
;
174 pidgin_status_window_hide();
180 status_window_use_cb(GtkButton
*button
, StatusWindow
*dialog
)
182 GtkTreeSelection
*selection
;
185 int num_selected
= 0;
187 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
189 num_selected
= gtk_tree_selection_count_selected_rows(selection
);
190 if (num_selected
!= 1)
192 * This shouldn't happen because the "Use" button should have
193 * been grayed out. Oh well.
197 list
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
199 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog
->model
),
203 PurpleSavedStatus
*saved_status
;
204 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
205 STATUS_WINDOW_COLUMN_TITLE
, &title
,
207 saved_status
= purple_savedstatus_find(title
);
209 purple_savedstatus_activate(saved_status
);
212 g_list_foreach(list
, (GFunc
)gtk_tree_path_free
, NULL
);
217 status_window_add_cb(GtkButton
*button
, gpointer user_data
)
219 pidgin_status_editor_show(FALSE
, NULL
);
223 status_window_modify_foreach(GtkTreeModel
*model
, GtkTreePath
*path
,
224 GtkTreeIter
*iter
, gpointer user_data
)
227 PurpleSavedStatus
*saved_status
;
229 gtk_tree_model_get(model
, iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
230 saved_status
= purple_savedstatus_find(title
);
232 pidgin_status_editor_show(TRUE
, saved_status
);
236 status_window_modify_cb(GtkButton
*button
, gpointer user_data
)
238 StatusWindow
*dialog
= user_data
;
239 GtkTreeSelection
*selection
;
241 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
243 gtk_tree_selection_selected_foreach(selection
, status_window_modify_foreach
, user_data
);
247 status_window_delete_cancel_cb(gpointer data
)
249 GList
*sel_titles
= data
;
250 g_list_foreach(sel_titles
, (GFunc
) g_free
, NULL
);
251 g_list_free(sel_titles
);
255 status_window_delete_confirm_cb(gpointer data
)
258 GList
*sel_titles
= data
, *l
;
261 for (l
= sel_titles
; l
!= NULL
; l
= l
->next
) {
263 if (purple_savedstatus_find(title
) != purple_savedstatus_get_current()) {
264 if (status_window_find_savedstatus(&iter
, title
))
265 gtk_list_store_remove(status_window
->model
, &iter
);
266 purple_savedstatus_delete(title
);
270 g_list_free(sel_titles
);
274 status_window_delete_cb(GtkButton
*button
, gpointer user_data
)
276 StatusWindow
*dialog
= user_data
;
278 GtkTreeSelection
*selection
;
279 GList
*sel_paths
, *l
, *sel_titles
= NULL
;
280 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
284 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
285 sel_paths
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
287 /* This is ugly because we're not allowed to modify the model from within
288 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid
289 * when something is removed from the model. The selection can also change while
290 * the request dialog is displayed, so we need to capture the selected rows at this time. */
292 for (l
= sel_paths
; l
!= NULL
; l
= l
->next
) {
293 if (gtk_tree_model_get_iter(model
, &iter
, l
->data
)) {
294 gtk_tree_model_get(model
, &iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
295 sel_titles
= g_list_prepend(sel_titles
, title
);
297 gtk_tree_path_free(l
->data
);
299 g_list_free(sel_paths
);
301 g_return_if_fail(sel_titles
!= NULL
);
302 if (!sel_titles
->next
) {
303 title
= g_strdup_printf(_("Are you sure you want to delete %s?"),
304 (const gchar
*)sel_titles
->data
);
305 handle
= purple_savedstatus_find(sel_titles
->data
);
307 title
= g_strdup(_("Are you sure you want to delete the selected saved statuses?"));
311 purple_request_action(handle
, NULL
, title
, NULL
, 0,
314 _("Delete"), status_window_delete_confirm_cb
,
315 _("Cancel"), status_window_delete_cancel_cb
);
321 status_window_close_cb(GtkButton
*button
, gpointer user_data
)
323 pidgin_status_window_hide();
327 status_selected_cb(GtkTreeSelection
*sel
, gpointer user_data
)
329 StatusWindow
*dialog
= user_data
;
330 GList
*sel_paths
, *tmp
;
331 gboolean can_use
= TRUE
, can_delete
= TRUE
;
333 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
335 sel_paths
= gtk_tree_selection_get_selected_rows(sel
, NULL
);
337 for (tmp
= sel_paths
, num_selected
= 0; tmp
; tmp
= tmp
->next
, num_selected
++) {
341 if (gtk_tree_model_get_iter(model
, &iter
, tmp
->data
)) {
342 gtk_tree_model_get(model
, &iter
,
343 STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
344 if (purple_savedstatus_find(title
) == purple_savedstatus_get_current()) {
345 can_use
= can_delete
= FALSE
;
351 gtk_tree_path_free(tmp
->data
);
354 gtk_widget_set_sensitive(dialog
->use_button
, (num_selected
== 1) && can_use
);
355 gtk_widget_set_sensitive(dialog
->modify_button
, (num_selected
> 0));
356 gtk_widget_set_sensitive(dialog
->delete_button
, num_selected
> 0 && can_delete
);
358 g_list_free(sel_paths
);
362 get_stock_icon_from_primitive(PurpleStatusPrimitive type
)
364 return pidgin_stock_id_from_status_primitive(type
);
368 add_status_to_saved_status_list(GtkListStore
*model
, PurpleSavedStatus
*saved_status
)
376 if (purple_savedstatus_is_transient(saved_status
))
379 title
= purple_savedstatus_get_title(saved_status
);
380 type
= purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status
));
381 message
= purple_markup_strip_html(purple_savedstatus_get_message(saved_status
));
382 icon
= get_stock_icon_from_primitive(purple_savedstatus_get_primitive_type(saved_status
));
384 gtk_list_store_append(model
, &iter
);
385 gtk_list_store_set(model
, &iter
,
386 STATUS_WINDOW_COLUMN_ICON
, icon
,
387 STATUS_WINDOW_COLUMN_TITLE
, title
,
388 STATUS_WINDOW_COLUMN_TYPE
, type
,
389 STATUS_WINDOW_COLUMN_MESSAGE
, message
,
395 populate_saved_status_list(StatusWindow
*dialog
)
397 GList
*saved_statuses
;
399 gtk_list_store_clear(dialog
->model
);
401 for (saved_statuses
= purple_savedstatuses_get_all(); saved_statuses
!= NULL
;
402 saved_statuses
= g_list_next(saved_statuses
))
404 add_status_to_saved_status_list(dialog
->model
, saved_statuses
->data
);
409 search_func(GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer search_data
)
414 gtk_tree_model_get(model
, iter
, column
, &haystack
, -1);
416 result
= (purple_strcasestr(haystack
, key
) == NULL
);
424 savedstatus_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, StatusWindow
*dialog
)
426 status_window_use_cb(NULL
, dialog
);
427 status_window_close_cb(NULL
, dialog
);
431 saved_status_updated_cb(PurpleSavedStatus
*status
, StatusWindow
*sw
)
433 populate_saved_status_list(sw
);
437 create_saved_status_list(StatusWindow
*dialog
)
440 GtkTreeSelection
*sel
;
441 GtkTreeViewColumn
*column
;
442 GtkCellRenderer
*renderer
;
444 /* Create the list model */
445 dialog
->model
= gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS
,
452 /* Create the treeview */
453 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog
->model
));
454 dialog
->treeview
= treeview
;
455 g_signal_connect(G_OBJECT(treeview
), "row-activated",
456 G_CALLBACK(savedstatus_activated_cb
), dialog
);
458 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
459 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
460 g_signal_connect(G_OBJECT(sel
), "changed",
461 G_CALLBACK(status_selected_cb
), dialog
);
464 column
= gtk_tree_view_column_new();
465 gtk_tree_view_column_set_title(column
, _("Title"));
466 gtk_tree_view_column_set_resizable(column
, TRUE
);
467 gtk_tree_view_column_set_min_width(column
, 100);
468 gtk_tree_view_column_set_sort_column_id(column
,
469 STATUS_WINDOW_COLUMN_TITLE
);
470 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
471 renderer
= gtk_cell_renderer_text_new();
472 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
473 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
474 STATUS_WINDOW_COLUMN_TITLE
);
475 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
477 column
= gtk_tree_view_column_new();
478 gtk_tree_view_column_set_title(column
, _("Type"));
479 gtk_tree_view_column_set_resizable(column
, TRUE
);
480 gtk_tree_view_column_set_sort_column_id(column
,
481 STATUS_WINDOW_COLUMN_TYPE
);
482 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
483 renderer
= gtk_cell_renderer_pixbuf_new();
484 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
485 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
486 STATUS_WINDOW_COLUMN_ICON
);
487 renderer
= gtk_cell_renderer_text_new();
488 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
489 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
490 STATUS_WINDOW_COLUMN_TYPE
);
492 column
= gtk_tree_view_column_new();
493 gtk_tree_view_column_set_title(column
, _("Message"));
494 gtk_tree_view_column_set_resizable(column
, TRUE
);
495 gtk_tree_view_column_set_sort_column_id(column
,
496 STATUS_WINDOW_COLUMN_MESSAGE
);
497 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
498 renderer
= gtk_cell_renderer_text_new();
499 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
500 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
501 STATUS_WINDOW_COLUMN_MESSAGE
);
502 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
504 /* Enable CTRL+F searching */
505 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview
), STATUS_WINDOW_COLUMN_TITLE
);
506 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview
), search_func
, NULL
, NULL
);
508 /* Sort the title column by default */
509 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog
->model
),
510 STATUS_WINDOW_COLUMN_TITLE
,
514 populate_saved_status_list(dialog
);
516 gtk_widget_show_all(treeview
);
518 return pidgin_make_scrollable(treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1);
522 configure_cb(GtkWidget
*widget
, GdkEventConfigure
*event
, StatusWindow
*dialog
)
524 if (gtk_widget_get_visible(widget
))
526 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/width", event
->width
);
527 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/height", event
->height
);
534 current_status_changed(PurpleSavedStatus
*old
, PurpleSavedStatus
*new_status
,
535 StatusWindow
*dialog
)
537 status_selected_cb(gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
)), dialog
);
541 pidgin_status_window_show(void)
543 StatusWindow
*dialog
;
551 if (status_window
!= NULL
)
553 gtk_window_present(GTK_WINDOW(status_window
->window
));
557 status_window
= dialog
= g_new0(StatusWindow
, 1);
559 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/width");
560 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/height");
562 dialog
->window
= win
= pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER
, "statuses", TRUE
);
563 gtk_window_set_default_size(GTK_WINDOW(win
), width
, height
);
565 g_signal_connect(G_OBJECT(win
), "delete_event",
566 G_CALLBACK(status_window_destroy_cb
), dialog
);
567 g_signal_connect(G_OBJECT(win
), "configure_event",
568 G_CALLBACK(configure_cb
), dialog
);
571 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
573 /* List of saved status states */
574 list
= create_saved_status_list(dialog
);
575 gtk_box_pack_start(GTK_BOX(vbox
), list
, TRUE
, TRUE
, 0);
578 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
581 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
582 PIDGIN_BUTTON_HORIZONTAL
);
583 dialog
->use_button
= button
;
584 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
585 gtk_widget_set_sensitive(button
, FALSE
);
587 g_signal_connect(G_OBJECT(button
), "clicked",
588 G_CALLBACK(status_window_use_cb
), dialog
);
591 pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_ADD
,
592 G_CALLBACK(status_window_add_cb
), dialog
);
595 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_MODIFY
,
596 G_CALLBACK(status_window_modify_cb
), dialog
);
597 dialog
->modify_button
= button
;
598 gtk_widget_set_sensitive(button
, FALSE
);
601 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_DELETE
,
602 G_CALLBACK(status_window_delete_cb
), dialog
);
603 dialog
->delete_button
= button
;
604 gtk_widget_set_sensitive(button
, FALSE
);
607 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CLOSE
,
608 G_CALLBACK(status_window_close_cb
), dialog
);
610 purple_signal_connect(purple_savedstatuses_get_handle(),
611 "savedstatus-changed", status_window
,
612 PURPLE_CALLBACK(current_status_changed
), dialog
);
613 purple_signal_connect(purple_savedstatuses_get_handle(),
614 "savedstatus-added", status_window
,
615 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
616 purple_signal_connect(purple_savedstatuses_get_handle(),
617 "savedstatus-deleted", status_window
,
618 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
619 purple_signal_connect(purple_savedstatuses_get_handle(),
620 "savedstatus-modified", status_window
,
621 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
623 gtk_widget_show_all(win
);
627 pidgin_status_window_hide(void)
629 if (status_window
== NULL
)
632 if (status_window
->window
!= NULL
)
633 gtk_widget_destroy(status_window
->window
);
635 purple_request_close_with_handle(status_window
);
636 purple_notify_close_with_handle(status_window
);
637 purple_signals_disconnect_by_handle(status_window
);
638 g_object_unref(G_OBJECT(status_window
->model
));
639 g_free(status_window
);
640 status_window
= NULL
;
644 /**************************************************************************
646 **************************************************************************/
648 static void substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
);
651 status_editor_remove_dialog(StatusEditor
*dialog
)
656 /* Remove the reference to this dialog from our parent's list store */
657 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
659 gtk_list_store_set(status_window
->model
, &iter
,
660 STATUS_WINDOW_COLUMN_WINDOW
, NULL
,
664 /* Close any substatus editors that may be open */
665 model
= GTK_TREE_MODEL(dialog
->model
);
666 if (gtk_tree_model_get_iter_first(model
, &iter
))
669 SubStatusEditor
*substatus_dialog
;
671 gtk_tree_model_get(model
, &iter
,
672 STATUS_EDITOR_COLUMN_WINDOW
, &substatus_dialog
,
674 if (substatus_dialog
!= NULL
)
676 gtk_list_store_set(dialog
->model
, &iter
,
677 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
679 substatus_editor_cancel_cb(NULL
, substatus_dialog
);
681 } while (gtk_tree_model_iter_next(model
, &iter
));
687 status_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
689 StatusEditor
*dialog
= user_data
;
691 status_editor_remove_dialog(dialog
);
692 g_free(dialog
->original_title
);
693 g_object_unref(G_OBJECT(dialog
->model
));
698 status_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
700 StatusEditor
*dialog
= user_data
;
701 gtk_widget_destroy(dialog
->window
);
705 status_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
707 StatusEditor
*dialog
= user_data
;
709 PurpleStatusPrimitive type
;
710 char *message
, *unformatted
;
711 PurpleSavedStatus
*saved_status
= NULL
;
715 title
= gtk_entry_get_text(dialog
->title
);
718 * If we're saving this status, and the title is already taken
719 * then show an error dialog and don't do anything.
721 if (((button
== dialog
->saveanduse_button
) || (button
== dialog
->save_button
)) &&
722 (purple_savedstatus_find(title
) != NULL
) &&
723 ((dialog
->original_title
== NULL
) || (!purple_strequal(title
, dialog
->original_title
))))
725 purple_notify_error(status_window
, NULL
, _("Title already in use. You must "
726 "choose a unique title."), NULL
, NULL
);
730 type
= gtk_combo_box_get_active(dialog
->type
) + (PURPLE_STATUS_UNSET
+ 1);
731 message
= talkatu_markup_get_html(dialog
->message_buffer
, NULL
);
732 unformatted
= purple_markup_strip_html(message
);
735 * If we're editing an old status, then lookup the old status.
736 * Note: It is possible that it has been deleted or renamed
737 * or something, and no longer exists.
739 if (dialog
->original_title
!= NULL
)
743 saved_status
= purple_savedstatus_find(dialog
->original_title
);
745 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
746 gtk_list_store_remove(status_window
->model
, &iter
);
749 if (saved_status
== NULL
)
751 /* This is a new status */
752 if ((button
== dialog
->saveanduse_button
)
753 || (button
== dialog
->save_button
))
754 saved_status
= purple_savedstatus_new(title
, type
);
756 saved_status
= purple_savedstatus_new(NULL
, type
);
760 /* Modify the old status */
761 if (!purple_strequal(title
, dialog
->original_title
))
762 purple_savedstatus_set_title(saved_status
, title
);
763 purple_savedstatus_set_primitive_type(saved_status
, type
);
766 if (*unformatted
== '\0')
767 purple_savedstatus_set_message(saved_status
, NULL
);
769 purple_savedstatus_set_message(saved_status
, message
);
771 /* Set any substatuses */
772 model
= GTK_TREE_MODEL(dialog
->model
);
773 if (gtk_tree_model_get_iter_first(model
, &iter
))
776 PurpleAccount
*account
;
780 PurpleStatusType
*type
;
782 gtk_tree_model_get(model
, &iter
,
783 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
784 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
785 STATUS_EDITOR_COLUMN_STATUS_ID
, &id
,
786 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, &message
,
790 type
= purple_account_get_status_type(account
, id
);
791 purple_savedstatus_set_substatus(saved_status
, account
, type
, message
);
795 purple_savedstatus_unset_substatus(saved_status
, account
);
799 } while (gtk_tree_model_iter_next(model
, &iter
));
805 /* If they clicked on "Save and Use" or "Use," then activate the status */
806 if (button
!= dialog
->save_button
)
807 purple_savedstatus_activate(saved_status
);
809 gtk_widget_destroy(dialog
->window
);
813 editor_title_changed_cb(GtkWidget
*widget
, gpointer user_data
)
815 StatusEditor
*dialog
= user_data
;
818 text
= gtk_entry_get_text(dialog
->title
);
820 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->saveanduse_button
), (*text
!= '\0'));
821 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->save_button
), (*text
!= '\0'));
825 create_status_type_menu(PurpleStatusPrimitive type
)
831 GtkCellRenderer
*renderer
;
833 store
= gtk_list_store_new(STATUS_NUM_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
835 for (i
= PURPLE_STATUS_UNSET
+ 1; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
837 /* Someone should fix this for 3.0.0. The independent boolean
838 * should probably be set on the status type, not the status.
839 * I guess that would prevent third party plugins from creating
840 * independent statuses?
842 if (i
== PURPLE_STATUS_MOBILE
||
843 i
== PURPLE_STATUS_MOOD
||
844 i
== PURPLE_STATUS_TUNE
)
846 * Special-case these. They're intended to be independent
847 * status types, so don't show them in the list.
851 gtk_list_store_append(store
, &iter
);
852 gtk_list_store_set(store
, &iter
,
853 STATUS_COLUMN_ICON
, get_stock_icon_from_primitive(i
),
854 STATUS_COLUMN_STATUS_ID
, purple_primitive_get_id_from_type(i
),
855 STATUS_COLUMN_STATUS_NAME
, purple_primitive_get_name_from_type(i
),
859 dropdown
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(store
));
861 renderer
= gtk_cell_renderer_pixbuf_new();
862 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, FALSE
);
863 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
864 "stock-id", STATUS_COLUMN_ICON
,
867 renderer
= gtk_cell_renderer_text_new();
868 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, TRUE
);
869 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
870 "text", STATUS_COLUMN_STATUS_NAME
,
873 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown
),
874 type
- (PURPLE_STATUS_UNSET
+ 1));
879 static void edit_substatus(StatusEditor
*status_editor
, PurpleAccount
*account
);
882 edit_substatus_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, gpointer user_data
)
884 StatusEditor
*dialog
= user_data
;
886 PurpleAccount
*account
;
888 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog
->model
), &iter
, path
);
889 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
890 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
893 edit_substatus(dialog
, account
);
897 status_editor_substatus_cb(GtkCellRendererToggle
*renderer
, gchar
*path_str
, gpointer data
)
899 StatusEditor
*dialog
= (StatusEditor
*)data
;
902 PurpleAccount
*account
;
904 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog
->model
), &iter
, path_str
);
905 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
906 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
907 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
914 edit_substatus(dialog
, account
);
918 /* Remove the substatus */
919 gtk_list_store_set(dialog
->model
, &iter
,
920 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, enabled
,
921 STATUS_EDITOR_COLUMN_STATUS_ID
, NULL
,
922 STATUS_EDITOR_COLUMN_STATUS_NAME
, NULL
,
923 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, NULL
,
924 STATUS_EDITOR_COLUMN_STATUS_ICON
, NULL
,
930 status_editor_add_columns(StatusEditor
*dialog
)
932 GtkCellRenderer
*renderer
;
933 GtkTreeViewColumn
*column
;
935 /* Enable Different status column */
936 renderer
= gtk_cell_renderer_toggle_new();
937 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog
->treeview
),
940 "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
,
942 g_signal_connect(G_OBJECT(renderer
), "toggled",
943 G_CALLBACK(status_editor_substatus_cb
), dialog
);
945 /* Username column */
946 column
= gtk_tree_view_column_new();
947 gtk_tree_view_column_set_resizable(column
, TRUE
);
948 gtk_tree_view_column_set_title(column
, _("Username"));
949 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
950 gtk_tree_view_column_set_resizable(column
, TRUE
);
953 renderer
= gtk_cell_renderer_pixbuf_new();
954 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
955 gtk_tree_view_column_add_attribute(column
, renderer
, "pixbuf",
956 STATUS_EDITOR_COLUMN_ICON
);
959 renderer
= gtk_cell_renderer_text_new();
960 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
961 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
962 STATUS_EDITOR_COLUMN_USERNAME
);
965 column
= gtk_tree_view_column_new();
966 gtk_tree_view_column_set_resizable(column
, TRUE
);
967 gtk_tree_view_column_set_title(column
, _("Status"));
968 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
969 gtk_tree_view_column_set_resizable(column
, TRUE
);
970 renderer
= gtk_cell_renderer_pixbuf_new();
971 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
972 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
973 STATUS_EDITOR_COLUMN_STATUS_ICON
);
974 renderer
= gtk_cell_renderer_text_new();
975 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
976 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
977 STATUS_EDITOR_COLUMN_STATUS_NAME
);
980 column
= gtk_tree_view_column_new();
981 gtk_tree_view_column_set_resizable(column
, TRUE
);
982 gtk_tree_view_column_set_title(column
, _("Message"));
983 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
984 gtk_tree_view_column_set_resizable(column
, TRUE
);
985 renderer
= gtk_cell_renderer_text_new();
986 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
987 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
988 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
);
990 g_signal_connect(G_OBJECT(dialog
->treeview
), "row-activated",
991 G_CALLBACK(edit_substatus_cb
), dialog
);
995 status_editor_set_account(GtkListStore
*store
, PurpleAccount
*account
,
996 GtkTreeIter
*iter
, PurpleSavedStatusSub
*substatus
)
999 const char *id
= NULL
, *name
= NULL
, *message
= NULL
;
1000 PurpleStatusPrimitive prim
= PURPLE_STATUS_UNSET
;
1002 pixbuf
= pidgin_create_protocol_icon(account
, PIDGIN_PROTOCOL_ICON_MEDIUM
);
1003 if ((pixbuf
!= NULL
) && !purple_account_is_connected(account
))
1005 gdk_pixbuf_saturate_and_pixelate(pixbuf
, pixbuf
, 0.0, FALSE
);
1008 if (substatus
!= NULL
)
1010 const PurpleStatusType
*type
;
1012 type
= purple_savedstatus_substatus_get_status_type(substatus
);
1013 id
= purple_status_type_get_id(type
);
1014 name
= purple_status_type_get_name(type
);
1015 prim
= purple_status_type_get_primitive(type
);
1016 if (purple_status_type_get_attr(type
, "message"))
1017 message
= purple_savedstatus_substatus_get_message(substatus
);
1020 gtk_list_store_set(store
, iter
,
1021 STATUS_EDITOR_COLUMN_ACCOUNT
, account
,
1022 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, (substatus
!= NULL
),
1023 STATUS_EDITOR_COLUMN_ICON
, pixbuf
,
1024 STATUS_EDITOR_COLUMN_USERNAME
, purple_account_get_username(account
),
1025 STATUS_EDITOR_COLUMN_STATUS_ID
, id
,
1026 STATUS_EDITOR_COLUMN_STATUS_NAME
, name
,
1027 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, message
,
1028 STATUS_EDITOR_COLUMN_STATUS_ICON
, get_stock_icon_from_primitive(prim
),
1032 g_object_unref(G_OBJECT(pixbuf
));
1036 status_editor_add_account(StatusEditor
*dialog
, PurpleAccount
*account
,
1037 PurpleSavedStatusSub
*substatus
)
1041 gtk_list_store_append(dialog
->model
, &iter
);
1043 status_editor_set_account(dialog
->model
, account
, &iter
, substatus
);
1047 status_editor_populate_list(StatusEditor
*dialog
, PurpleSavedStatus
*saved_status
)
1050 PurpleSavedStatusSub
*substatus
;
1052 gtk_list_store_clear(dialog
->model
);
1054 for (iter
= purple_accounts_get_all(); iter
!= NULL
; iter
= iter
->next
)
1056 PurpleAccount
*account
= (PurpleAccount
*)iter
->data
;
1058 if (saved_status
!= NULL
)
1059 substatus
= purple_savedstatus_get_substatus(saved_status
, account
);
1063 status_editor_add_account(dialog
, account
, substatus
);
1068 pidgin_status_editor_show(gboolean edit
, PurpleSavedStatus
*saved_status
)
1071 StatusEditor
*dialog
;
1076 GtkWidget
*expander
;
1077 GtkWidget
*dropdown
;
1083 GList
*focus_chain
= NULL
;
1087 g_return_if_fail(saved_status
!= NULL
);
1088 g_return_if_fail(!purple_savedstatus_is_transient(saved_status
));
1091 /* Find a possible window for this saved status and present it */
1092 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1094 gtk_tree_model_get(GTK_TREE_MODEL(status_window
->model
), &iter
,
1095 STATUS_WINDOW_COLUMN_WINDOW
, &dialog
,
1099 gtk_window_present(GTK_WINDOW(dialog
->window
));
1104 dialog
= g_new0(StatusEditor
, 1);
1105 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1107 gtk_list_store_set(status_window
->model
, &iter
,
1108 STATUS_WINDOW_COLUMN_WINDOW
, dialog
,
1113 dialog
->original_title
= g_strdup(purple_savedstatus_get_title(saved_status
));
1115 dialog
->window
= win
= pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER
, "status", TRUE
);
1117 g_signal_connect(G_OBJECT(win
), "destroy",
1118 G_CALLBACK(status_editor_destroy_cb
), dialog
);
1120 /* Setup the vbox */
1121 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1123 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1126 entry
= gtk_entry_new();
1127 dialog
->title
= GTK_ENTRY(entry
);
1128 if ((saved_status
!= NULL
)
1129 && !purple_savedstatus_is_transient(saved_status
)
1130 && (purple_savedstatus_get_title(saved_status
) != NULL
))
1131 gtk_entry_set_text(GTK_ENTRY(entry
), purple_savedstatus_get_title(saved_status
));
1132 g_signal_connect(G_OBJECT(entry
), "changed",
1133 G_CALLBACK(editor_title_changed_cb
), dialog
);
1134 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Title:"), sg
, entry
, TRUE
, NULL
);
1137 if (saved_status
!= NULL
)
1138 dropdown
= create_status_type_menu(purple_savedstatus_get_primitive_type(saved_status
));
1140 dropdown
= create_status_type_menu(PURPLE_STATUS_AWAY
);
1141 dialog
->type
= GTK_COMBO_BOX(dropdown
);
1142 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Status:"), sg
, dropdown
, TRUE
, NULL
);
1144 /* Status message */
1145 editor
= talkatu_editor_new();
1147 dialog
->message_view
= talkatu_editor_get_view(TALKATU_EDITOR(editor
));
1148 hbox
= pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Message:"), sg
, editor
, TRUE
, NULL
);
1150 dialog
->message_buffer
= talkatu_html_buffer_new();
1151 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog
->message_view
), dialog
->message_buffer
);
1153 gtk_container_child_set(GTK_CONTAINER(vbox
), hbox
, "expand", TRUE
, "fill", TRUE
, NULL
);
1154 focus_chain
= g_list_prepend(focus_chain
, dialog
->message_view
);
1155 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), focus_chain
);
1156 g_list_free(focus_chain
);
1158 if ((saved_status
!= NULL
) && (purple_savedstatus_get_message(saved_status
) != NULL
)) {
1159 talkatu_markup_set_html(
1160 TALKATU_BUFFER(dialog
->message_buffer
),
1161 purple_savedstatus_get_message(saved_status
),
1166 /* Different status message expander */
1167 expander
= gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts"));
1168 gtk_box_pack_start(GTK_BOX(vbox
), expander
, FALSE
, FALSE
, 0);
1170 /* Setup the box that the expander will cover */
1171 dbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, PIDGIN_HIG_CAT_SPACE
);
1172 gtk_container_add(GTK_CONTAINER(expander
), dbox
);
1174 /* Create the list model */
1175 dialog
->model
= gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS
,
1186 /* Create the treeview */
1187 dialog
->treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog
->model
));
1188 gtk_widget_set_size_request(dialog
->treeview
, -1, 150);
1189 gtk_box_pack_start(GTK_BOX(dbox
),
1190 pidgin_make_scrollable(dialog
->treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
1194 status_editor_add_columns(dialog
);
1197 status_editor_populate_list(dialog
, saved_status
);
1199 /* Expand the treeview if we have substatuses */
1200 gtk_expander_set_expanded(GTK_EXPANDER(expander
),
1201 (saved_status
!= NULL
) && purple_savedstatus_has_substatuses(saved_status
));
1204 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
1205 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
1206 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
1209 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CANCEL
,
1210 G_CALLBACK(status_editor_cancel_cb
), dialog
);
1213 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
1214 PIDGIN_BUTTON_HORIZONTAL
);
1215 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1216 g_signal_connect(G_OBJECT(button
), "clicked",
1217 G_CALLBACK(status_editor_ok_cb
), dialog
);
1219 /* Save and Use button */
1220 button
= pidgin_pixbuf_button_from_stock(_("Sa_ve and Use"), GTK_STOCK_OK
,
1221 PIDGIN_BUTTON_HORIZONTAL
);
1222 dialog
->saveanduse_button
= GTK_BUTTON(button
);
1223 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1224 if (dialog
->original_title
== NULL
)
1225 gtk_widget_set_sensitive(button
, FALSE
);
1226 g_signal_connect(G_OBJECT(button
), "clicked",
1227 G_CALLBACK(status_editor_ok_cb
), dialog
);
1230 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_SAVE
,
1231 G_CALLBACK(status_editor_ok_cb
), dialog
);
1232 if (dialog
->original_title
== NULL
)
1233 gtk_widget_set_sensitive(button
, FALSE
);
1234 dialog
->save_button
= GTK_BUTTON(button
);
1236 gtk_widget_show_all(win
);
1241 /**************************************************************************
1243 **************************************************************************/
1246 substatus_selection_changed_cb(GtkComboBox
*box
, gpointer user_data
)
1248 SubStatusEditor
*select
= user_data
;
1251 PurpleStatusType
*type
;
1253 if (!gtk_combo_box_get_active_iter(box
, &iter
))
1255 gtk_tree_model_get(GTK_TREE_MODEL(select
->model
), &iter
,
1256 STATUS_COLUMN_STATUS_ID
, &id
,
1258 type
= purple_account_get_status_type(select
->account
, id
);
1261 if (purple_status_type_get_attr(type
, "message") == NULL
)
1263 gtk_widget_set_sensitive(select
->message_view
, FALSE
);
1267 gtk_widget_set_sensitive(select
->message_view
, TRUE
);
1272 status_editor_find_account_in_treemodel(GtkTreeIter
*iter
,
1273 StatusEditor
*status_editor
,
1274 PurpleAccount
*account
)
1276 GtkTreeModel
*model
;
1279 g_return_val_if_fail(status_editor
!= NULL
, FALSE
);
1280 g_return_val_if_fail(account
!= NULL
, FALSE
);
1282 model
= GTK_TREE_MODEL(status_editor
->model
);
1284 if (!gtk_tree_model_get_iter_first(model
, iter
))
1288 gtk_tree_model_get(model
, iter
, STATUS_EDITOR_COLUMN_ACCOUNT
, &cur
, -1);
1291 } while (gtk_tree_model_iter_next(model
, iter
));
1297 substatus_editor_remove_dialog(SubStatusEditor
*dialog
)
1301 if (status_editor_find_account_in_treemodel(&iter
, dialog
->status_editor
, dialog
->account
))
1303 /* Remove the reference to this dialog from our parent's list store */
1304 gtk_list_store_set(dialog
->status_editor
->model
, &iter
,
1305 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
1311 substatus_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
1313 SubStatusEditor
*dialog
= user_data
;
1315 substatus_editor_remove_dialog(dialog
);
1320 substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
1322 SubStatusEditor
*dialog
= user_data
;
1323 gtk_widget_destroy(dialog
->window
);
1328 substatus_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
1330 SubStatusEditor
*dialog
= user_data
;
1331 StatusEditor
*status_editor
;
1333 PurpleStatusType
*type
;
1335 char *message
= NULL
;
1336 const char *name
= NULL
, *stock
= NULL
;
1338 if (!gtk_combo_box_get_active_iter(dialog
->box
, &iter
))
1340 gtk_widget_destroy(dialog
->window
);
1344 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
1345 STATUS_COLUMN_STATUS_ID
, &id
,
1347 type
= purple_account_get_status_type(dialog
->account
, id
);
1348 if (purple_status_type_get_attr(type
, "message") != NULL
) {
1349 message
= talkatu_markup_get_html(dialog
->message_buffer
, NULL
);
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
;
1386 GtkCellRenderer
*rend
;
1387 char *status_id
= NULL
;
1388 char *message
= NULL
;
1389 gboolean parent_dialog_has_substatus
= FALSE
;
1391 gboolean select
= FALSE
;
1393 g_return_if_fail(status_editor
!= NULL
);
1394 g_return_if_fail(account
!= NULL
);
1396 status_editor_find_account_in_treemodel(&iter
, status_editor
, account
);
1397 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1398 STATUS_EDITOR_COLUMN_WINDOW
, &dialog
,
1402 gtk_window_present(GTK_WINDOW(dialog
->window
));
1406 dialog
= g_new0(SubStatusEditor
, 1);
1407 gtk_list_store_set(status_editor
->model
, &iter
,
1408 STATUS_EDITOR_COLUMN_WINDOW
, dialog
,
1410 dialog
->status_editor
= status_editor
;
1411 dialog
->account
= account
;
1413 tmp
= g_strdup_printf(_("Status for %s"), purple_account_get_username(account
));
1414 dialog
->window
= win
= pidgin_create_dialog(tmp
, PIDGIN_HIG_BORDER
, "substatus", TRUE
);
1417 g_signal_connect(G_OBJECT(win
), "destroy",
1418 G_CALLBACK(substatus_editor_destroy_cb
), dialog
);
1420 /* Setup the vbox */
1421 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1423 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1426 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
1427 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1429 label
= gtk_label_new_with_mnemonic(_("_Status:"));
1430 gtk_label_set_xalign(GTK_LABEL(label
), 0);
1431 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1432 gtk_size_group_add_widget(sg
, label
);
1434 dialog
->model
= gtk_list_store_new(STATUS_NUM_COLUMNS
,
1438 combo
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog
->model
));
1439 dialog
->box
= GTK_COMBO_BOX(combo
);
1441 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
1442 g_object_set(G_OBJECT(rend
),
1443 "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
),
1445 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, FALSE
);
1446 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1447 "stock-id", STATUS_COLUMN_ICON
, NULL
);
1449 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
1450 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, TRUE
);
1451 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1452 "text", STATUS_COLUMN_STATUS_NAME
, NULL
);
1454 g_signal_connect(G_OBJECT(combo
), "changed",
1455 G_CALLBACK(substatus_selection_changed_cb
), dialog
);
1457 gtk_box_pack_start(GTK_BOX(hbox
), combo
, FALSE
, FALSE
, 0);
1460 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
1461 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
1463 label
= gtk_label_new_with_mnemonic(_("_Message:"));
1464 gtk_label_set_xalign(GTK_LABEL(label
), 0);
1465 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1466 gtk_size_group_add_widget(sg
, label
);
1468 editor
= talkatu_editor_new();
1469 gtk_box_pack_start(GTK_BOX(hbox
), editor
, TRUE
, TRUE
, 0);
1471 dialog
->message_view
= talkatu_editor_get_view(TALKATU_EDITOR(editor
));
1472 dialog
->message_buffer
= talkatu_html_buffer_new();
1473 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog
->message_view
), dialog
->message_buffer
);
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(
1500 purple_savedstatus_substatus_get_status_type(substatus
));
1504 /* TODO: Else get the generic status type from our parent */
1507 talkatu_markup_set_html(TALKATU_BUFFER(dialog
->message_buffer
), message
, -1);
1510 for (list
= purple_account_get_status_types(account
); list
; list
= list
->next
)
1512 PurpleStatusType
*status_type
;
1513 const char *id
, *name
;
1514 PurpleStatusPrimitive prim
;
1516 status_type
= list
->data
;
1519 * Only allow users to select statuses that are flagged as
1520 * "user settable" and that aren't independent.
1522 if (!purple_status_type_is_user_settable(status_type
) ||
1523 purple_status_type_is_independent(status_type
))
1526 id
= purple_status_type_get_id(status_type
);
1527 prim
= purple_status_type_get_primitive(status_type
);
1528 name
= purple_status_type_get_name(status_type
);
1530 gtk_list_store_append(dialog
->model
, &iter
);
1531 gtk_list_store_set(dialog
->model
, &iter
,
1532 STATUS_COLUMN_ICON
, pidgin_stock_id_from_status_primitive(prim
),
1533 STATUS_COLUMN_STATUS_ID
, id
,
1534 STATUS_COLUMN_STATUS_NAME
, name
,
1536 if ((status_id
!= NULL
) && purple_strequal(status_id
, id
))
1538 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo
), &iter
);
1544 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
1546 if (parent_dialog_has_substatus
) {
1547 /* These two were gotten from the parent tree model, so they need to be freed */
1552 gtk_widget_show_all(win
);
1557 /**************************************************************************
1559 **************************************************************************/
1562 SS_MENU_ENTRY_TYPE_PRIMITIVE
,
1563 SS_MENU_ENTRY_TYPE_SAVEDSTATUS
1567 /* _SSMenuEntryType */
1568 SS_MENU_TYPE_COLUMN
,
1571 * This is a GdkPixbuf (the other columns are strings).
1572 * This column is visible.
1574 SS_MENU_ICON_COLUMN
,
1576 /* The text displayed on the status box. This column is visible. */
1577 SS_MENU_TEXT_COLUMN
,
1580 * This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types,
1581 * this is the creation time. For _PRIMITIVE types,
1582 * this is the PurpleStatusPrimitive.
1584 SS_MENU_DATA_COLUMN
,
1587 * This is the emblem to use for this status
1589 SS_MENU_EMBLEM_COLUMN
,
1592 * And whether or not that emblem is visible
1594 SS_MENU_EMBLEM_VISIBLE_COLUMN
,
1600 status_menu_cb(GtkComboBox
*widget
, void(*callback
)(PurpleSavedStatus
*))
1605 PurpleSavedStatus
*status
= NULL
;
1607 if (!gtk_combo_box_get_active_iter(widget
, &iter
))
1610 gtk_tree_model_get(gtk_combo_box_get_model(widget
), &iter
,
1611 SS_MENU_TYPE_COLUMN
, &type
,
1612 SS_MENU_DATA_COLUMN
, &data
,
1615 if (type
== SS_MENU_ENTRY_TYPE_PRIMITIVE
)
1617 PurpleStatusPrimitive primitive
= GPOINTER_TO_INT(data
);
1618 status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, NULL
);
1620 status
= purple_savedstatus_new(NULL
, primitive
);
1622 else if (type
== SS_MENU_ENTRY_TYPE_SAVEDSTATUS
)
1623 status
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
1629 saved_status_sort_alphabetically_func(gconstpointer a
, gconstpointer b
)
1631 const PurpleSavedStatus
*saved_status_a
= a
;
1632 const PurpleSavedStatus
*saved_status_b
= b
;
1633 return g_utf8_collate(purple_savedstatus_get_title(saved_status_a
),
1634 purple_savedstatus_get_title(saved_status_b
));
1637 static gboolean
pidgin_status_menu_add_primitive(GtkListStore
*model
, GtkWidget
*w
, PurpleStatusPrimitive primitive
,
1638 PurpleSavedStatus
*current_status
)
1641 gboolean currently_selected
= FALSE
;
1643 gtk_list_store_append(model
, &iter
);
1644 gtk_list_store_set(model
, &iter
,
1645 SS_MENU_TYPE_COLUMN
, SS_MENU_ENTRY_TYPE_PRIMITIVE
,
1646 SS_MENU_ICON_COLUMN
, pidgin_stock_id_from_status_primitive(primitive
),
1647 SS_MENU_TEXT_COLUMN
, purple_primitive_get_name_from_type(primitive
),
1648 SS_MENU_DATA_COLUMN
, GINT_TO_POINTER(primitive
),
1649 SS_MENU_EMBLEM_VISIBLE_COLUMN
, FALSE
,
1652 if (purple_savedstatus_is_transient(current_status
)
1653 && !purple_savedstatus_has_substatuses(current_status
)
1654 && purple_savedstatus_get_primitive_type(current_status
) == primitive
)
1655 currently_selected
= TRUE
;
1657 return currently_selected
;
1661 pidgin_status_menu_update_iter(GtkWidget
*combobox
, GtkListStore
*store
, GtkTreeIter
*iter
,
1662 PurpleSavedStatus
*status
)
1664 PurpleStatusPrimitive primitive
;
1667 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1669 primitive
= purple_savedstatus_get_primitive_type(status
);
1670 gtk_list_store_set(store
, iter
,
1671 SS_MENU_TYPE_COLUMN
, SS_MENU_ENTRY_TYPE_SAVEDSTATUS
,
1672 SS_MENU_ICON_COLUMN
, pidgin_stock_id_from_status_primitive(primitive
),
1673 SS_MENU_TEXT_COLUMN
, purple_savedstatus_get_title(status
),
1674 SS_MENU_DATA_COLUMN
, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status
)),
1675 SS_MENU_EMBLEM_COLUMN
, GTK_STOCK_SAVE
,
1676 SS_MENU_EMBLEM_VISIBLE_COLUMN
, TRUE
,
1681 pidgin_status_menu_find_iter(GtkListStore
*store
, GtkTreeIter
*iter
, PurpleSavedStatus
*find
)
1685 time_t creation_time
= purple_savedstatus_get_creation_time(find
);
1686 GtkTreeModel
*model
= GTK_TREE_MODEL(store
);
1688 if (!gtk_tree_model_get_iter_first(model
, iter
))
1692 gtk_tree_model_get(model
, iter
,
1693 SS_MENU_TYPE_COLUMN
, &type
,
1694 SS_MENU_DATA_COLUMN
, &data
,
1696 if (type
== SS_MENU_ENTRY_TYPE_PRIMITIVE
)
1698 if (GPOINTER_TO_INT(data
) == creation_time
)
1700 } while (gtk_tree_model_iter_next(model
, iter
));
1706 savedstatus_added_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1708 GtkListStore
*store
;
1711 if (purple_savedstatus_is_transient(status
))
1714 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1715 gtk_list_store_append(store
, &iter
);
1716 pidgin_status_menu_update_iter(combobox
, store
, &iter
, status
);
1720 savedstatus_deleted_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1722 GtkListStore
*store
;
1725 if (purple_savedstatus_is_transient(status
))
1728 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1729 if (pidgin_status_menu_find_iter(store
, &iter
, status
))
1730 gtk_list_store_remove(store
, &iter
);
1734 savedstatus_modified_cb(PurpleSavedStatus
*status
, GtkWidget
*combobox
)
1736 GtkListStore
*store
;
1739 if (purple_savedstatus_is_transient(status
))
1742 store
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox
)));
1743 if (pidgin_status_menu_find_iter(store
, &iter
, status
))
1744 pidgin_status_menu_update_iter(combobox
, store
, &iter
, status
);
1747 GtkWidget
*pidgin_status_menu(PurpleSavedStatus
*current_status
, GCallback callback
)
1749 GtkWidget
*combobox
;
1750 GtkListStore
*model
;
1751 GList
*sorted
, *cur
;
1755 GtkCellRenderer
*text_rend
;
1756 GtkCellRenderer
*icon_rend
;
1757 GtkCellRenderer
*emblem_rend
;
1759 model
= gtk_list_store_new(SS_MENU_NUM_COLUMNS
, G_TYPE_INT
, G_TYPE_STRING
,
1760 G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1762 combobox
= gtk_combo_box_new();
1764 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_AVAILABLE
, current_status
))
1768 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_AWAY
, current_status
))
1772 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_INVISIBLE
, current_status
))
1776 if (pidgin_status_menu_add_primitive(model
, combobox
, PURPLE_STATUS_OFFLINE
, current_status
))
1780 sorted
= g_list_copy((GList
*)purple_savedstatuses_get_all());
1781 sorted
= g_list_sort(sorted
, saved_status_sort_alphabetically_func
);
1782 for (cur
= sorted
; cur
; cur
= cur
->next
)
1784 PurpleSavedStatus
*status
= (PurpleSavedStatus
*) cur
->data
;
1785 if (!purple_savedstatus_is_transient(status
))
1787 gtk_list_store_append(model
, &iter
);
1789 pidgin_status_menu_update_iter(combobox
, model
, &iter
, status
);
1791 if (status
== current_status
)
1796 g_list_free(sorted
);
1798 gtk_combo_box_set_model(GTK_COMBO_BOX(combobox
), GTK_TREE_MODEL(model
));
1800 text_rend
= gtk_cell_renderer_text_new();
1801 icon_rend
= gtk_cell_renderer_pixbuf_new();
1802 emblem_rend
= gtk_cell_renderer_pixbuf_new();
1803 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), icon_rend
, FALSE
);
1804 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), text_rend
, TRUE
);
1805 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox
), emblem_rend
, FALSE
);
1806 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), icon_rend
, "stock-id", SS_MENU_ICON_COLUMN
, NULL
);
1807 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), text_rend
, "markup", SS_MENU_TEXT_COLUMN
, NULL
);
1808 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox
), emblem_rend
,
1809 "stock-id", SS_MENU_EMBLEM_COLUMN
, "visible", SS_MENU_EMBLEM_VISIBLE_COLUMN
, NULL
);
1810 g_object_set(G_OBJECT(icon_rend
),
1811 "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
),
1813 g_object_set(text_rend
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1815 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox
), index
);
1816 g_signal_connect(G_OBJECT(combobox
), "changed", G_CALLBACK(status_menu_cb
), callback
);
1818 /* Make sure the list is updated dynamically when a substatus is changed/deleted
1819 * or a new one is added. */
1820 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added",
1821 combobox
, G_CALLBACK(savedstatus_added_cb
), combobox
);
1822 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted",
1823 combobox
, G_CALLBACK(savedstatus_deleted_cb
), combobox
);
1824 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified",
1825 combobox
, G_CALLBACK(savedstatus_modified_cb
), combobox
);
1826 g_signal_connect(G_OBJECT(combobox
), "destroy",
1827 G_CALLBACK(purple_signals_disconnect_by_handle
), NULL
);
1833 /**************************************************************************
1834 * GTK+ saved status glue
1835 **************************************************************************/
1838 pidgin_status_get_handle(void)
1846 pidgin_status_init(void)
1848 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/status");
1849 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/status/dialog");
1850 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/status/dialog/width", 550);
1851 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/status/dialog/height", 250);
1855 pidgin_status_uninit(void)
1857 pidgin_status_window_hide();