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_free_full(list
, (GDestroyNotify
)gtk_tree_path_free
);
216 status_window_add_cb(GtkButton
*button
, gpointer user_data
)
218 pidgin_status_editor_show(FALSE
, NULL
);
222 status_window_modify_foreach(GtkTreeModel
*model
, GtkTreePath
*path
,
223 GtkTreeIter
*iter
, gpointer user_data
)
226 PurpleSavedStatus
*saved_status
;
228 gtk_tree_model_get(model
, iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
229 saved_status
= purple_savedstatus_find(title
);
231 pidgin_status_editor_show(TRUE
, saved_status
);
235 status_window_modify_cb(GtkButton
*button
, gpointer user_data
)
237 StatusWindow
*dialog
= user_data
;
238 GtkTreeSelection
*selection
;
240 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
242 gtk_tree_selection_selected_foreach(selection
, status_window_modify_foreach
, user_data
);
246 status_window_delete_cancel_cb(gpointer data
)
248 GList
*sel_titles
= data
;
249 g_list_free_full(sel_titles
, g_free
);
253 status_window_delete_confirm_cb(gpointer data
)
256 GList
*sel_titles
= data
, *l
;
259 for (l
= sel_titles
; l
!= NULL
; l
= l
->next
) {
261 if (purple_savedstatus_find(title
) != purple_savedstatus_get_current()) {
262 if (status_window_find_savedstatus(&iter
, title
))
263 gtk_list_store_remove(status_window
->model
, &iter
);
264 purple_savedstatus_delete(title
);
268 g_list_free(sel_titles
);
272 status_window_delete_cb(GtkButton
*button
, gpointer user_data
)
274 StatusWindow
*dialog
= user_data
;
276 GtkTreeSelection
*selection
;
277 GList
*sel_paths
, *l
, *sel_titles
= NULL
;
278 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
282 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
));
283 sel_paths
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
285 /* This is ugly because we're not allowed to modify the model from within
286 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid
287 * when something is removed from the model. The selection can also change while
288 * the request dialog is displayed, so we need to capture the selected rows at this time. */
290 for (l
= sel_paths
; l
!= NULL
; l
= l
->next
) {
291 if (gtk_tree_model_get_iter(model
, &iter
, l
->data
)) {
292 gtk_tree_model_get(model
, &iter
, STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
293 sel_titles
= g_list_prepend(sel_titles
, title
);
295 gtk_tree_path_free(l
->data
);
297 g_list_free(sel_paths
);
299 g_return_if_fail(sel_titles
!= NULL
);
300 if (!sel_titles
->next
) {
301 title
= g_strdup_printf(_("Are you sure you want to delete %s?"),
302 (const gchar
*)sel_titles
->data
);
303 handle
= purple_savedstatus_find(sel_titles
->data
);
305 title
= g_strdup(_("Are you sure you want to delete the selected saved statuses?"));
309 purple_request_action(handle
, NULL
, title
, NULL
, 0,
312 _("Delete"), status_window_delete_confirm_cb
,
313 _("Cancel"), status_window_delete_cancel_cb
);
319 status_window_close_cb(GtkButton
*button
, gpointer user_data
)
321 pidgin_status_window_hide();
325 status_selected_cb(GtkTreeSelection
*sel
, gpointer user_data
)
327 StatusWindow
*dialog
= user_data
;
328 GList
*sel_paths
, *tmp
;
329 gboolean can_use
= TRUE
, can_delete
= TRUE
;
331 GtkTreeModel
*model
= GTK_TREE_MODEL(dialog
->model
);
333 sel_paths
= gtk_tree_selection_get_selected_rows(sel
, NULL
);
335 for (tmp
= sel_paths
, num_selected
= 0; tmp
; tmp
= tmp
->next
, num_selected
++) {
339 if (gtk_tree_model_get_iter(model
, &iter
, tmp
->data
)) {
340 gtk_tree_model_get(model
, &iter
,
341 STATUS_WINDOW_COLUMN_TITLE
, &title
, -1);
342 if (purple_savedstatus_find(title
) == purple_savedstatus_get_current()) {
343 can_use
= can_delete
= FALSE
;
349 gtk_tree_path_free(tmp
->data
);
352 gtk_widget_set_sensitive(dialog
->use_button
, (num_selected
== 1) && can_use
);
353 gtk_widget_set_sensitive(dialog
->modify_button
, (num_selected
> 0));
354 gtk_widget_set_sensitive(dialog
->delete_button
, num_selected
> 0 && can_delete
);
356 g_list_free(sel_paths
);
360 get_stock_icon_from_primitive(PurpleStatusPrimitive type
)
362 return pidgin_stock_id_from_status_primitive(type
);
366 add_status_to_saved_status_list(GtkListStore
*model
, PurpleSavedStatus
*saved_status
)
374 if (purple_savedstatus_is_transient(saved_status
))
377 title
= purple_savedstatus_get_title(saved_status
);
378 type
= purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status
));
379 message
= purple_markup_strip_html(purple_savedstatus_get_message(saved_status
));
380 icon
= get_stock_icon_from_primitive(purple_savedstatus_get_primitive_type(saved_status
));
382 gtk_list_store_append(model
, &iter
);
383 gtk_list_store_set(model
, &iter
,
384 STATUS_WINDOW_COLUMN_ICON
, icon
,
385 STATUS_WINDOW_COLUMN_TITLE
, title
,
386 STATUS_WINDOW_COLUMN_TYPE
, type
,
387 STATUS_WINDOW_COLUMN_MESSAGE
, message
,
393 populate_saved_status_list(StatusWindow
*dialog
)
395 GList
*saved_statuses
;
397 gtk_list_store_clear(dialog
->model
);
399 for (saved_statuses
= purple_savedstatuses_get_all(); saved_statuses
!= NULL
;
400 saved_statuses
= g_list_next(saved_statuses
))
402 add_status_to_saved_status_list(dialog
->model
, saved_statuses
->data
);
407 search_func(GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer search_data
)
412 gtk_tree_model_get(model
, iter
, column
, &haystack
, -1);
414 result
= (purple_strcasestr(haystack
, key
) == NULL
);
422 savedstatus_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, StatusWindow
*dialog
)
424 status_window_use_cb(NULL
, dialog
);
425 status_window_close_cb(NULL
, dialog
);
429 saved_status_updated_cb(PurpleSavedStatus
*status
, StatusWindow
*sw
)
431 populate_saved_status_list(sw
);
435 create_saved_status_list(StatusWindow
*dialog
)
438 GtkTreeSelection
*sel
;
439 GtkTreeViewColumn
*column
;
440 GtkCellRenderer
*renderer
;
442 /* Create the list model */
443 dialog
->model
= gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS
,
450 /* Create the treeview */
451 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog
->model
));
452 dialog
->treeview
= treeview
;
453 g_signal_connect(G_OBJECT(treeview
), "row-activated",
454 G_CALLBACK(savedstatus_activated_cb
), dialog
);
456 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
457 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
458 g_signal_connect(G_OBJECT(sel
), "changed",
459 G_CALLBACK(status_selected_cb
), dialog
);
462 column
= gtk_tree_view_column_new();
463 gtk_tree_view_column_set_title(column
, _("Title"));
464 gtk_tree_view_column_set_resizable(column
, TRUE
);
465 gtk_tree_view_column_set_min_width(column
, 100);
466 gtk_tree_view_column_set_sort_column_id(column
,
467 STATUS_WINDOW_COLUMN_TITLE
);
468 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
469 renderer
= gtk_cell_renderer_text_new();
470 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
471 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
472 STATUS_WINDOW_COLUMN_TITLE
);
473 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
475 column
= gtk_tree_view_column_new();
476 gtk_tree_view_column_set_title(column
, _("Type"));
477 gtk_tree_view_column_set_resizable(column
, TRUE
);
478 gtk_tree_view_column_set_sort_column_id(column
,
479 STATUS_WINDOW_COLUMN_TYPE
);
480 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
481 renderer
= gtk_cell_renderer_pixbuf_new();
482 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
483 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
484 STATUS_WINDOW_COLUMN_ICON
);
485 renderer
= gtk_cell_renderer_text_new();
486 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
487 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
488 STATUS_WINDOW_COLUMN_TYPE
);
490 column
= gtk_tree_view_column_new();
491 gtk_tree_view_column_set_title(column
, _("Message"));
492 gtk_tree_view_column_set_resizable(column
, TRUE
);
493 gtk_tree_view_column_set_sort_column_id(column
,
494 STATUS_WINDOW_COLUMN_MESSAGE
);
495 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview
), column
);
496 renderer
= gtk_cell_renderer_text_new();
497 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
498 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
499 STATUS_WINDOW_COLUMN_MESSAGE
);
500 g_object_set(renderer
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
502 /* Enable CTRL+F searching */
503 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview
), STATUS_WINDOW_COLUMN_TITLE
);
504 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview
), search_func
, NULL
, NULL
);
506 /* Sort the title column by default */
507 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog
->model
),
508 STATUS_WINDOW_COLUMN_TITLE
,
512 populate_saved_status_list(dialog
);
514 gtk_widget_show_all(treeview
);
516 return pidgin_make_scrollable(treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1);
520 configure_cb(GtkWidget
*widget
, GdkEventConfigure
*event
, StatusWindow
*dialog
)
522 if (gtk_widget_get_visible(widget
))
524 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/width", event
->width
);
525 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/status/dialog/height", event
->height
);
532 current_status_changed(PurpleSavedStatus
*old
, PurpleSavedStatus
*new_status
,
533 StatusWindow
*dialog
)
535 status_selected_cb(gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog
->treeview
)), dialog
);
539 pidgin_status_window_show(void)
541 StatusWindow
*dialog
;
549 if (status_window
!= NULL
)
551 gtk_window_present(GTK_WINDOW(status_window
->window
));
555 status_window
= dialog
= g_new0(StatusWindow
, 1);
557 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/width");
558 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/status/dialog/height");
560 dialog
->window
= win
= pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER
, "statuses", TRUE
);
561 gtk_window_set_default_size(GTK_WINDOW(win
), width
, height
);
563 g_signal_connect(G_OBJECT(win
), "delete_event",
564 G_CALLBACK(status_window_destroy_cb
), dialog
);
565 g_signal_connect(G_OBJECT(win
), "configure_event",
566 G_CALLBACK(configure_cb
), dialog
);
569 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
571 /* List of saved status states */
572 list
= create_saved_status_list(dialog
);
573 gtk_box_pack_start(GTK_BOX(vbox
), list
, TRUE
, TRUE
, 0);
576 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
579 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
580 PIDGIN_BUTTON_HORIZONTAL
);
581 dialog
->use_button
= button
;
582 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
583 gtk_widget_set_sensitive(button
, FALSE
);
585 g_signal_connect(G_OBJECT(button
), "clicked",
586 G_CALLBACK(status_window_use_cb
), dialog
);
589 pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_ADD
,
590 G_CALLBACK(status_window_add_cb
), dialog
);
593 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), PIDGIN_STOCK_MODIFY
,
594 G_CALLBACK(status_window_modify_cb
), dialog
);
595 dialog
->modify_button
= button
;
596 gtk_widget_set_sensitive(button
, FALSE
);
599 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_DELETE
,
600 G_CALLBACK(status_window_delete_cb
), dialog
);
601 dialog
->delete_button
= button
;
602 gtk_widget_set_sensitive(button
, FALSE
);
605 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CLOSE
,
606 G_CALLBACK(status_window_close_cb
), dialog
);
608 purple_signal_connect(purple_savedstatuses_get_handle(),
609 "savedstatus-changed", status_window
,
610 PURPLE_CALLBACK(current_status_changed
), dialog
);
611 purple_signal_connect(purple_savedstatuses_get_handle(),
612 "savedstatus-added", status_window
,
613 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
614 purple_signal_connect(purple_savedstatuses_get_handle(),
615 "savedstatus-deleted", status_window
,
616 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
617 purple_signal_connect(purple_savedstatuses_get_handle(),
618 "savedstatus-modified", status_window
,
619 PURPLE_CALLBACK(saved_status_updated_cb
), dialog
);
621 gtk_widget_show_all(win
);
625 pidgin_status_window_hide(void)
627 if (status_window
== NULL
)
630 if (status_window
->window
!= NULL
)
631 gtk_widget_destroy(status_window
->window
);
633 purple_request_close_with_handle(status_window
);
634 purple_notify_close_with_handle(status_window
);
635 purple_signals_disconnect_by_handle(status_window
);
636 g_object_unref(G_OBJECT(status_window
->model
));
637 g_free(status_window
);
638 status_window
= NULL
;
642 /**************************************************************************
644 **************************************************************************/
646 static void substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
);
649 status_editor_remove_dialog(StatusEditor
*dialog
)
654 /* Remove the reference to this dialog from our parent's list store */
655 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
657 gtk_list_store_set(status_window
->model
, &iter
,
658 STATUS_WINDOW_COLUMN_WINDOW
, NULL
,
662 /* Close any substatus editors that may be open */
663 model
= GTK_TREE_MODEL(dialog
->model
);
664 if (gtk_tree_model_get_iter_first(model
, &iter
))
667 SubStatusEditor
*substatus_dialog
;
669 gtk_tree_model_get(model
, &iter
,
670 STATUS_EDITOR_COLUMN_WINDOW
, &substatus_dialog
,
672 if (substatus_dialog
!= NULL
)
674 gtk_list_store_set(dialog
->model
, &iter
,
675 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
677 substatus_editor_cancel_cb(NULL
, substatus_dialog
);
679 } while (gtk_tree_model_iter_next(model
, &iter
));
685 status_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
687 StatusEditor
*dialog
= user_data
;
689 status_editor_remove_dialog(dialog
);
690 g_free(dialog
->original_title
);
691 g_object_unref(G_OBJECT(dialog
->model
));
696 status_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
698 StatusEditor
*dialog
= user_data
;
699 gtk_widget_destroy(dialog
->window
);
703 status_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
705 StatusEditor
*dialog
= user_data
;
707 PurpleStatusPrimitive type
;
708 char *message
, *unformatted
;
709 PurpleSavedStatus
*saved_status
= NULL
;
713 title
= gtk_entry_get_text(dialog
->title
);
716 * If we're saving this status, and the title is already taken
717 * then show an error dialog and don't do anything.
719 if (((button
== dialog
->saveanduse_button
) || (button
== dialog
->save_button
)) &&
720 (purple_savedstatus_find(title
) != NULL
) &&
721 ((dialog
->original_title
== NULL
) || (!purple_strequal(title
, dialog
->original_title
))))
723 purple_notify_error(status_window
, NULL
, _("Title already in use. You must "
724 "choose a unique title."), NULL
, NULL
);
728 type
= gtk_combo_box_get_active(dialog
->type
) + (PURPLE_STATUS_UNSET
+ 1);
729 message
= talkatu_markup_get_html(dialog
->message_buffer
, NULL
);
730 unformatted
= purple_markup_strip_html(message
);
733 * If we're editing an old status, then lookup the old status.
734 * Note: It is possible that it has been deleted or renamed
735 * or something, and no longer exists.
737 if (dialog
->original_title
!= NULL
)
741 saved_status
= purple_savedstatus_find(dialog
->original_title
);
743 if (status_window_find_savedstatus(&iter
, dialog
->original_title
))
744 gtk_list_store_remove(status_window
->model
, &iter
);
747 if (saved_status
== NULL
)
749 /* This is a new status */
750 if ((button
== dialog
->saveanduse_button
)
751 || (button
== dialog
->save_button
))
752 saved_status
= purple_savedstatus_new(title
, type
);
754 saved_status
= purple_savedstatus_new(NULL
, type
);
758 /* Modify the old status */
759 if (!purple_strequal(title
, dialog
->original_title
))
760 purple_savedstatus_set_title(saved_status
, title
);
761 purple_savedstatus_set_primitive_type(saved_status
, type
);
764 if (*unformatted
== '\0')
765 purple_savedstatus_set_message(saved_status
, NULL
);
767 purple_savedstatus_set_message(saved_status
, message
);
769 /* Set any substatuses */
770 model
= GTK_TREE_MODEL(dialog
->model
);
771 if (gtk_tree_model_get_iter_first(model
, &iter
))
774 PurpleAccount
*account
;
778 PurpleStatusType
*type
;
780 gtk_tree_model_get(model
, &iter
,
781 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
782 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
783 STATUS_EDITOR_COLUMN_STATUS_ID
, &id
,
784 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, &message
,
788 type
= purple_account_get_status_type(account
, id
);
789 purple_savedstatus_set_substatus(saved_status
, account
, type
, message
);
793 purple_savedstatus_unset_substatus(saved_status
, account
);
797 } while (gtk_tree_model_iter_next(model
, &iter
));
803 /* If they clicked on "Save and Use" or "Use," then activate the status */
804 if (button
!= dialog
->save_button
)
805 purple_savedstatus_activate(saved_status
);
807 gtk_widget_destroy(dialog
->window
);
811 editor_title_changed_cb(GtkWidget
*widget
, gpointer user_data
)
813 StatusEditor
*dialog
= user_data
;
816 text
= gtk_entry_get_text(dialog
->title
);
818 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->saveanduse_button
), (*text
!= '\0'));
819 gtk_widget_set_sensitive(GTK_WIDGET(dialog
->save_button
), (*text
!= '\0'));
823 create_status_type_menu(PurpleStatusPrimitive type
)
829 GtkCellRenderer
*renderer
;
831 store
= gtk_list_store_new(STATUS_NUM_COLUMNS
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
833 for (i
= PURPLE_STATUS_UNSET
+ 1; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
835 /* Someone should fix this for 3.0.0. The independent boolean
836 * should probably be set on the status type, not the status.
837 * I guess that would prevent third party plugins from creating
838 * independent statuses?
840 if (i
== PURPLE_STATUS_MOBILE
||
841 i
== PURPLE_STATUS_MOOD
||
842 i
== PURPLE_STATUS_TUNE
)
844 * Special-case these. They're intended to be independent
845 * status types, so don't show them in the list.
849 gtk_list_store_append(store
, &iter
);
850 gtk_list_store_set(store
, &iter
,
851 STATUS_COLUMN_ICON
, get_stock_icon_from_primitive(i
),
852 STATUS_COLUMN_STATUS_ID
, purple_primitive_get_id_from_type(i
),
853 STATUS_COLUMN_STATUS_NAME
, purple_primitive_get_name_from_type(i
),
857 dropdown
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(store
));
859 renderer
= gtk_cell_renderer_pixbuf_new();
860 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, FALSE
);
861 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
862 "stock-id", STATUS_COLUMN_ICON
,
865 renderer
= gtk_cell_renderer_text_new();
866 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown
), renderer
, TRUE
);
867 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown
), renderer
,
868 "text", STATUS_COLUMN_STATUS_NAME
,
871 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown
),
872 type
- (PURPLE_STATUS_UNSET
+ 1));
877 static void edit_substatus(StatusEditor
*status_editor
, PurpleAccount
*account
);
880 edit_substatus_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, gpointer user_data
)
882 StatusEditor
*dialog
= user_data
;
884 PurpleAccount
*account
;
886 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog
->model
), &iter
, path
);
887 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
888 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
891 edit_substatus(dialog
, account
);
895 status_editor_substatus_cb(GtkCellRendererToggle
*renderer
, gchar
*path_str
, gpointer data
)
897 StatusEditor
*dialog
= (StatusEditor
*)data
;
900 PurpleAccount
*account
;
902 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog
->model
), &iter
, path_str
);
903 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
904 STATUS_EDITOR_COLUMN_ACCOUNT
, &account
,
905 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &enabled
,
912 edit_substatus(dialog
, account
);
916 /* Remove the substatus */
917 gtk_list_store_set(dialog
->model
, &iter
,
918 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, enabled
,
919 STATUS_EDITOR_COLUMN_STATUS_ID
, NULL
,
920 STATUS_EDITOR_COLUMN_STATUS_NAME
, NULL
,
921 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, NULL
,
922 STATUS_EDITOR_COLUMN_STATUS_ICON
, NULL
,
928 status_editor_add_columns(StatusEditor
*dialog
)
930 GtkCellRenderer
*renderer
;
931 GtkTreeViewColumn
*column
;
933 /* Enable Different status column */
934 renderer
= gtk_cell_renderer_toggle_new();
935 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog
->treeview
),
938 "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
,
940 g_signal_connect(G_OBJECT(renderer
), "toggled",
941 G_CALLBACK(status_editor_substatus_cb
), dialog
);
943 /* Username column */
944 column
= gtk_tree_view_column_new();
945 gtk_tree_view_column_set_resizable(column
, TRUE
);
946 gtk_tree_view_column_set_title(column
, _("Username"));
947 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
948 gtk_tree_view_column_set_resizable(column
, TRUE
);
951 renderer
= gtk_cell_renderer_pixbuf_new();
952 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
953 gtk_tree_view_column_add_attribute(column
, renderer
, "pixbuf",
954 STATUS_EDITOR_COLUMN_ICON
);
957 renderer
= gtk_cell_renderer_text_new();
958 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
959 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
960 STATUS_EDITOR_COLUMN_USERNAME
);
963 column
= gtk_tree_view_column_new();
964 gtk_tree_view_column_set_resizable(column
, TRUE
);
965 gtk_tree_view_column_set_title(column
, _("Status"));
966 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
967 gtk_tree_view_column_set_resizable(column
, TRUE
);
968 renderer
= gtk_cell_renderer_pixbuf_new();
969 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
970 gtk_tree_view_column_add_attribute(column
, renderer
, "stock-id",
971 STATUS_EDITOR_COLUMN_STATUS_ICON
);
972 renderer
= gtk_cell_renderer_text_new();
973 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
974 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
975 STATUS_EDITOR_COLUMN_STATUS_NAME
);
978 column
= gtk_tree_view_column_new();
979 gtk_tree_view_column_set_resizable(column
, TRUE
);
980 gtk_tree_view_column_set_title(column
, _("Message"));
981 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog
->treeview
), column
, -1);
982 gtk_tree_view_column_set_resizable(column
, TRUE
);
983 renderer
= gtk_cell_renderer_text_new();
984 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
985 gtk_tree_view_column_add_attribute(column
, renderer
, "text",
986 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
);
988 g_signal_connect(G_OBJECT(dialog
->treeview
), "row-activated",
989 G_CALLBACK(edit_substatus_cb
), dialog
);
993 status_editor_set_account(GtkListStore
*store
, PurpleAccount
*account
,
994 GtkTreeIter
*iter
, PurpleSavedStatusSub
*substatus
)
997 const char *id
= NULL
, *name
= NULL
, *message
= NULL
;
998 PurpleStatusPrimitive prim
= PURPLE_STATUS_UNSET
;
1000 pixbuf
= pidgin_create_protocol_icon(account
, PIDGIN_PROTOCOL_ICON_MEDIUM
);
1001 if ((pixbuf
!= NULL
) && !purple_account_is_connected(account
))
1003 gdk_pixbuf_saturate_and_pixelate(pixbuf
, pixbuf
, 0.0, FALSE
);
1006 if (substatus
!= NULL
)
1008 const PurpleStatusType
*type
;
1010 type
= purple_savedstatus_substatus_get_status_type(substatus
);
1011 id
= purple_status_type_get_id(type
);
1012 name
= purple_status_type_get_name(type
);
1013 prim
= purple_status_type_get_primitive(type
);
1014 if (purple_status_type_get_attr(type
, "message"))
1015 message
= purple_savedstatus_substatus_get_message(substatus
);
1018 gtk_list_store_set(store
, iter
,
1019 STATUS_EDITOR_COLUMN_ACCOUNT
, account
,
1020 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, (substatus
!= NULL
),
1021 STATUS_EDITOR_COLUMN_ICON
, pixbuf
,
1022 STATUS_EDITOR_COLUMN_USERNAME
, purple_account_get_username(account
),
1023 STATUS_EDITOR_COLUMN_STATUS_ID
, id
,
1024 STATUS_EDITOR_COLUMN_STATUS_NAME
, name
,
1025 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, message
,
1026 STATUS_EDITOR_COLUMN_STATUS_ICON
, get_stock_icon_from_primitive(prim
),
1030 g_object_unref(G_OBJECT(pixbuf
));
1034 status_editor_add_account(StatusEditor
*dialog
, PurpleAccount
*account
,
1035 PurpleSavedStatusSub
*substatus
)
1039 gtk_list_store_append(dialog
->model
, &iter
);
1041 status_editor_set_account(dialog
->model
, account
, &iter
, substatus
);
1045 status_editor_populate_list(StatusEditor
*dialog
, PurpleSavedStatus
*saved_status
)
1048 PurpleSavedStatusSub
*substatus
;
1050 gtk_list_store_clear(dialog
->model
);
1052 for (iter
= purple_accounts_get_all(); iter
!= NULL
; iter
= iter
->next
)
1054 PurpleAccount
*account
= (PurpleAccount
*)iter
->data
;
1056 if (saved_status
!= NULL
)
1057 substatus
= purple_savedstatus_get_substatus(saved_status
, account
);
1061 status_editor_add_account(dialog
, account
, substatus
);
1066 pidgin_status_editor_show(gboolean edit
, PurpleSavedStatus
*saved_status
)
1069 StatusEditor
*dialog
;
1074 GtkWidget
*expander
;
1075 GtkWidget
*dropdown
;
1081 GList
*focus_chain
= NULL
;
1085 g_return_if_fail(saved_status
!= NULL
);
1086 g_return_if_fail(!purple_savedstatus_is_transient(saved_status
));
1089 /* Find a possible window for this saved status and present it */
1090 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1092 gtk_tree_model_get(GTK_TREE_MODEL(status_window
->model
), &iter
,
1093 STATUS_WINDOW_COLUMN_WINDOW
, &dialog
,
1097 gtk_window_present(GTK_WINDOW(dialog
->window
));
1102 dialog
= g_new0(StatusEditor
, 1);
1103 if (edit
&& status_window_find_savedstatus(&iter
, purple_savedstatus_get_title(saved_status
)))
1105 gtk_list_store_set(status_window
->model
, &iter
,
1106 STATUS_WINDOW_COLUMN_WINDOW
, dialog
,
1111 dialog
->original_title
= g_strdup(purple_savedstatus_get_title(saved_status
));
1113 dialog
->window
= win
= pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER
, "status", TRUE
);
1115 g_signal_connect(G_OBJECT(win
), "destroy",
1116 G_CALLBACK(status_editor_destroy_cb
), dialog
);
1118 /* Setup the vbox */
1119 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1121 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1124 entry
= gtk_entry_new();
1125 dialog
->title
= GTK_ENTRY(entry
);
1126 if ((saved_status
!= NULL
)
1127 && !purple_savedstatus_is_transient(saved_status
)
1128 && (purple_savedstatus_get_title(saved_status
) != NULL
))
1129 gtk_entry_set_text(GTK_ENTRY(entry
), purple_savedstatus_get_title(saved_status
));
1130 g_signal_connect(G_OBJECT(entry
), "changed",
1131 G_CALLBACK(editor_title_changed_cb
), dialog
);
1132 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Title:"), sg
, entry
, TRUE
, NULL
);
1135 if (saved_status
!= NULL
)
1136 dropdown
= create_status_type_menu(purple_savedstatus_get_primitive_type(saved_status
));
1138 dropdown
= create_status_type_menu(PURPLE_STATUS_AWAY
);
1139 dialog
->type
= GTK_COMBO_BOX(dropdown
);
1140 pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Status:"), sg
, dropdown
, TRUE
, NULL
);
1142 /* Status message */
1143 editor
= talkatu_editor_new();
1145 dialog
->message_view
= talkatu_editor_get_view(TALKATU_EDITOR(editor
));
1146 hbox
= pidgin_add_widget_to_vbox(GTK_BOX(vbox
), _("_Message:"), sg
, editor
, TRUE
, NULL
);
1148 dialog
->message_buffer
= talkatu_html_buffer_new();
1149 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog
->message_view
), dialog
->message_buffer
);
1151 gtk_container_child_set(GTK_CONTAINER(vbox
), hbox
, "expand", TRUE
, "fill", TRUE
, NULL
);
1152 focus_chain
= g_list_prepend(focus_chain
, dialog
->message_view
);
1153 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), focus_chain
);
1154 g_list_free(focus_chain
);
1156 if ((saved_status
!= NULL
) && (purple_savedstatus_get_message(saved_status
) != NULL
)) {
1157 talkatu_markup_set_html(
1158 TALKATU_BUFFER(dialog
->message_buffer
),
1159 purple_savedstatus_get_message(saved_status
),
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_box_new(GTK_ORIENTATION_VERTICAL
, 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_widget_set_size_request(dialog
->treeview
, -1, 150);
1187 gtk_box_pack_start(GTK_BOX(dbox
),
1188 pidgin_make_scrollable(dialog
->treeview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1),
1192 status_editor_add_columns(dialog
);
1195 status_editor_populate_list(dialog
, saved_status
);
1197 /* Expand the treeview if we have substatuses */
1198 gtk_expander_set_expanded(GTK_EXPANDER(expander
),
1199 (saved_status
!= NULL
) && purple_savedstatus_has_substatuses(saved_status
));
1202 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(win
));
1203 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
1204 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
1207 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CANCEL
,
1208 G_CALLBACK(status_editor_cancel_cb
), dialog
);
1211 button
= pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE
,
1212 PIDGIN_BUTTON_HORIZONTAL
);
1213 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1214 g_signal_connect(G_OBJECT(button
), "clicked",
1215 G_CALLBACK(status_editor_ok_cb
), dialog
);
1217 /* Save and Use button */
1218 button
= pidgin_pixbuf_button_from_stock(_("Sa_ve and Use"), GTK_STOCK_OK
,
1219 PIDGIN_BUTTON_HORIZONTAL
);
1220 dialog
->saveanduse_button
= GTK_BUTTON(button
);
1221 gtk_box_pack_start(GTK_BOX(bbox
), button
, FALSE
, FALSE
, 0);
1222 if (dialog
->original_title
== NULL
)
1223 gtk_widget_set_sensitive(button
, FALSE
);
1224 g_signal_connect(G_OBJECT(button
), "clicked",
1225 G_CALLBACK(status_editor_ok_cb
), dialog
);
1228 button
= pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_SAVE
,
1229 G_CALLBACK(status_editor_ok_cb
), dialog
);
1230 if (dialog
->original_title
== NULL
)
1231 gtk_widget_set_sensitive(button
, FALSE
);
1232 dialog
->save_button
= GTK_BUTTON(button
);
1234 gtk_widget_show_all(win
);
1239 /**************************************************************************
1241 **************************************************************************/
1244 substatus_selection_changed_cb(GtkComboBox
*box
, gpointer user_data
)
1246 SubStatusEditor
*select
= user_data
;
1249 PurpleStatusType
*type
;
1251 if (!gtk_combo_box_get_active_iter(box
, &iter
))
1253 gtk_tree_model_get(GTK_TREE_MODEL(select
->model
), &iter
,
1254 STATUS_COLUMN_STATUS_ID
, &id
,
1256 type
= purple_account_get_status_type(select
->account
, id
);
1259 if (purple_status_type_get_attr(type
, "message") == NULL
)
1261 gtk_widget_set_sensitive(select
->message_view
, FALSE
);
1265 gtk_widget_set_sensitive(select
->message_view
, TRUE
);
1270 status_editor_find_account_in_treemodel(GtkTreeIter
*iter
,
1271 StatusEditor
*status_editor
,
1272 PurpleAccount
*account
)
1274 GtkTreeModel
*model
;
1277 g_return_val_if_fail(status_editor
!= NULL
, FALSE
);
1278 g_return_val_if_fail(account
!= NULL
, FALSE
);
1280 model
= GTK_TREE_MODEL(status_editor
->model
);
1282 if (!gtk_tree_model_get_iter_first(model
, iter
))
1286 gtk_tree_model_get(model
, iter
, STATUS_EDITOR_COLUMN_ACCOUNT
, &cur
, -1);
1289 } while (gtk_tree_model_iter_next(model
, iter
));
1295 substatus_editor_remove_dialog(SubStatusEditor
*dialog
)
1299 if (status_editor_find_account_in_treemodel(&iter
, dialog
->status_editor
, dialog
->account
))
1301 /* Remove the reference to this dialog from our parent's list store */
1302 gtk_list_store_set(dialog
->status_editor
->model
, &iter
,
1303 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
1309 substatus_editor_destroy_cb(GtkWidget
*widget
, gpointer user_data
)
1311 SubStatusEditor
*dialog
= user_data
;
1313 substatus_editor_remove_dialog(dialog
);
1318 substatus_editor_cancel_cb(GtkButton
*button
, gpointer user_data
)
1320 SubStatusEditor
*dialog
= user_data
;
1321 gtk_widget_destroy(dialog
->window
);
1326 substatus_editor_ok_cb(GtkButton
*button
, gpointer user_data
)
1328 SubStatusEditor
*dialog
= user_data
;
1329 StatusEditor
*status_editor
;
1331 PurpleStatusType
*type
;
1333 char *message
= NULL
;
1334 const char *name
= NULL
, *stock
= NULL
;
1336 if (!gtk_combo_box_get_active_iter(dialog
->box
, &iter
))
1338 gtk_widget_destroy(dialog
->window
);
1342 gtk_tree_model_get(GTK_TREE_MODEL(dialog
->model
), &iter
,
1343 STATUS_COLUMN_STATUS_ID
, &id
,
1345 type
= purple_account_get_status_type(dialog
->account
, id
);
1346 if (purple_status_type_get_attr(type
, "message") != NULL
) {
1347 message
= talkatu_markup_get_html(dialog
->message_buffer
, NULL
);
1349 name
= purple_status_type_get_name(type
);
1350 stock
= get_stock_icon_from_primitive(purple_status_type_get_primitive(type
));
1352 status_editor
= dialog
->status_editor
;
1354 if (status_editor_find_account_in_treemodel(&iter
, status_editor
, dialog
->account
))
1356 gtk_list_store_set(status_editor
->model
, &iter
,
1357 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, TRUE
,
1358 STATUS_EDITOR_COLUMN_STATUS_ID
, id
,
1359 STATUS_EDITOR_COLUMN_STATUS_NAME
, name
,
1360 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, message
,
1361 STATUS_EDITOR_COLUMN_WINDOW
, NULL
,
1362 STATUS_EDITOR_COLUMN_STATUS_ICON
, stock
,
1366 gtk_widget_destroy(dialog
->window
);
1372 edit_substatus(StatusEditor
*status_editor
, PurpleAccount
*account
)
1375 SubStatusEditor
*dialog
;
1384 GtkCellRenderer
*rend
;
1385 char *status_id
= NULL
;
1386 char *message
= NULL
;
1387 gboolean parent_dialog_has_substatus
= FALSE
;
1389 gboolean select
= FALSE
;
1391 g_return_if_fail(status_editor
!= NULL
);
1392 g_return_if_fail(account
!= NULL
);
1394 status_editor_find_account_in_treemodel(&iter
, status_editor
, account
);
1395 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1396 STATUS_EDITOR_COLUMN_WINDOW
, &dialog
,
1400 gtk_window_present(GTK_WINDOW(dialog
->window
));
1404 dialog
= g_new0(SubStatusEditor
, 1);
1405 gtk_list_store_set(status_editor
->model
, &iter
,
1406 STATUS_EDITOR_COLUMN_WINDOW
, dialog
,
1408 dialog
->status_editor
= status_editor
;
1409 dialog
->account
= account
;
1411 tmp
= g_strdup_printf(_("Status for %s"), purple_account_get_username(account
));
1412 dialog
->window
= win
= pidgin_create_dialog(tmp
, PIDGIN_HIG_BORDER
, "substatus", TRUE
);
1415 g_signal_connect(G_OBJECT(win
), "destroy",
1416 G_CALLBACK(substatus_editor_destroy_cb
), dialog
);
1418 /* Setup the vbox */
1419 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win
), FALSE
, PIDGIN_HIG_BORDER
);
1421 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1424 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
1425 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1427 label
= gtk_label_new_with_mnemonic(_("_Status:"));
1428 gtk_label_set_xalign(GTK_LABEL(label
), 0);
1429 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1430 gtk_size_group_add_widget(sg
, label
);
1432 dialog
->model
= gtk_list_store_new(STATUS_NUM_COLUMNS
,
1436 combo
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog
->model
));
1437 dialog
->box
= GTK_COMBO_BOX(combo
);
1439 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new());
1440 g_object_set(G_OBJECT(rend
),
1441 "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
),
1443 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, FALSE
);
1444 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1445 "stock-id", STATUS_COLUMN_ICON
, NULL
);
1447 rend
= GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
1448 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), rend
, TRUE
);
1449 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
), rend
,
1450 "text", STATUS_COLUMN_STATUS_NAME
, NULL
);
1452 g_signal_connect(G_OBJECT(combo
), "changed",
1453 G_CALLBACK(substatus_selection_changed_cb
), dialog
);
1455 gtk_box_pack_start(GTK_BOX(hbox
), combo
, FALSE
, FALSE
, 0);
1458 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
1459 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
1461 label
= gtk_label_new_with_mnemonic(_("_Message:"));
1462 gtk_label_set_xalign(GTK_LABEL(label
), 0);
1463 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1464 gtk_size_group_add_widget(sg
, label
);
1466 editor
= talkatu_editor_new();
1467 gtk_box_pack_start(GTK_BOX(hbox
), editor
, TRUE
, TRUE
, 0);
1469 dialog
->message_view
= talkatu_editor_get_view(TALKATU_EDITOR(editor
));
1470 dialog
->message_buffer
= talkatu_html_buffer_new();
1471 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog
->message_view
), dialog
->message_buffer
);
1474 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_CANCEL
,
1475 G_CALLBACK(substatus_editor_cancel_cb
), dialog
);
1478 pidgin_dialog_add_button(GTK_DIALOG(win
), GTK_STOCK_OK
,
1479 G_CALLBACK(substatus_editor_ok_cb
), dialog
);
1481 /* Seed the input widgets with the current values */
1483 /* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
1484 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1485 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS
, &parent_dialog_has_substatus
, -1);
1486 if (parent_dialog_has_substatus
) {
1487 gtk_tree_model_get(GTK_TREE_MODEL(status_editor
->model
), &iter
,
1488 STATUS_EDITOR_COLUMN_STATUS_ID
, &status_id
,
1489 STATUS_EDITOR_COLUMN_STATUS_MESSAGE
, &message
, -1);
1490 } else if (status_editor
->original_title
!= NULL
) {
1491 PurpleSavedStatus
*saved_status
= NULL
;
1492 PurpleSavedStatusSub
*substatus
= NULL
;
1494 if ((saved_status
= purple_savedstatus_find(status_editor
->original_title
)) != NULL
) {
1495 if ((substatus
= purple_savedstatus_get_substatus(saved_status
, account
)) != NULL
) {
1496 message
= (char *)purple_savedstatus_substatus_get_message(substatus
);
1497 status_id
= (char *)purple_status_type_get_id(
1498 purple_savedstatus_substatus_get_status_type(substatus
));
1502 /* TODO: Else get the generic status type from our parent */
1505 talkatu_markup_set_html(TALKATU_BUFFER(dialog
->message_buffer
), message
, -1);
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_primitive_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_primitive_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();