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
23 * The status box is made up of two main pieces:
24 * - The box that displays the current status, which is made
25 * of a GtkListStore ("status_box->store") and GtkCellView
26 * ("status_box->cell_view"). There is always exactly 1 row
27 * in this list store. Only the TYPE_ICON and TYPE_TEXT
28 * columns are used in this list store.
29 * - The dropdown menu that lets users select a status, which
30 * is made of a GtkComboBox ("status_box") and GtkListStore
31 * ("status_box->dropdown_store"). This dropdown is shown
32 * when the user clicks on the box that displays the current
33 * status. This list store contains one row for Available,
34 * one row for Away, etc., a few rows for popular statuses,
35 * and the "New..." and "Saved..." options.
38 #include <gdk/gdkkeysyms.h>
43 #include "buddyicon.h"
47 #include "savedstatuses.h"
52 #include "gtksavedstatuses.h"
53 #include "pidginstock.h"
54 #include "gtkstatusbox.h"
56 #include "pidgingdkpixbuf.h"
58 #include "gtk3compat.h"
60 /* Timeout for typing notifications in seconds */
61 #define TYPING_TIMEOUT 4
63 static void remove_typing_cb(PidginStatusBox
*box
);
64 static void update_size (PidginStatusBox
*box
);
65 static gint
get_statusbox_index(PidginStatusBox
*box
, PurpleSavedStatus
*saved_status
);
66 static PurpleAccount
* check_active_accounts_for_identical_statuses(void);
68 static void pidgin_status_box_pulse_typing(PidginStatusBox
*status_box
);
69 static void pidgin_status_box_refresh(PidginStatusBox
*status_box
);
70 static void status_menu_refresh_iter(PidginStatusBox
*status_box
, gboolean status_changed
);
71 static void pidgin_status_box_regenerate(PidginStatusBox
*status_box
, gboolean status_changed
);
72 static void pidgin_status_box_changed(PidginStatusBox
*box
);
73 static void pidgin_status_box_get_preferred_height (GtkWidget
*widget
,
74 gint
*minimum_height
, gint
*natural_height
);
75 static gboolean
pidgin_status_box_draw (GtkWidget
*widget
, cairo_t
*cr
);
76 static void pidgin_status_box_size_allocate (GtkWidget
*widget
, GtkAllocation
*allocation
);
77 static void pidgin_status_box_redisplay_buddy_icon(PidginStatusBox
*status_box
);
78 static void pidgin_status_box_forall (GtkContainer
*container
, gboolean include_internals
, GtkCallback callback
, gpointer callback_data
);
79 static void pidgin_status_box_popup(PidginStatusBox
*box
, GdkEvent
*event
);
80 static void pidgin_status_box_popdown(PidginStatusBox
*box
, GdkEvent
*event
);
82 static void do_colorshift (GdkPixbuf
*dest
, GdkPixbuf
*src
, int shift
);
83 static void icon_choose_cb(const char *filename
, gpointer data
);
84 static void remove_buddy_icon_cb(GtkWidget
*w
, PidginStatusBox
*box
);
85 static void choose_buddy_icon_cb(GtkWidget
*w
, PidginStatusBox
*box
);
88 /* A PidginStatusBoxItemType */
91 /* This is the stock-id for the icon. */
95 * This is a GdkPixbuf (the other columns are strings).
96 * This column is visible.
100 /* The text displayed on the status box. This column is visible. */
103 /* The plain-English title of this item */
106 /* A plain-English description of this item */
110 * This value depends on TYPE_COLUMN. For POPULAR types,
111 * this is the creation time. For PRIMITIVE types,
112 * this is the PurpleStatusPrimitive.
117 * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored.
118 * In the GtkTreeModel for the dropdown, this is the stock-id (gchararray), and for the
119 * GtkTreeModel for the cell_view (for the account-specific statusbox), this is the protocol icon
120 * (GdkPixbuf) of the account.
125 * This column stores whether to show the emblem.
127 EMBLEM_VISIBLE_COLUMN
,
138 static char *typing_stock_ids
[7] = {
139 PIDGIN_STOCK_ANIMATION_TYPING0
,
140 PIDGIN_STOCK_ANIMATION_TYPING1
,
141 PIDGIN_STOCK_ANIMATION_TYPING2
,
142 PIDGIN_STOCK_ANIMATION_TYPING3
,
143 PIDGIN_STOCK_ANIMATION_TYPING4
,
147 static char *connecting_stock_ids
[] = {
148 PIDGIN_STOCK_ANIMATION_CONNECT0
,
149 PIDGIN_STOCK_ANIMATION_CONNECT1
,
150 PIDGIN_STOCK_ANIMATION_CONNECT2
,
151 PIDGIN_STOCK_ANIMATION_CONNECT3
,
152 PIDGIN_STOCK_ANIMATION_CONNECT4
,
153 PIDGIN_STOCK_ANIMATION_CONNECT5
,
154 PIDGIN_STOCK_ANIMATION_CONNECT6
,
155 PIDGIN_STOCK_ANIMATION_CONNECT7
,
156 PIDGIN_STOCK_ANIMATION_CONNECT8
,
157 PIDGIN_STOCK_ANIMATION_CONNECT9
,
158 PIDGIN_STOCK_ANIMATION_CONNECT10
,
159 PIDGIN_STOCK_ANIMATION_CONNECT11
,
160 PIDGIN_STOCK_ANIMATION_CONNECT12
,
161 PIDGIN_STOCK_ANIMATION_CONNECT13
,
162 PIDGIN_STOCK_ANIMATION_CONNECT14
,
163 PIDGIN_STOCK_ANIMATION_CONNECT15
,
164 PIDGIN_STOCK_ANIMATION_CONNECT16
,
165 PIDGIN_STOCK_ANIMATION_CONNECT17
,
166 PIDGIN_STOCK_ANIMATION_CONNECT18
,
167 PIDGIN_STOCK_ANIMATION_CONNECT19
,
168 PIDGIN_STOCK_ANIMATION_CONNECT20
,
169 PIDGIN_STOCK_ANIMATION_CONNECT21
,
170 PIDGIN_STOCK_ANIMATION_CONNECT22
,
171 PIDGIN_STOCK_ANIMATION_CONNECT23
,
172 PIDGIN_STOCK_ANIMATION_CONNECT24
,
173 PIDGIN_STOCK_ANIMATION_CONNECT25
,
174 PIDGIN_STOCK_ANIMATION_CONNECT26
,
175 PIDGIN_STOCK_ANIMATION_CONNECT27
,
176 PIDGIN_STOCK_ANIMATION_CONNECT28
,
177 PIDGIN_STOCK_ANIMATION_CONNECT29
,
178 PIDGIN_STOCK_ANIMATION_CONNECT30
,
182 static GtkContainerClass
*parent_class
= NULL
;
184 static void pidgin_status_box_class_init (PidginStatusBoxClass
*klass
);
185 static void pidgin_status_box_init (PidginStatusBox
*status_box
);
188 pidgin_status_box_get_type (void)
190 static GType status_box_type
= 0;
192 if (!status_box_type
)
194 static const GTypeInfo status_box_info
=
196 sizeof (PidginStatusBoxClass
),
197 NULL
, /* base_init */
198 NULL
, /* base_finalize */
199 (GClassInitFunc
) pidgin_status_box_class_init
,
200 NULL
, /* class_finalize */
201 NULL
, /* class_data */
202 sizeof (PidginStatusBox
),
204 (GInstanceInitFunc
) pidgin_status_box_init
,
205 NULL
/* value_table */
208 status_box_type
= g_type_register_static(GTK_TYPE_CONTAINER
,
214 return status_box_type
;
218 pidgin_status_box_get_property(GObject
*object
, guint param_id
,
219 GValue
*value
, GParamSpec
*psec
)
221 PidginStatusBox
*statusbox
= PIDGIN_STATUS_BOX(object
);
225 g_value_set_pointer(value
, statusbox
->account
);
228 g_value_set_boolean(value
, statusbox
->icon_box
!= NULL
);
231 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, param_id
, psec
);
237 update_to_reflect_account_status(PidginStatusBox
*status_box
, PurpleAccount
*account
, PurpleStatus
*newstatus
)
241 const PurpleStatusType
*statustype
= NULL
;
244 statustype
= purple_status_type_find_with_id((GList
*)purple_account_get_status_types(account
),
245 (char *)purple_status_type_get_id(purple_status_get_status_type(newstatus
)));
247 for (l
= purple_account_get_status_types(account
); l
!= NULL
; l
= l
->next
) {
248 PurpleStatusType
*status_type
= (PurpleStatusType
*)l
->data
;
250 if (!purple_status_type_is_user_settable(status_type
) ||
251 purple_status_type_is_independent(status_type
))
254 if (statustype
== status_type
)
258 if (status_no
!= -1) {
260 gtk_widget_set_sensitive(GTK_WIDGET(status_box
), FALSE
);
261 path
= gtk_tree_path_new_from_indices(status_no
, -1);
262 if (status_box
->active_row
)
263 gtk_tree_row_reference_free(status_box
->active_row
);
264 status_box
->active_row
= gtk_tree_row_reference_new(GTK_TREE_MODEL(status_box
->dropdown_store
), path
);
265 gtk_tree_path_free(path
);
267 message
= purple_status_get_attr_string(newstatus
, "message");
269 if (!message
|| !*message
)
271 gtk_widget_hide(status_box
->vbox
);
272 status_box
->editor_visible
= FALSE
;
276 gtk_widget_show_all(status_box
->vbox
);
277 status_box
->editor_visible
= TRUE
;
278 talkatu_markup_set_html(TALKATU_BUFFER(status_box
->buffer
), message
, -1);
280 gtk_widget_set_sensitive(GTK_WIDGET(status_box
), TRUE
);
281 pidgin_status_box_refresh(status_box
);
286 account_status_changed_cb(PurpleAccount
*account
, PurpleStatus
*oldstatus
, PurpleStatus
*newstatus
, PidginStatusBox
*status_box
)
288 if (status_box
->account
== account
)
289 update_to_reflect_account_status(status_box
, account
, newstatus
);
290 else if (status_box
->token_status_account
== account
)
291 status_menu_refresh_iter(status_box
, TRUE
);
295 icon_box_press_cb(GtkWidget
*widget
, GdkEventButton
*event
, PidginStatusBox
*box
)
297 if (gdk_event_triggers_context_menu((GdkEvent
*)event
)) {
298 GtkWidget
*menu_item
;
301 if (box
->icon_box_menu
)
302 gtk_widget_destroy(box
->icon_box_menu
);
304 box
->icon_box_menu
= gtk_menu_new();
306 pidgin_new_menu_item(box
->icon_box_menu
,
307 _("Select Buddy Icon"), GTK_STOCK_ADD
,
308 G_CALLBACK(choose_buddy_icon_cb
), box
);
310 menu_item
= pidgin_new_menu_item(box
->icon_box_menu
, _("Remove"), GTK_STOCK_REMOVE
,
311 G_CALLBACK(remove_buddy_icon_cb
), box
);
312 if (!(path
= purple_prefs_get_path(PIDGIN_PREFS_ROOT
"/accounts/buddyicon"))
314 gtk_widget_set_sensitive(menu_item
, FALSE
);
316 gtk_menu_popup_at_pointer(GTK_MENU(box
->icon_box_menu
), (GdkEvent
*)event
);
319 choose_buddy_icon_cb(widget
, box
);
325 icon_box_dnd_cb(GtkWidget
*widget
, GdkDragContext
*dc
, gint x
, gint y
,
326 GtkSelectionData
*sd
, guint info
, guint t
, PidginStatusBox
*box
)
328 gchar
*name
= (gchar
*) gtk_selection_data_get_data(sd
);
330 if ((gtk_selection_data_get_length(sd
) >= 0)
331 && (gtk_selection_data_get_format(sd
) == 8)) {
332 /* Well, it looks like the drag event was cool.
333 * Let's do something with it */
334 if (!g_ascii_strncasecmp(name
, "file://", 7)) {
335 GError
*converr
= NULL
;
338 if(!(tmp
= g_filename_from_uri(name
, NULL
, &converr
))) {
339 purple_debug(PURPLE_DEBUG_ERROR
, "buddyicon", "%s\n",
340 (converr
? converr
->message
:
341 "g_filename_from_uri error"));
344 if ((rtmp
= strchr(tmp
, '\r')) || (rtmp
= strchr(tmp
, '\n')))
346 icon_choose_cb(tmp
, box
);
349 gtk_drag_finish(dc
, TRUE
, FALSE
, t
);
351 gtk_drag_finish(dc
, FALSE
, FALSE
, t
);
355 icon_box_enter_cb(GtkWidget
*widget
, GdkEventCrossing
*event
, PidginStatusBox
*box
)
357 gdk_window_set_cursor(gtk_widget_get_window(widget
), box
->hand_cursor
);
358 gtk_image_set_from_pixbuf(GTK_IMAGE(box
->icon
), box
->buddy_icon_hover
);
363 icon_box_leave_cb(GtkWidget
*widget
, GdkEventCrossing
*event
, PidginStatusBox
*box
)
365 gdk_window_set_cursor(gtk_widget_get_window(widget
), box
->arrow_cursor
);
366 gtk_image_set_from_pixbuf(GTK_IMAGE(box
->icon
), box
->buddy_icon
) ;
371 static const GtkTargetEntry dnd_targets
[] = {
372 {"text/plain", 0, 0},
373 {"text/uri-list", 0, 1},
378 setup_icon_box(PidginStatusBox
*status_box
)
382 if (status_box
->icon_box
!= NULL
)
385 status_box
->icon
= gtk_image_new();
386 status_box
->icon_box
= gtk_event_box_new();
387 gtk_widget_set_parent(status_box
->icon_box
, GTK_WIDGET(status_box
));
388 gtk_widget_show(status_box
->icon_box
);
390 gtk_widget_set_tooltip_text(status_box
->icon_box
,
391 status_box
->account
? _("Click to change your buddyicon for this account.") :
392 _("Click to change your buddyicon for all accounts."));
394 if (status_box
->account
&&
395 !purple_account_get_bool(status_box
->account
, "use-global-buddyicon", TRUE
))
397 PurpleImage
*img
= purple_buddy_icons_find_account_icon(status_box
->account
);
398 pidgin_status_box_set_buddy_icon(status_box
, img
);
403 const char *filename
= purple_prefs_get_path(PIDGIN_PREFS_ROOT
"/accounts/buddyicon");
404 PurpleImage
*img
= NULL
;
406 if (filename
&& *filename
)
407 img
= purple_image_new_from_file(filename
, NULL
);
409 pidgin_status_box_set_buddy_icon(status_box
, img
);
414 display
= gtk_widget_get_display(status_box
->icon_box
);
415 status_box
->hand_cursor
= gdk_cursor_new_for_display(display
, GDK_HAND2
);
416 status_box
->arrow_cursor
= gdk_cursor_new_for_display(display
, GDK_LEFT_PTR
);
419 gtk_drag_dest_set(status_box
->icon_box
,
420 GTK_DEST_DEFAULT_MOTION
|
421 GTK_DEST_DEFAULT_DROP
,
423 sizeof(dnd_targets
) / sizeof(GtkTargetEntry
),
426 g_signal_connect(G_OBJECT(status_box
->icon_box
), "drag_data_received", G_CALLBACK(icon_box_dnd_cb
), status_box
);
427 g_signal_connect(G_OBJECT(status_box
->icon_box
), "enter-notify-event", G_CALLBACK(icon_box_enter_cb
), status_box
);
428 g_signal_connect(G_OBJECT(status_box
->icon_box
), "leave-notify-event", G_CALLBACK(icon_box_leave_cb
), status_box
);
429 g_signal_connect(G_OBJECT(status_box
->icon_box
), "button-press-event", G_CALLBACK(icon_box_press_cb
), status_box
);
431 gtk_container_add(GTK_CONTAINER(status_box
->icon_box
), status_box
->icon
);
432 gtk_widget_show(status_box
->icon
);
436 destroy_icon_box(PidginStatusBox
*statusbox
)
438 g_clear_pointer(&statusbox
->icon_box
, gtk_widget_destroy
);
440 g_clear_object(&statusbox
->hand_cursor
);
441 g_clear_object(&statusbox
->arrow_cursor
);
443 g_clear_object(&statusbox
->buddy_icon_img
);
445 g_clear_object(&statusbox
->buddy_icon
);
446 g_clear_object(&statusbox
->buddy_icon_hover
);
448 g_clear_pointer(&statusbox
->buddy_icon_sel
, gtk_widget_destroy
);
450 g_clear_pointer(&statusbox
->icon_box_menu
, gtk_widget_destroy
);
452 statusbox
->icon
= NULL
;
456 pidgin_status_box_set_property(GObject
*object
, guint param_id
,
457 const GValue
*value
, GParamSpec
*pspec
)
459 PidginStatusBox
*statusbox
= PIDGIN_STATUS_BOX(object
);
463 if (g_value_get_boolean(value
)) {
464 if (statusbox
->account
) {
465 PurpleBuddyIconSpec
*icon_spec
= NULL
;
466 PurpleProtocol
*protocol
=
467 purple_protocols_find(purple_account_get_protocol_id(statusbox
->account
));
469 icon_spec
= purple_protocol_get_icon_spec(protocol
);
470 if (icon_spec
&& icon_spec
->format
!= NULL
)
471 setup_icon_box(statusbox
);
473 setup_icon_box(statusbox
);
476 destroy_icon_box(statusbox
);
480 statusbox
->account
= g_value_get_pointer(value
);
481 if (statusbox
->account
)
482 statusbox
->token_status_account
= NULL
;
484 statusbox
->token_status_account
= check_active_accounts_for_identical_statuses();
486 pidgin_status_box_regenerate(statusbox
, TRUE
);
490 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, param_id
, pspec
);
496 pidgin_status_box_dispose(GObject
*obj
)
498 PidginStatusBox
*statusbox
= PIDGIN_STATUS_BOX(obj
);
500 destroy_icon_box(statusbox
);
504 pidgin_status_box_finalize(GObject
*obj
)
506 PidginStatusBox
*statusbox
= PIDGIN_STATUS_BOX(obj
);
509 purple_signals_disconnect_by_handle(statusbox
);
510 purple_prefs_disconnect_by_handle(statusbox
);
512 if (statusbox
->active_row
)
513 gtk_tree_row_reference_free(statusbox
->active_row
);
515 for (i
= 0; i
< G_N_ELEMENTS(statusbox
->connecting_pixbufs
); i
++) {
516 if (statusbox
->connecting_pixbufs
[i
] != NULL
)
517 g_object_unref(G_OBJECT(statusbox
->connecting_pixbufs
[i
]));
520 for (i
= 0; i
< G_N_ELEMENTS(statusbox
->typing_pixbufs
); i
++) {
521 if (statusbox
->typing_pixbufs
[i
] != NULL
)
522 g_object_unref(G_OBJECT(statusbox
->typing_pixbufs
[i
]));
525 g_object_unref(G_OBJECT(statusbox
->store
));
526 g_object_unref(G_OBJECT(statusbox
->dropdown_store
));
527 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
531 pidgin_status_box_child_type (GtkContainer
*container
)
533 return GTK_TYPE_WIDGET
;
537 pidgin_status_box_class_init (PidginStatusBoxClass
*klass
)
539 GObjectClass
*object_class
;
540 GtkWidgetClass
*widget_class
;
541 GtkContainerClass
*container_class
= (GtkContainerClass
*)klass
;
543 parent_class
= g_type_class_peek_parent(klass
);
545 widget_class
= (GtkWidgetClass
*)klass
;
546 widget_class
->get_preferred_height
= pidgin_status_box_get_preferred_height
;
547 widget_class
->draw
= pidgin_status_box_draw
;
548 widget_class
->size_allocate
= pidgin_status_box_size_allocate
;
550 container_class
->child_type
= pidgin_status_box_child_type
;
551 container_class
->forall
= pidgin_status_box_forall
;
552 container_class
->remove
= NULL
;
554 object_class
= (GObjectClass
*)klass
;
556 object_class
->dispose
= pidgin_status_box_dispose
;
557 object_class
->finalize
= pidgin_status_box_finalize
;
559 object_class
->get_property
= pidgin_status_box_get_property
;
560 object_class
->set_property
= pidgin_status_box_set_property
;
562 g_object_class_install_property(object_class
,
564 g_param_spec_pointer("account",
566 "The account, or NULL for all accounts",
567 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
570 g_object_class_install_property(object_class
,
572 g_param_spec_boolean("iconsel",
574 "Whether the icon selector should be displayed or not.",
576 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
582 * This updates the text displayed on the status box so that it shows
583 * the current status. This is the only function in this file that
584 * should modify status_box->store
587 pidgin_status_box_refresh(PidginStatusBox
*status_box
)
589 const char *aa_color
;
590 PurpleSavedStatus
*saved_status
;
591 char *primary
, *secondary
, *text
;
592 const char *stock
= NULL
;
593 GdkPixbuf
*emblem
= NULL
;
595 gboolean account_status
= FALSE
;
596 PurpleAccount
*acct
= (status_box
->account
) ? status_box
->account
: status_box
->token_status_account
;
598 saved_status
= purple_savedstatus_get_current();
600 if (status_box
->account
|| (status_box
->token_status_account
601 && purple_savedstatus_is_transient(saved_status
)))
602 account_status
= TRUE
;
605 if (status_box
->typing
!= 0)
608 PidginStatusBoxItemType type
;
611 /* Primary (get the status selected in the dropdown) */
612 path
= gtk_tree_row_reference_get_path(status_box
->active_row
);
613 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
, path
))
615 gtk_tree_path_free(path
);
617 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
621 if (type
== PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
)
622 primary
= g_strdup(purple_primitive_get_name_from_type(GPOINTER_TO_INT(data
)));
624 /* This should never happen, but just in case... */
625 primary
= g_strdup("New status");
627 else if (account_status
)
628 primary
= g_strdup(purple_status_get_name(purple_account_get_active_status(acct
)));
629 else if (purple_savedstatus_is_transient(saved_status
))
630 primary
= g_strdup(purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status
)));
632 primary
= g_markup_escape_text(purple_savedstatus_get_title(saved_status
), -1);
635 if (status_box
->typing
!= 0)
636 secondary
= g_strdup(_("Typing"));
637 else if (status_box
->connecting
)
638 secondary
= g_strdup(_("Connecting"));
639 else if (!status_box
->network_available
)
640 secondary
= g_strdup(_("Waiting for network connection"));
641 else if (purple_savedstatus_is_transient(saved_status
))
647 message
= purple_savedstatus_get_message(saved_status
);
650 tmp
= purple_markup_strip_html(message
);
651 purple_util_chrreplace(tmp
, '\n', ' ');
652 secondary
= g_markup_escape_text(tmp
, -1);
660 if (status_box
->typing
!= 0)
661 stock
= typing_stock_ids
[status_box
->typing_index
];
662 else if (status_box
->connecting
)
663 stock
= connecting_stock_ids
[status_box
->connecting_index
];
666 PurpleStatusType
*status_type
;
667 PurpleStatusPrimitive prim
;
668 if (account_status
) {
669 status_type
= purple_status_get_status_type(purple_account_get_active_status(acct
));
670 prim
= purple_status_type_get_primitive(status_type
);
672 prim
= purple_savedstatus_get_primitive_type(saved_status
);
675 stock
= pidgin_stock_id_from_status_primitive(prim
);
678 aa_color
= pidgin_get_dim_grey_string(GTK_WIDGET(status_box
));
679 if (status_box
->account
!= NULL
) {
680 text
= g_strdup_printf("%s - <span size=\"smaller\" color=\"%s\">%s</span>",
681 purple_account_get_username(status_box
->account
),
682 aa_color
, secondary
? secondary
: primary
);
683 emblem
= pidgin_create_protocol_icon(status_box
->account
, PIDGIN_PROTOCOL_ICON_SMALL
);
684 } else if (secondary
!= NULL
) {
685 text
= g_strdup_printf("%s<span size=\"smaller\" color=\"%s\"> - %s</span>",
686 primary
, aa_color
, secondary
);
688 text
= g_strdup(primary
);
694 * Only two columns are used in this list store (does it
695 * really need to be a list store?)
697 gtk_list_store_set(status_box
->store
, &(status_box
->iter
),
698 ICON_STOCK_COLUMN
, (gpointer
)stock
,
700 EMBLEM_COLUMN
, emblem
,
701 EMBLEM_VISIBLE_COLUMN
, (emblem
!= NULL
),
705 g_object_unref(emblem
);
707 /* Make sure to activate the only row in the tree view */
708 path
= gtk_tree_path_new_from_string("0");
709 gtk_cell_view_set_displayed_row(GTK_CELL_VIEW(status_box
->cell_view
), path
);
710 gtk_tree_path_free(path
);
712 update_size(status_box
);
715 static PurpleStatusType
*
716 find_status_type_by_index(PurpleAccount
*account
, gint active
)
718 GList
*l
= purple_account_get_status_types(account
);
721 for (i
= 0; l
; l
= l
->next
) {
722 PurpleStatusType
*status_type
= l
->data
;
723 if (!purple_status_type_is_user_settable(status_type
) ||
724 purple_status_type_is_independent(status_type
))
736 * This updates the GtkTreeView so that it correctly shows the state
737 * we are currently using. It is used when the current state is
738 * updated from somewhere other than the GtkStatusBox (from a plugin,
739 * or when signing on with the "-n" option, for example). It is
740 * also used when the user selects the "New..." option.
742 * Maybe we could accomplish this by triggering off the mouse and
743 * keyboard signals instead of the changed signal?
746 status_menu_refresh_iter(PidginStatusBox
*status_box
, gboolean status_changed
)
748 PurpleSavedStatus
*saved_status
;
749 PurpleStatusPrimitive primitive
;
752 GtkTreePath
*path
= NULL
;
754 /* this function is inappropriate for ones with accounts */
755 if (status_box
->account
)
758 saved_status
= purple_savedstatus_get_current();
761 * Suppress the "changed" signal because the status
762 * was changed programmatically.
764 gtk_widget_set_sensitive(GTK_WIDGET(status_box
), FALSE
);
767 * If there is a token-account, then select the primitive from the
768 * dropdown using a loop. Otherwise select from the default list.
770 primitive
= purple_savedstatus_get_primitive_type(saved_status
);
771 if (!status_box
->token_status_account
&& purple_savedstatus_is_transient(saved_status
) &&
772 ((primitive
== PURPLE_STATUS_AVAILABLE
) || (primitive
== PURPLE_STATUS_AWAY
) ||
773 (primitive
== PURPLE_STATUS_INVISIBLE
) || (primitive
== PURPLE_STATUS_OFFLINE
) ||
774 (primitive
== PURPLE_STATUS_UNAVAILABLE
)) &&
775 (!purple_savedstatus_has_substatuses(saved_status
)))
777 index
= get_statusbox_index(status_box
, saved_status
);
778 path
= gtk_tree_path_new_from_indices(index
, -1);
783 PidginStatusBoxItemType type
;
786 /* If this saved status is in the list store, then set it as the active item */
787 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
))
791 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
796 /* This is a special case because Primitives for the token_status_account are actually
797 * saved statuses with substatuses for the enabled accounts */
798 if (status_box
->token_status_account
&& purple_savedstatus_is_transient(saved_status
)
799 && type
== PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
&& primitive
== (PurpleStatusPrimitive
)GPOINTER_TO_INT(data
))
802 const char *acct_status_name
= purple_status_get_name(
803 purple_account_get_active_status(status_box
->token_status_account
));
805 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
806 TEXT_COLUMN
, &name
, -1);
808 if (!purple_savedstatus_has_substatuses(saved_status
)
809 || purple_strequal(name
, acct_status_name
))
812 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
);
818 } else if ((type
== PIDGIN_STATUS_BOX_TYPE_POPULAR
) &&
819 (GPOINTER_TO_INT(data
) == purple_savedstatus_get_creation_time(saved_status
)))
822 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
);
825 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
));
829 if (status_box
->active_row
)
830 gtk_tree_row_reference_free(status_box
->active_row
);
831 if (path
) { /* path should never be NULL */
832 status_box
->active_row
= gtk_tree_row_reference_new(GTK_TREE_MODEL(status_box
->dropdown_store
), path
);
833 gtk_tree_path_free(path
);
835 status_box
->active_row
= NULL
;
837 if (status_changed
) {
838 message
= purple_savedstatus_get_message(saved_status
);
841 * If we are going to hide the editor, don't retain the
842 * message because showing the old message later is
843 * confusing. If we are going to set the message to a pre-set,
844 * then we need to do this anyway
846 * Suppress the "changed" signal because the status
847 * was changed programmatically.
849 gtk_widget_set_sensitive(GTK_WIDGET(status_box
->view
), FALSE
);
851 talkatu_buffer_clear(TALKATU_BUFFER(status_box
->buffer
));
853 if (!purple_savedstatus_is_transient(saved_status
) || !message
|| !*message
)
855 status_box
->editor_visible
= FALSE
;
856 gtk_widget_hide(status_box
->vbox
);
860 status_box
->editor_visible
= TRUE
;
861 gtk_widget_show_all(status_box
->vbox
);
863 talkatu_markup_set_html(TALKATU_BUFFER(status_box
->buffer
), message
, -1);
866 gtk_widget_set_sensitive(GTK_WIDGET(status_box
->view
), TRUE
);
867 update_size(status_box
);
870 /* Stop suppressing the "changed" signal. */
871 gtk_widget_set_sensitive(GTK_WIDGET(status_box
), TRUE
);
875 add_popular_statuses(PidginStatusBox
*statusbox
)
879 list
= purple_savedstatuses_get_popular(6);
881 /* Odd... oh well, nothing we can do about it. */
884 pidgin_status_box_add_separator(statusbox
);
886 for (cur
= list
; cur
!= NULL
; cur
= cur
->next
)
888 PurpleSavedStatus
*saved
= cur
->data
;
889 const gchar
*message
;
890 gchar
*stripped
= NULL
;
891 PidginStatusBoxItemType type
;
893 if (purple_savedstatus_is_transient(saved
))
896 * Transient statuses do not have a title, so the savedstatus
897 * API returns the message when purple_savedstatus_get_title() is
898 * called, so we don't need to get the message a second time.
900 type
= PIDGIN_STATUS_BOX_TYPE_POPULAR
;
904 type
= PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR
;
906 message
= purple_savedstatus_get_message(saved
);
909 stripped
= purple_markup_strip_html(message
);
910 purple_util_chrreplace(stripped
, '\n', ' ');
914 pidgin_status_box_add(statusbox
, type
,
915 NULL
, purple_savedstatus_get_title(saved
), stripped
,
916 GINT_TO_POINTER(purple_savedstatus_get_creation_time(saved
)));
923 /* This returns NULL if the active accounts don't have identical
924 * statuses and a token account if they do */
925 static PurpleAccount
* check_active_accounts_for_identical_statuses(void)
927 GList
*iter
, *active_accts
= purple_accounts_get_all_active();
928 PurpleAccount
*acct1
= NULL
;
929 const char *proto1
= NULL
;
932 acct1
= active_accts
->data
;
933 proto1
= purple_account_get_protocol_id(acct1
);
935 /* there's no enabled account */
939 /* start at the second account */
940 for (iter
= active_accts
->next
; iter
; iter
= iter
->next
) {
941 PurpleAccount
*acct2
= iter
->data
;
944 if (!purple_strequal(proto1
, purple_account_get_protocol_id(acct2
))) {
949 for (s1
= purple_account_get_status_types(acct1
),
950 s2
= purple_account_get_status_types(acct2
); s1
&& s2
;
951 s1
= s1
->next
, s2
= s2
->next
) {
952 PurpleStatusType
*st1
= s1
->data
, *st2
= s2
->data
;
953 /* TODO: Are these enough to consider the statuses identical? */
954 if (purple_status_type_get_primitive(st1
) != purple_status_type_get_primitive(st2
)
955 || !purple_strequal(purple_status_type_get_id(st1
), purple_status_type_get_id(st2
))
956 || !purple_strequal(purple_status_type_get_name(st1
), purple_status_type_get_name(st2
))) {
962 if (s1
!= s2
) {/* Will both be NULL if matched */
968 g_list_free(active_accts
);
974 add_account_statuses(PidginStatusBox
*status_box
, PurpleAccount
*account
)
979 for (l
= purple_account_get_status_types(account
); l
!= NULL
; l
= l
->next
)
981 PurpleStatusType
*status_type
= (PurpleStatusType
*)l
->data
;
982 PurpleStatusPrimitive prim
;
984 if (!purple_status_type_is_user_settable(status_type
) ||
985 purple_status_type_is_independent(status_type
))
988 prim
= purple_status_type_get_primitive(status_type
);
990 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
),
991 PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
,
992 purple_status_type_get_name(status_type
),
994 GINT_TO_POINTER(prim
));
999 pidgin_status_box_regenerate(PidginStatusBox
*status_box
, gboolean status_changed
)
1001 /* Unset the model while clearing it */
1002 gtk_tree_view_set_model(GTK_TREE_VIEW(status_box
->tree_view
), NULL
);
1003 gtk_list_store_clear(status_box
->dropdown_store
);
1004 /* Don't set the model until the new statuses have been added to the box.
1005 * What is presumably a bug in Gtk < 2.4 causes things to get all confused
1006 * if we do this here. */
1007 /* gtk_combo_box_set_model(GTK_COMBO_BOX(status_box), GTK_TREE_MODEL(status_box->dropdown_store)); */
1009 if (status_box
->account
== NULL
)
1011 /* Do all the currently enabled accounts have the same statuses?
1012 * If so, display them instead of our global list.
1014 if (status_box
->token_status_account
) {
1015 add_account_statuses(status_box
, status_box
->token_status_account
);
1018 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
, _("Available"), NULL
, GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE
));
1019 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
, _("Away"), NULL
, GINT_TO_POINTER(PURPLE_STATUS_AWAY
));
1020 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
, _("Do not disturb"), NULL
, GINT_TO_POINTER(PURPLE_STATUS_UNAVAILABLE
));
1021 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
, _("Invisible"), NULL
, GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE
));
1022 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
, NULL
, _("Offline"), NULL
, GINT_TO_POINTER(PURPLE_STATUS_OFFLINE
));
1025 add_popular_statuses(status_box
);
1027 pidgin_status_box_add_separator(PIDGIN_STATUS_BOX(status_box
));
1028 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_CUSTOM
, NULL
, _("New status..."), NULL
, NULL
);
1029 pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box
), PIDGIN_STATUS_BOX_TYPE_SAVED
, NULL
, _("Saved statuses..."), NULL
, NULL
);
1031 status_menu_refresh_iter(status_box
, status_changed
);
1032 pidgin_status_box_refresh(status_box
);
1035 add_account_statuses(status_box
, status_box
->account
);
1036 update_to_reflect_account_status(status_box
, status_box
->account
,
1037 purple_account_get_active_status(status_box
->account
));
1039 gtk_tree_view_set_model(GTK_TREE_VIEW(status_box
->tree_view
), GTK_TREE_MODEL(status_box
->dropdown_store
));
1040 gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box
->tree_view
), TEXT_COLUMN
);
1044 combo_box_scroll_event_cb(GtkWidget
*w
, GdkEventScroll
*event
, G_GNUC_UNUSED gpointer data
)
1046 pidgin_status_box_popup(PIDGIN_STATUS_BOX(w
), (GdkEvent
*)event
);
1051 editor_remove_focus(GtkWidget
*w
, GdkEventKey
*event
, PidginStatusBox
*status_box
)
1053 if (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_KP_Enter
) {
1054 remove_typing_cb(status_box
);
1057 else if (event
->keyval
== GDK_KEY_Tab
|| event
->keyval
== GDK_KEY_KP_Tab
|| event
->keyval
== GDK_KEY_ISO_Left_Tab
)
1059 /* If last inserted character is a tab, then remove the focus from here */
1060 GtkWidget
*top
= gtk_widget_get_toplevel(w
);
1061 g_signal_emit_by_name(G_OBJECT(top
), "move-focus",
1062 (event
->state
& GDK_SHIFT_MASK
) ?
1063 GTK_DIR_TAB_BACKWARD
: GTK_DIR_TAB_FORWARD
);
1066 if (status_box
->typing
== 0)
1069 /* Reset the status if Escape was pressed */
1070 if (event
->keyval
== GDK_KEY_Escape
)
1072 g_source_remove(status_box
->typing
);
1073 status_box
->typing
= 0;
1075 if (status_box
->account
!= NULL
)
1076 update_to_reflect_account_status(status_box
, status_box
->account
,
1077 purple_account_get_active_status(status_box
->account
));
1079 status_menu_refresh_iter(status_box
, TRUE
);
1080 pidgin_status_box_refresh(status_box
);
1085 pidgin_status_box_pulse_typing(status_box
);
1086 g_source_remove(status_box
->typing
);
1087 status_box
->typing
= g_timeout_add_seconds(TYPING_TIMEOUT
, (GSourceFunc
)remove_typing_cb
, status_box
);
1093 dropdown_store_row_separator_func(GtkTreeModel
*model
,
1094 GtkTreeIter
*iter
, gpointer data
)
1096 PidginStatusBoxItemType type
;
1098 gtk_tree_model_get(model
, iter
, TYPE_COLUMN
, &type
, -1);
1100 if (type
== PIDGIN_STATUS_BOX_TYPE_SEPARATOR
)
1107 cache_pixbufs(PidginStatusBox
*status_box
)
1109 GtkIconSize icon_size
;
1112 g_object_set(G_OBJECT(status_box
->icon_rend
), "xpad", 3, NULL
);
1113 icon_size
= gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
);
1115 for (i
= 0; i
< G_N_ELEMENTS(status_box
->connecting_pixbufs
); i
++) {
1116 if (status_box
->connecting_pixbufs
[i
] != NULL
)
1117 g_object_unref(G_OBJECT(status_box
->connecting_pixbufs
[i
]));
1118 if (connecting_stock_ids
[i
])
1119 status_box
->connecting_pixbufs
[i
] = gtk_widget_render_icon (GTK_WIDGET(status_box
->vbox
),
1120 connecting_stock_ids
[i
], icon_size
, "PidginStatusBox");
1122 status_box
->connecting_pixbufs
[i
] = NULL
;
1124 status_box
->connecting_index
= 0;
1127 for (i
= 0; i
< G_N_ELEMENTS(status_box
->typing_pixbufs
); i
++) {
1128 if (status_box
->typing_pixbufs
[i
] != NULL
)
1129 g_object_unref(G_OBJECT(status_box
->typing_pixbufs
[i
]));
1130 if (typing_stock_ids
[i
])
1131 status_box
->typing_pixbufs
[i
] = gtk_widget_render_icon (GTK_WIDGET(status_box
->vbox
),
1132 typing_stock_ids
[i
], icon_size
, "PidginStatusBox");
1134 status_box
->typing_pixbufs
[i
] = NULL
;
1136 status_box
->typing_index
= 0;
1139 static void account_enabled_cb(PurpleAccount
*acct
, PidginStatusBox
*status_box
)
1141 PurpleAccount
*initial_token_acct
= status_box
->token_status_account
;
1143 if (status_box
->account
)
1146 status_box
->token_status_account
= check_active_accounts_for_identical_statuses();
1148 /* Regenerate the list if it has changed */
1149 if (initial_token_acct
!= status_box
->token_status_account
) {
1150 pidgin_status_box_regenerate(status_box
, TRUE
);
1156 current_savedstatus_changed_cb(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
, PidginStatusBox
*status_box
)
1158 /* Make sure our current status is added to the list of popular statuses */
1159 pidgin_status_box_regenerate(status_box
, TRUE
);
1163 saved_status_updated_cb(PurpleSavedStatus
*status
, PidginStatusBox
*status_box
)
1165 pidgin_status_box_regenerate(status_box
,
1166 purple_savedstatus_get_current() == status
);
1170 pidgin_status_box_list_position (PidginStatusBox
*status_box
, int *x
, int *y
, int *width
, int *height
)
1174 GdkRectangle monitor
;
1175 GtkRequisition popup_req
;
1176 GtkPolicyType hpolicy
, vpolicy
;
1177 GtkAllocation allocation
;
1179 gtk_widget_get_allocation(GTK_WIDGET(status_box
), &allocation
);
1180 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(status_box
)), x
, y
);
1185 *width
= allocation
.width
;
1187 hpolicy
= vpolicy
= GTK_POLICY_NEVER
;
1188 g_object_set(G_OBJECT(status_box
->scrolled_window
),
1189 "hscrollbar-policy", hpolicy
,
1190 "vscrollbar-policy", vpolicy
,
1192 gtk_widget_get_preferred_size(status_box
->popup_frame
, NULL
, &popup_req
);
1194 if (popup_req
.width
> *width
) {
1195 hpolicy
= GTK_POLICY_ALWAYS
;
1196 g_object_set(G_OBJECT(status_box
->scrolled_window
),
1197 "hscrollbar-policy", hpolicy
,
1198 "vscrollbar-policy", vpolicy
,
1200 gtk_widget_get_preferred_size(status_box
->popup_frame
, NULL
, &popup_req
);
1203 *height
= popup_req
.height
;
1205 screen
= gtk_widget_get_screen(GTK_WIDGET(status_box
));
1206 monitor_num
= gdk_screen_get_monitor_at_window(screen
,
1207 gtk_widget_get_window(GTK_WIDGET(status_box
)));
1208 gdk_screen_get_monitor_geometry(screen
, monitor_num
, &monitor
);
1212 else if (*x
+ *width
> monitor
.x
+ monitor
.width
)
1213 *x
= monitor
.x
+ monitor
.width
- *width
;
1215 if (*y
+ allocation
.height
+ *height
<= monitor
.y
+ monitor
.height
)
1216 *y
+= allocation
.height
;
1217 else if (*y
- *height
>= monitor
.y
)
1219 else if (monitor
.y
+ monitor
.height
- (*y
+ allocation
.height
) > *y
- monitor
.y
)
1221 *y
+= allocation
.height
;
1222 *height
= monitor
.y
+ monitor
.height
- *y
;
1226 *height
= *y
- monitor
.y
;
1230 if (popup_req
.height
> *height
)
1232 vpolicy
= GTK_POLICY_ALWAYS
;
1234 g_object_set(G_OBJECT(status_box
->scrolled_window
),
1235 "hscrollbar-policy", hpolicy
,
1236 "vscrollbar-policy", vpolicy
,
1242 popup_grab_on_window(GdkWindow
*window
, GdkEvent
*event
)
1244 guint32 activate_time
= gdk_event_get_time(event
);
1245 GdkDevice
*device
= gdk_event_get_device(event
);
1246 GdkGrabStatus status
;
1248 status
= gdk_device_grab(device
, window
, GDK_OWNERSHIP_WINDOW
, TRUE
,
1249 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1250 GDK_POINTER_MOTION_MASK
| GDK_KEY_PRESS_MASK
|
1251 GDK_KEY_RELEASE_MASK
, NULL
, activate_time
);
1252 if (status
== GDK_GRAB_SUCCESS
) {
1253 status
= gdk_device_grab(gdk_device_get_associated_device(device
),
1254 window
, GDK_OWNERSHIP_WINDOW
, TRUE
,
1255 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1256 GDK_POINTER_MOTION_MASK
| GDK_KEY_PRESS_MASK
|
1257 GDK_KEY_RELEASE_MASK
, NULL
, activate_time
);
1258 if (status
== GDK_GRAB_SUCCESS
)
1261 gdk_device_ungrab(device
, activate_time
);
1269 pidgin_status_box_popup(PidginStatusBox
*box
, GdkEvent
*event
)
1271 int width
, height
, x
, y
;
1272 pidgin_status_box_list_position (box
, &x
, &y
, &width
, &height
);
1274 gtk_widget_set_size_request (box
->popup_window
, width
, height
);
1275 gtk_window_move (GTK_WINDOW (box
->popup_window
), x
, y
);
1276 gtk_widget_show(box
->popup_window
);
1277 gtk_widget_grab_focus (box
->tree_view
);
1278 if (!popup_grab_on_window(gtk_widget_get_window(box
->popup_window
), event
)) {
1279 gtk_widget_hide (box
->popup_window
);
1282 gtk_grab_add (box
->popup_window
);
1283 /*box->popup_in_progress = TRUE;*/
1284 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box
->toggle_button
),
1287 if (box
->active_row
) {
1288 GtkTreePath
*path
= gtk_tree_row_reference_get_path(box
->active_row
);
1289 gtk_tree_view_set_cursor(GTK_TREE_VIEW(box
->tree_view
), path
, NULL
, FALSE
);
1290 gtk_tree_path_free(path
);
1295 pidgin_status_box_popdown(PidginStatusBox
*box
, GdkEvent
*event
)
1299 gtk_widget_hide(box
->popup_window
);
1300 box
->popup_in_progress
= FALSE
;
1301 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box
->toggle_button
), FALSE
);
1302 gtk_grab_remove(box
->popup_window
);
1303 time
= gdk_event_get_time(event
);
1304 device
= gdk_event_get_device(event
);
1305 gdk_device_ungrab(device
, time
);
1306 gdk_device_ungrab(gdk_device_get_associated_device(device
), time
);
1310 toggle_key_press_cb(GtkWidget
*widget
, GdkEventKey
*event
, PidginStatusBox
*box
)
1312 switch (event
->keyval
) {
1313 case GDK_KEY_Return
:
1314 case GDK_KEY_KP_Enter
:
1315 case GDK_KEY_KP_Space
:
1317 if (!box
->popup_in_progress
) {
1318 pidgin_status_box_popup(box
, (GdkEvent
*)event
);
1319 box
->popup_in_progress
= TRUE
;
1321 pidgin_status_box_popdown(box
, (GdkEvent
*)event
);
1330 toggled_cb(GtkWidget
*widget
, GdkEventButton
*event
, PidginStatusBox
*box
)
1332 if (!box
->popup_in_progress
)
1333 pidgin_status_box_popup(box
, (GdkEvent
*)event
);
1335 pidgin_status_box_popdown(box
, (GdkEvent
*)event
);
1340 buddy_icon_set_cb(const char *filename
, PidginStatusBox
*box
)
1342 PurpleImage
*img
= NULL
;
1343 PurpleBuddyIconSpec
*icon_spec
= NULL
;
1346 PurpleProtocol
*protocol
=
1347 purple_protocols_find(purple_account_get_protocol_id(box
->account
));
1349 icon_spec
= purple_protocol_get_icon_spec(protocol
);
1350 if (icon_spec
&& icon_spec
->format
) {
1351 gpointer data
= NULL
;
1354 data
= pidgin_convert_buddy_icon(protocol
, filename
, &len
);
1355 img
= purple_buddy_icons_set_account_icon(box
->account
, data
, len
);
1358 * set_account_icon doesn't give us a reference, but we
1359 * unref one below (for the other code path)
1364 purple_account_set_buddy_icon_path(box
->account
, filename
);
1366 purple_account_set_bool(box
->account
, "use-global-buddyicon", (filename
!= NULL
));
1370 for (accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
1371 PurpleAccount
*account
= accounts
->data
;
1372 PurpleProtocol
*protocol
=
1373 purple_protocols_find(purple_account_get_protocol_id(account
));
1375 icon_spec
= purple_protocol_get_icon_spec(protocol
);
1376 if (icon_spec
&& icon_spec
->format
&&
1377 purple_account_get_bool(account
, "use-global-buddyicon", TRUE
)) {
1378 gpointer data
= NULL
;
1381 data
= pidgin_convert_buddy_icon(protocol
, filename
, &len
);
1382 purple_buddy_icons_set_account_icon(account
, data
, len
);
1383 purple_account_set_buddy_icon_path(account
, filename
);
1387 /* Even if no accounts were processed, load the icon that was set. */
1388 if (filename
!= NULL
)
1389 img
= purple_image_new_from_file(filename
, NULL
);
1392 pidgin_status_box_set_buddy_icon(box
, img
);
1394 g_object_unref(img
);
1398 remove_buddy_icon_cb(GtkWidget
*w
, PidginStatusBox
*box
)
1400 if (box
->account
== NULL
)
1401 /* The pref-connect callback does the actual work */
1402 purple_prefs_set_path(PIDGIN_PREFS_ROOT
"/accounts/buddyicon", NULL
);
1404 buddy_icon_set_cb(NULL
, box
);
1406 gtk_widget_destroy(box
->icon_box_menu
);
1407 box
->icon_box_menu
= NULL
;
1411 choose_buddy_icon_cb(GtkWidget
*w
, PidginStatusBox
*box
)
1413 if (box
->buddy_icon_sel
) {
1414 gtk_window_present(GTK_WINDOW(box
->buddy_icon_sel
));
1416 box
->buddy_icon_sel
= pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(w
)), icon_choose_cb
, box
);
1417 gtk_widget_show_all(box
->buddy_icon_sel
);
1422 icon_choose_cb(const char *filename
, gpointer data
)
1424 PidginStatusBox
*box
= data
;
1426 if (box
->account
== NULL
)
1427 /* The pref-connect callback does the actual work */
1428 purple_prefs_set_path(PIDGIN_PREFS_ROOT
"/accounts/buddyicon", filename
);
1430 buddy_icon_set_cb(filename
, box
);
1433 box
->buddy_icon_sel
= NULL
;
1437 update_buddyicon_cb(const char *name
, PurplePrefType type
,
1438 gconstpointer value
, gpointer data
)
1440 buddy_icon_set_cb(value
, (PidginStatusBox
*) data
);
1444 treeview_activate_current_selection(PidginStatusBox
*status_box
, GtkTreePath
*path
, GdkEvent
*event
)
1446 if (status_box
->active_row
)
1447 gtk_tree_row_reference_free(status_box
->active_row
);
1449 status_box
->active_row
= gtk_tree_row_reference_new(GTK_TREE_MODEL(status_box
->dropdown_store
), path
);
1450 pidgin_status_box_popdown(status_box
, event
);
1451 pidgin_status_box_changed(status_box
);
1454 static void tree_view_delete_current_selection_cb(gpointer data
)
1456 PurpleSavedStatus
*saved
;
1458 saved
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
1459 g_return_if_fail(saved
!= NULL
);
1461 if (purple_savedstatus_get_current() != saved
)
1462 purple_savedstatus_delete_by_status(saved
);
1466 tree_view_delete_current_selection(PidginStatusBox
*status_box
, GtkTreePath
*path
, GdkEvent
*event
)
1470 PurpleSavedStatus
*saved
;
1473 if (status_box
->active_row
) {
1474 /* don't delete active status */
1475 if (gtk_tree_path_compare(path
, gtk_tree_row_reference_get_path(status_box
->active_row
)) == 0)
1479 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
, path
))
1482 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
1486 saved
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
1487 g_return_if_fail(saved
!= NULL
);
1488 if (saved
== purple_savedstatus_get_current())
1491 msg
= g_strdup_printf(_("Are you sure you want to delete %s?"), purple_savedstatus_get_title(saved
));
1493 purple_request_action(saved
, NULL
, msg
, NULL
, 0,
1496 _("Delete"), tree_view_delete_current_selection_cb
,
1501 pidgin_status_box_popdown(status_box
, event
);
1505 treeview_button_release_cb(GtkWidget
*widget
, GdkEventButton
*event
, PidginStatusBox
*status_box
)
1507 GtkTreePath
*path
= NULL
;
1509 GtkWidget
*ewidget
= gtk_get_event_widget ((GdkEvent
*)event
);
1511 if (ewidget
!= status_box
->tree_view
) {
1512 if (ewidget
== status_box
->toggle_button
&&
1513 status_box
->popup_in_progress
&&
1514 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (status_box
->toggle_button
))) {
1515 pidgin_status_box_popdown(status_box
, (GdkEvent
*)event
);
1517 } else if (ewidget
== status_box
->toggle_button
) {
1518 status_box
->popup_in_progress
= TRUE
;
1521 /* released outside treeview */
1522 if (ewidget
!= status_box
->toggle_button
) {
1523 pidgin_status_box_popdown(status_box
, (GdkEvent
*)event
);
1530 ret
= gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (status_box
->tree_view
),
1536 return TRUE
; /* clicked outside window? */
1538 treeview_activate_current_selection(status_box
, path
, (GdkEvent
*)event
);
1539 gtk_tree_path_free (path
);
1545 treeview_key_press_event(GtkWidget
*widget
,
1546 GdkEventKey
*event
, PidginStatusBox
*box
)
1548 if (box
->popup_in_progress
) {
1549 if (event
->keyval
== GDK_KEY_Escape
) {
1550 pidgin_status_box_popdown(box
, (GdkEvent
*)event
);
1553 GtkTreeSelection
*sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(box
->tree_view
));
1557 if (gtk_tree_selection_get_selected(sel
, NULL
, &iter
)) {
1558 gboolean ret
= TRUE
;
1559 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(box
->dropdown_store
), &iter
);
1560 if (event
->keyval
== GDK_KEY_Return
) {
1561 treeview_activate_current_selection(box
, path
, (GdkEvent
*)event
);
1562 } else if (event
->keyval
== GDK_KEY_Delete
) {
1563 tree_view_delete_current_selection(box
, path
, (GdkEvent
*)event
);
1567 gtk_tree_path_free (path
);
1576 treeview_cursor_changed_cb(GtkTreeView
*treeview
, gpointer data
)
1578 GtkTreeSelection
*sel
= gtk_tree_view_get_selection (treeview
);
1579 GtkTreeModel
*model
= GTK_TREE_MODEL (data
);
1581 GtkTreePath
*cursor
;
1582 GtkTreePath
*selection
;
1585 if (gtk_tree_selection_get_selected (sel
, NULL
, &iter
)) {
1586 if ((selection
= gtk_tree_model_get_path (model
, &iter
)) == NULL
) {
1587 /* Shouldn't happen, but ignore anyway */
1591 /* I don't think this can happen, but we'll just ignore it */
1595 gtk_tree_view_get_cursor (treeview
, &cursor
, NULL
);
1596 if (cursor
== NULL
) {
1597 /* Probably won't happen in a 'cursor-changed' event? */
1598 gtk_tree_path_free (selection
);
1602 cmp
= gtk_tree_path_compare (cursor
, selection
);
1604 /* The cursor moved up without moving the selection, so move it up again */
1605 gtk_tree_path_prev (cursor
);
1606 gtk_tree_view_set_cursor (treeview
, cursor
, NULL
, FALSE
);
1607 } else if (cmp
> 0) {
1608 /* The cursor moved down without moving the selection, so move it down again */
1609 gtk_tree_path_next (cursor
);
1610 gtk_tree_view_set_cursor (treeview
, cursor
, NULL
, FALSE
);
1613 gtk_tree_path_free (selection
);
1614 gtk_tree_path_free (cursor
);
1618 pidgin_status_box_buffer_changed_cb(GtkTextBuffer
*buffer
, gpointer data
) {
1619 PidginStatusBox
*status_box
= (PidginStatusBox
*)data
;
1620 if (gtk_widget_get_sensitive(GTK_WIDGET(status_box
)))
1622 if (status_box
->typing
!= 0) {
1623 pidgin_status_box_pulse_typing(status_box
);
1624 g_source_remove(status_box
->typing
);
1626 status_box
->typing
= g_timeout_add_seconds(TYPING_TIMEOUT
, (GSourceFunc
)remove_typing_cb
, status_box
);
1628 pidgin_status_box_refresh(status_box
);
1632 pidgin_status_box_init (PidginStatusBox
*status_box
)
1634 GtkCellRenderer
*text_rend
;
1635 GtkCellRenderer
*icon_rend
;
1636 GtkCellRenderer
*emblem_rend
;
1637 GtkWidget
*toplevel
;
1638 GtkTreeSelection
*sel
;
1640 gtk_widget_set_has_window(GTK_WIDGET(status_box
), FALSE
);
1641 status_box
->editor_visible
= FALSE
;
1642 status_box
->network_available
= purple_network_is_available();
1643 status_box
->connecting
= FALSE
;
1644 status_box
->typing
= 0;
1645 status_box
->toggle_button
= gtk_toggle_button_new();
1646 status_box
->hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
1647 status_box
->cell_view
= gtk_cell_view_new();
1648 status_box
->vsep
= gtk_separator_new(GTK_ORIENTATION_VERTICAL
);
1649 status_box
->arrow
= gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_BUTTON
);
1651 status_box
->store
= gtk_list_store_new(NUM_COLUMNS
, G_TYPE_INT
, G_TYPE_STRING
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
, G_TYPE_STRING
,
1652 G_TYPE_STRING
, G_TYPE_POINTER
, GDK_TYPE_PIXBUF
, G_TYPE_BOOLEAN
);
1653 status_box
->dropdown_store
= gtk_list_store_new(NUM_COLUMNS
, G_TYPE_INT
, G_TYPE_STRING
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
,
1654 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1656 gtk_cell_view_set_model(GTK_CELL_VIEW(status_box
->cell_view
), GTK_TREE_MODEL(status_box
->store
));
1657 gtk_list_store_append(status_box
->store
, &(status_box
->iter
));
1659 atk_object_set_name(gtk_widget_get_accessible(status_box
->toggle_button
), _("Status Selector"));
1661 gtk_container_add(GTK_CONTAINER(status_box
->toggle_button
), status_box
->hbox
);
1662 gtk_box_pack_start(GTK_BOX(status_box
->hbox
), status_box
->cell_view
, TRUE
, TRUE
, 0);
1663 gtk_box_pack_start(GTK_BOX(status_box
->hbox
), status_box
->vsep
, FALSE
, FALSE
, 0);
1664 gtk_box_pack_start(GTK_BOX(status_box
->hbox
), status_box
->arrow
, FALSE
, FALSE
, 0);
1665 gtk_widget_show_all(status_box
->toggle_button
);
1666 gtk_button_set_focus_on_click(GTK_BUTTON(status_box
->toggle_button
), FALSE
);
1668 text_rend
= gtk_cell_renderer_text_new();
1669 icon_rend
= gtk_cell_renderer_pixbuf_new();
1670 emblem_rend
= gtk_cell_renderer_pixbuf_new();
1671 status_box
->popup_window
= gtk_window_new (GTK_WINDOW_POPUP
);
1673 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (status_box
));
1674 if (GTK_IS_WINDOW (toplevel
)) {
1675 gtk_window_set_transient_for (GTK_WINDOW (status_box
->popup_window
),
1676 GTK_WINDOW (toplevel
));
1679 gtk_window_set_resizable (GTK_WINDOW (status_box
->popup_window
), FALSE
);
1680 gtk_window_set_type_hint (GTK_WINDOW (status_box
->popup_window
),
1681 GDK_WINDOW_TYPE_HINT_POPUP_MENU
);
1682 gtk_window_set_screen (GTK_WINDOW (status_box
->popup_window
),
1683 gtk_widget_get_screen (GTK_WIDGET (status_box
)));
1684 status_box
->popup_frame
= gtk_frame_new (NULL
);
1685 gtk_frame_set_shadow_type (GTK_FRAME (status_box
->popup_frame
),
1686 GTK_SHADOW_ETCHED_IN
);
1687 gtk_container_add (GTK_CONTAINER (status_box
->popup_window
),
1688 status_box
->popup_frame
);
1690 gtk_widget_show (status_box
->popup_frame
);
1692 status_box
->tree_view
= gtk_tree_view_new ();
1693 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (status_box
->tree_view
));
1694 gtk_tree_selection_set_mode (sel
, GTK_SELECTION_BROWSE
);
1695 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (status_box
->tree_view
),
1697 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (status_box
->tree_view
),
1699 gtk_tree_view_set_model (GTK_TREE_VIEW (status_box
->tree_view
),
1700 GTK_TREE_MODEL(status_box
->dropdown_store
));
1701 status_box
->column
= gtk_tree_view_column_new ();
1702 gtk_tree_view_append_column (GTK_TREE_VIEW (status_box
->tree_view
),
1703 status_box
->column
);
1704 gtk_tree_view_column_pack_start(status_box
->column
, icon_rend
, FALSE
);
1705 gtk_tree_view_column_pack_start(status_box
->column
, text_rend
, TRUE
);
1706 gtk_tree_view_column_pack_start(status_box
->column
, emblem_rend
, FALSE
);
1707 gtk_tree_view_column_set_attributes(status_box
->column
, icon_rend
, "stock-id", ICON_STOCK_COLUMN
, NULL
);
1708 gtk_tree_view_column_set_attributes(status_box
->column
, text_rend
, "markup", TEXT_COLUMN
, NULL
);
1709 gtk_tree_view_column_set_attributes(status_box
->column
, emblem_rend
, "stock-id", EMBLEM_COLUMN
, "visible", EMBLEM_VISIBLE_COLUMN
, NULL
);
1711 status_box
->scrolled_window
= pidgin_make_scrollable(status_box
->tree_view
, GTK_POLICY_NEVER
, GTK_POLICY_NEVER
, GTK_SHADOW_NONE
, -1, -1);
1712 gtk_container_add (GTK_CONTAINER (status_box
->popup_frame
),
1713 status_box
->scrolled_window
);
1715 gtk_widget_show(status_box
->tree_view
);
1716 gtk_tree_view_set_search_column(GTK_TREE_VIEW(status_box
->tree_view
), TEXT_COLUMN
);
1717 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(status_box
->tree_view
),
1718 pidgin_tree_view_search_equal_func
, NULL
, NULL
);
1720 g_object_set(text_rend
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1722 status_box
->icon_rend
= gtk_cell_renderer_pixbuf_new();
1723 status_box
->text_rend
= gtk_cell_renderer_text_new();
1724 emblem_rend
= gtk_cell_renderer_pixbuf_new();
1725 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box
->cell_view
), status_box
->icon_rend
, FALSE
);
1726 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box
->cell_view
), status_box
->text_rend
, TRUE
);
1727 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box
->cell_view
), emblem_rend
, FALSE
);
1728 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box
->cell_view
), status_box
->icon_rend
, "stock-id", ICON_STOCK_COLUMN
, NULL
);
1729 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box
->cell_view
), status_box
->text_rend
, "markup", TEXT_COLUMN
, NULL
);
1730 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box
->cell_view
), emblem_rend
, "pixbuf", EMBLEM_COLUMN
, "visible", EMBLEM_VISIBLE_COLUMN
, NULL
);
1731 g_object_set(status_box
->text_rend
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1733 status_box
->vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, FALSE
);
1734 status_box
->editor
= talkatu_editor_new();
1735 status_box
->view
= talkatu_editor_get_view(TALKATU_EDITOR(status_box
->editor
));
1736 status_box
->buffer
= talkatu_html_buffer_new();
1737 gtk_text_view_set_buffer(GTK_TEXT_VIEW(status_box
->view
), status_box
->buffer
);
1739 g_signal_connect(G_OBJECT(status_box
->buffer
), "changed",
1740 G_CALLBACK(pidgin_status_box_buffer_changed_cb
),
1743 g_signal_connect(G_OBJECT(status_box
->toggle_button
), "key-press-event",
1744 G_CALLBACK(toggle_key_press_cb
), status_box
);
1745 g_signal_connect(G_OBJECT(status_box
->toggle_button
), "button-press-event",
1746 G_CALLBACK(toggled_cb
), status_box
);
1747 g_signal_connect(G_OBJECT(status_box
->view
), "key-press-event",
1748 G_CALLBACK(editor_remove_focus
), status_box
);
1750 gtk_widget_set_parent(status_box
->vbox
, GTK_WIDGET(status_box
));
1751 gtk_widget_show_all(status_box
->vbox
);
1753 gtk_widget_set_parent(status_box
->toggle_button
, GTK_WIDGET(status_box
));
1755 gtk_box_pack_start(GTK_BOX(status_box
->vbox
), status_box
->editor
, TRUE
, TRUE
, 0);
1757 g_signal_connect(G_OBJECT(status_box
), "scroll-event", G_CALLBACK(combo_box_scroll_event_cb
), NULL
);
1758 g_signal_connect(G_OBJECT(status_box
->popup_window
), "button_release_event", G_CALLBACK(treeview_button_release_cb
), status_box
);
1759 g_signal_connect(G_OBJECT(status_box
->popup_window
), "key_press_event", G_CALLBACK(treeview_key_press_event
), status_box
);
1760 g_signal_connect(G_OBJECT(status_box
->tree_view
), "cursor-changed",
1761 G_CALLBACK(treeview_cursor_changed_cb
), status_box
->dropdown_store
);
1763 gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(status_box
->tree_view
), dropdown_store_row_separator_func
, NULL
, NULL
);
1765 status_box
->token_status_account
= check_active_accounts_for_identical_statuses();
1767 cache_pixbufs(status_box
);
1768 pidgin_status_box_regenerate(status_box
, TRUE
);
1770 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed",
1772 PURPLE_CALLBACK(current_savedstatus_changed_cb
),
1774 purple_signal_connect(purple_savedstatuses_get_handle(),
1775 "savedstatus-added", status_box
,
1776 PURPLE_CALLBACK(saved_status_updated_cb
), status_box
);
1777 purple_signal_connect(purple_savedstatuses_get_handle(),
1778 "savedstatus-deleted", status_box
,
1779 PURPLE_CALLBACK(saved_status_updated_cb
), status_box
);
1780 purple_signal_connect(purple_savedstatuses_get_handle(),
1781 "savedstatus-modified", status_box
,
1782 PURPLE_CALLBACK(saved_status_updated_cb
), status_box
);
1783 purple_signal_connect(purple_accounts_get_handle(), "account-enabled", status_box
,
1784 PURPLE_CALLBACK(account_enabled_cb
),
1786 purple_signal_connect(purple_accounts_get_handle(), "account-disabled", status_box
,
1787 PURPLE_CALLBACK(account_enabled_cb
),
1789 purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", status_box
,
1790 PURPLE_CALLBACK(account_status_changed_cb
),
1793 purple_prefs_connect_callback(status_box
, PIDGIN_PREFS_ROOT
"/accounts/buddyicon",
1794 update_buddyicon_cb
, status_box
);
1799 pidgin_status_box_get_preferred_height(GtkWidget
*widget
, gint
*minimum_height
,
1800 gint
*natural_height
)
1802 gint box_min_height
, box_nat_height
;
1803 gint border_width
= gtk_container_get_border_width(GTK_CONTAINER (widget
));
1805 gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget
)->toggle_button
,
1806 minimum_height
, natural_height
);
1808 *minimum_height
= MAX(*minimum_height
, 34) + border_width
* 2;
1809 *natural_height
= MAX(*natural_height
, 34) + border_width
* 2;
1811 /* If the editor is visible, then add some additional padding */
1812 if (PIDGIN_STATUS_BOX(widget
)->editor_visible
) {
1813 gtk_widget_get_preferred_height(PIDGIN_STATUS_BOX(widget
)->vbox
,
1814 &box_min_height
, &box_nat_height
);
1816 if (box_min_height
> 1)
1817 *minimum_height
+= box_min_height
+ border_width
* 2;
1819 if (box_nat_height
> 1)
1820 *natural_height
+= box_nat_height
+ border_width
* 2;
1824 /* From gnome-panel */
1826 do_colorshift (GdkPixbuf
*dest
, GdkPixbuf
*src
, int shift
)
1829 gint width
, height
, has_alpha
, srcrowstride
, destrowstride
;
1830 guchar
*target_pixels
;
1831 guchar
*original_pixels
;
1837 has_alpha
= gdk_pixbuf_get_has_alpha (src
);
1838 width
= gdk_pixbuf_get_width (src
);
1839 height
= gdk_pixbuf_get_height (src
);
1840 srcrowstride
= gdk_pixbuf_get_rowstride (src
);
1841 destrowstride
= gdk_pixbuf_get_rowstride (dest
);
1842 target_pixels
= gdk_pixbuf_get_pixels (dest
);
1843 original_pixels
= gdk_pixbuf_get_pixels (src
);
1845 for (i
= 0; i
< height
; i
++) {
1846 pixdest
= target_pixels
+ i
*destrowstride
;
1847 pixsrc
= original_pixels
+ i
*srcrowstride
;
1848 for (j
= 0; j
< width
; j
++) {
1853 *(pixdest
++) = CLAMP(val
, 0, 255);
1855 *(pixdest
++) = CLAMP(val
, 0, 255);
1857 *(pixdest
++) = CLAMP(val
, 0, 255);
1859 *(pixdest
++) = *(pixsrc
++);
1865 pidgin_status_box_size_allocate(GtkWidget
*widget
,
1866 GtkAllocation
*allocation
)
1868 PidginStatusBox
*status_box
= PIDGIN_STATUS_BOX(widget
);
1869 GtkRequisition req
= {0,0};
1870 GtkAllocation parent_alc
, box_alc
, icon_alc
;
1871 gint border_width
= gtk_container_get_border_width(GTK_CONTAINER (widget
));
1873 gtk_widget_get_preferred_size(status_box
->toggle_button
, NULL
, &req
);
1874 /* Make this icon the same size as other buddy icons in the list; unless it already wants to be bigger */
1876 req
.height
= MAX(req
.height
, 34);
1877 req
.height
+= border_width
* 2;
1879 box_alc
= *allocation
;
1881 box_alc
.width
-= (border_width
* 2);
1882 box_alc
.height
= MAX(1, ((allocation
->height
- req
.height
) - (border_width
*2)));
1883 box_alc
.x
+= border_width
;
1884 box_alc
.y
+= req
.height
+ border_width
;
1885 gtk_widget_size_allocate(status_box
->vbox
, &box_alc
);
1887 parent_alc
= *allocation
;
1888 parent_alc
.height
= MAX(1,req
.height
- (border_width
*2));
1889 parent_alc
.width
-= (border_width
* 2);
1890 parent_alc
.x
+= border_width
;
1891 parent_alc
.y
+= border_width
;
1893 if (status_box
->icon_box
)
1895 parent_alc
.width
-= (parent_alc
.height
+ border_width
);
1896 icon_alc
= parent_alc
;
1897 icon_alc
.height
= MAX(1, icon_alc
.height
) - 2;
1898 icon_alc
.width
= icon_alc
.height
;
1899 icon_alc
.x
+= allocation
->width
- (icon_alc
.width
+ border_width
+ 1);
1902 if (status_box
->icon_size
!= icon_alc
.height
)
1904 status_box
->icon_size
= icon_alc
.height
;
1905 pidgin_status_box_redisplay_buddy_icon(status_box
);
1907 gtk_widget_size_allocate(status_box
->icon_box
, &icon_alc
);
1909 gtk_widget_size_allocate(status_box
->toggle_button
, &parent_alc
);
1910 gtk_widget_set_allocation(GTK_WIDGET(status_box
), allocation
);
1914 pidgin_status_box_draw(GtkWidget
*widget
, cairo_t
*cr
)
1916 PidginStatusBox
*status_box
= PIDGIN_STATUS_BOX(widget
);
1917 gtk_widget_draw(status_box
->toggle_button
, cr
);
1919 gtk_container_propagate_draw(GTK_CONTAINER(widget
), status_box
->vbox
, cr
);
1921 if (status_box
->icon_box
) {
1922 gtk_container_propagate_draw(GTK_CONTAINER(widget
),
1923 status_box
->icon_box
, cr
);
1925 if (status_box
->icon_opaque
) {
1926 GtkAllocation allocation
;
1927 GtkStyleContext
*context
;
1929 gtk_widget_get_allocation(status_box
->icon_box
, &allocation
);
1930 context
= gtk_widget_get_style_context(widget
);
1931 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_BUTTON
);
1932 gtk_render_frame(context
, cr
, allocation
.x
-1, allocation
.y
-1, 34, 34);
1939 pidgin_status_box_forall(GtkContainer
*container
,
1940 gboolean include_internals
,
1941 GtkCallback callback
,
1942 gpointer callback_data
)
1944 PidginStatusBox
*status_box
= PIDGIN_STATUS_BOX (container
);
1946 if (include_internals
)
1948 (* callback
) (status_box
->vbox
, callback_data
);
1949 (* callback
) (status_box
->toggle_button
, callback_data
);
1950 (* callback
) (status_box
->arrow
, callback_data
);
1951 if (status_box
->icon_box
)
1952 (* callback
) (status_box
->icon_box
, callback_data
);
1957 pidgin_status_box_new()
1959 return g_object_new(PIDGIN_TYPE_STATUS_BOX
, "account", NULL
,
1960 "iconsel", TRUE
, NULL
);
1964 pidgin_status_box_new_with_account(PurpleAccount
*account
)
1966 return g_object_new(PIDGIN_TYPE_STATUS_BOX
, "account", account
,
1967 "iconsel", TRUE
, NULL
);
1971 * pidgin_status_box_add:
1972 * @status_box: The status box itself.
1973 * @type: A PidginStatusBoxItemType.
1974 * @pixbuf: The icon to associate with this row in the menu. The
1975 * function will try to decide a pixbuf if none is given.
1976 * @title: The title of this item. For the primitive entries,
1977 * this is something like "Available" or "Away." For
1978 * the saved statuses, this is something like
1979 * "My favorite away message!" This should be
1980 * plaintext (non-markedup) (this function escapes it).
1981 * @desc: The secondary text for this item. This will be
1982 * placed on the row below the title, in a dimmer
1983 * font (generally gray). This text should be plaintext
1984 * (non-markedup) (this function escapes it).
1985 * @data: Data to be associated with this row in the dropdown
1986 * menu. For primitives this is the value of the
1987 * PurpleStatusPrimitive. For saved statuses this is the
1988 * creation timestamp.
1990 * Add a row to the dropdown menu.
1993 pidgin_status_box_add(PidginStatusBox
*status_box
, PidginStatusBoxItemType type
, GdkPixbuf
*pixbuf
,
1994 const char *title
, const char *desc
, gpointer data
)
1998 const char *stock
= NULL
;
2002 text
= g_markup_escape_text(title
, -1);
2006 const char *aa_color
;
2007 gchar
*escaped_title
, *escaped_desc
;
2009 aa_color
= pidgin_get_dim_grey_string(GTK_WIDGET(status_box
));
2011 escaped_title
= g_markup_escape_text(title
, -1);
2012 escaped_desc
= g_markup_escape_text(desc
, -1);
2013 text
= g_strdup_printf("%s - <span color=\"%s\" size=\"smaller\">%s</span>",
2015 aa_color
, escaped_desc
);
2016 g_free(escaped_title
);
2017 g_free(escaped_desc
);
2021 PurpleStatusPrimitive prim
= PURPLE_STATUS_UNSET
;
2022 if (type
== PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
) {
2023 prim
= GPOINTER_TO_INT(data
);
2024 } else if (type
== PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR
||
2025 type
== PIDGIN_STATUS_BOX_TYPE_POPULAR
) {
2026 PurpleSavedStatus
*saved
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
2028 prim
= purple_savedstatus_get_primitive_type(saved
);
2032 stock
= pidgin_stock_id_from_status_primitive(prim
);
2035 gtk_list_store_append(status_box
->dropdown_store
, &iter
);
2036 gtk_list_store_set(status_box
->dropdown_store
, &iter
,
2038 ICON_STOCK_COLUMN
, stock
,
2040 TITLE_COLUMN
, title
,
2043 EMBLEM_VISIBLE_COLUMN
, type
== PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR
,
2044 EMBLEM_COLUMN
, GTK_STOCK_SAVE
,
2050 pidgin_status_box_add_separator(PidginStatusBox
*status_box
)
2052 /* Don't do anything unless GTK actually supports
2053 * gtk_combo_box_set_row_separator_func */
2056 gtk_list_store_append(status_box
->dropdown_store
, &iter
);
2057 gtk_list_store_set(status_box
->dropdown_store
, &iter
,
2058 TYPE_COLUMN
, PIDGIN_STATUS_BOX_TYPE_SEPARATOR
,
2063 pidgin_status_box_set_network_available(PidginStatusBox
*status_box
, gboolean available
)
2067 status_box
->network_available
= available
;
2068 pidgin_status_box_refresh(status_box
);
2072 pidgin_status_box_set_connecting(PidginStatusBox
*status_box
, gboolean connecting
)
2076 status_box
->connecting
= connecting
;
2077 pidgin_status_box_refresh(status_box
);
2081 pixbuf_size_prepared_cb(GdkPixbufLoader
*loader
, int width
, int height
, gpointer data
)
2084 GtkIconSize icon_size
= gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM
);
2085 gtk_icon_size_lookup(icon_size
, &w
, &h
);
2087 w
= width
* h
/ height
;
2088 else if (width
> height
)
2089 h
= height
* w
/ width
;
2090 gdk_pixbuf_loader_set_size(loader
, w
, h
);
2094 pidgin_status_box_redisplay_buddy_icon(PidginStatusBox
*status_box
)
2097 /* This is sometimes called before the box is shown, and we will not have a size */
2098 if (status_box
->icon_size
<= 0)
2101 if (status_box
->buddy_icon
)
2102 g_object_unref(status_box
->buddy_icon
);
2103 if (status_box
->buddy_icon_hover
)
2104 g_object_unref(status_box
->buddy_icon_hover
);
2105 status_box
->buddy_icon
= NULL
;
2106 status_box
->buddy_icon_hover
= NULL
;
2108 if (status_box
->buddy_icon_img
!= NULL
)
2110 GdkPixbufLoader
*loader
;
2111 GError
*error
= NULL
;
2113 loader
= gdk_pixbuf_loader_new();
2115 g_signal_connect(G_OBJECT(loader
), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb
), NULL
);
2116 if (!gdk_pixbuf_loader_write(loader
,
2117 purple_image_get_data(status_box
->buddy_icon_img
),
2118 purple_image_get_data_size(status_box
->buddy_icon_img
),
2121 purple_debug_warning("gtkstatusbox",
2122 "gdk_pixbuf_loader_write() failed with size=%"
2123 G_GSIZE_FORMAT
": %s", purple_image_get_data_size(
2124 status_box
->buddy_icon_img
),
2125 error
? error
->message
: "(no error message)");
2127 g_error_free(error
);
2128 } else if (!gdk_pixbuf_loader_close(loader
, &error
) || error
) {
2129 purple_debug_warning("gtkstatusbox",
2130 "gdk_pixbuf_loader_close() failed for image of "
2131 "size %" G_GSIZE_FORMAT
": %s",
2132 purple_image_get_data_size(status_box
->buddy_icon_img
),
2133 error
? error
->message
: "(no error message)");
2135 g_error_free(error
);
2137 GdkPixbuf
*buf
, *scale
;
2138 int scale_width
, scale_height
;
2140 buf
= gdk_pixbuf_loader_get_pixbuf(loader
);
2141 scale_width
= gdk_pixbuf_get_width(buf
);
2142 scale_height
= gdk_pixbuf_get_height(buf
);
2143 scale
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8, scale_width
, scale_height
);
2144 gdk_pixbuf_fill(scale
, 0x00000000);
2145 gdk_pixbuf_copy_area(buf
, 0, 0, scale_width
, scale_height
, scale
, 0, 0);
2146 if (pidgin_gdk_pixbuf_is_opaque(scale
))
2147 pidgin_gdk_pixbuf_make_round(scale
);
2148 status_box
->buddy_icon
= scale
;
2151 g_object_unref(loader
);
2154 if (status_box
->buddy_icon
== NULL
)
2156 /* Show a placeholder icon */
2157 GtkIconSize icon_size
= gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL
);
2158 status_box
->buddy_icon
= gtk_widget_render_icon(GTK_WIDGET(status_box
),
2159 PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR
,
2160 icon_size
, "PidginStatusBox");
2163 if (status_box
->buddy_icon
!= NULL
) {
2164 status_box
->icon_opaque
= pidgin_gdk_pixbuf_is_opaque(status_box
->buddy_icon
);
2165 gtk_image_set_from_pixbuf(GTK_IMAGE(status_box
->icon
), status_box
->buddy_icon
);
2166 status_box
->buddy_icon_hover
= gdk_pixbuf_copy(status_box
->buddy_icon
);
2167 do_colorshift(status_box
->buddy_icon_hover
, status_box
->buddy_icon_hover
, 32);
2168 gtk_widget_queue_resize(GTK_WIDGET(status_box
));
2173 pidgin_status_box_set_buddy_icon(PidginStatusBox
*status_box
, PurpleImage
*img
)
2175 if (status_box
->buddy_icon_img
)
2176 g_object_unref(status_box
->buddy_icon_img
);
2177 status_box
->buddy_icon_img
= img
;
2178 if (status_box
->buddy_icon_img
!= NULL
)
2179 g_object_ref(status_box
->buddy_icon_img
);
2181 pidgin_status_box_redisplay_buddy_icon(status_box
);
2185 pidgin_status_box_pulse_connecting(PidginStatusBox
*status_box
)
2189 if (!connecting_stock_ids
[++status_box
->connecting_index
])
2190 status_box
->connecting_index
= 0;
2191 pidgin_status_box_refresh(status_box
);
2195 pidgin_status_box_pulse_typing(PidginStatusBox
*status_box
)
2197 if (!typing_stock_ids
[++status_box
->typing_index
])
2198 status_box
->typing_index
= 0;
2199 pidgin_status_box_refresh(status_box
);
2203 activate_currently_selected_status(PidginStatusBox
*status_box
)
2205 PidginStatusBoxItemType type
;
2211 PurpleSavedStatus
*saved_status
= NULL
;
2212 gboolean changed
= TRUE
;
2214 path
= gtk_tree_row_reference_get_path(status_box
->active_row
);
2215 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
, path
))
2217 gtk_tree_path_free(path
);
2219 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
2225 * If the currently selected status is "New..." or
2226 * "Saved..." or a popular status then do nothing.
2227 * Popular statuses are
2228 * activated elsewhere, and we update the status_box
2229 * accordingly by connecting to the savedstatus-changed
2230 * signal and then calling status_menu_refresh_iter()
2232 if (type
!= PIDGIN_STATUS_BOX_TYPE_PRIMITIVE
)
2235 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
2236 TITLE_COLUMN
, &title
, -1);
2238 message
= pidgin_status_box_get_message(status_box
);
2239 if (!message
|| !*message
)
2241 gtk_widget_hide(status_box
->vbox
);
2242 status_box
->editor_visible
= FALSE
;
2247 if (status_box
->account
== NULL
) {
2248 PurpleStatusType
*acct_status_type
= NULL
;
2249 const char *id
= NULL
; /* id of acct_status_type */
2250 PurpleStatusPrimitive primitive
= GPOINTER_TO_INT(data
);
2252 /* Save the newly selected status to prefs.xml and status.xml */
2254 /* Has the status really been changed? */
2255 if (status_box
->token_status_account
) {
2257 PurpleStatus
*status
;
2258 GtkTreePath
*path
= gtk_tree_row_reference_get_path(status_box
->active_row
);
2259 active
= gtk_tree_path_get_indices(path
)[0];
2261 gtk_tree_path_free(path
);
2263 status
= purple_account_get_active_status(status_box
->token_status_account
);
2265 acct_status_type
= find_status_type_by_index(status_box
->token_status_account
, active
);
2266 id
= purple_status_type_get_id(acct_status_type
);
2268 if (purple_strequal(id
, purple_status_get_id(status
)) &&
2269 purple_strequal(message
, purple_status_get_attr_string(status
, "message")))
2271 /* Selected status and previous status is the same */
2272 PurpleSavedStatus
*ss
= purple_savedstatus_get_current();
2273 /* Make sure that statusbox displays the correct thing.
2274 * It can get messed up if the previous selection was a
2275 * saved status that wasn't supported by this account */
2276 if ((purple_savedstatus_get_primitive_type(ss
) == primitive
)
2277 && purple_savedstatus_is_transient(ss
)
2278 && purple_savedstatus_has_substatuses(ss
))
2282 saved_status
= purple_savedstatus_get_current();
2283 if (purple_savedstatus_get_primitive_type(saved_status
) == primitive
&&
2284 !purple_savedstatus_has_substatuses(saved_status
) &&
2285 purple_strequal(purple_savedstatus_get_message(saved_status
), message
))
2293 /* Manually find the appropriate transient status */
2294 if (status_box
->token_status_account
) {
2295 GList
*iter
= purple_savedstatuses_get_all();
2296 GList
*tmp
, *active_accts
= purple_accounts_get_all_active();
2298 for (; iter
!= NULL
; iter
= iter
->next
) {
2299 PurpleSavedStatus
*ss
= iter
->data
;
2300 const char *ss_msg
= purple_savedstatus_get_message(ss
);
2301 /* find a known transient status that is the same as the
2302 * new selected one */
2303 if ((purple_savedstatus_get_primitive_type(ss
) == primitive
) && purple_savedstatus_is_transient(ss
) &&
2304 purple_savedstatus_has_substatuses(ss
) && /* Must have substatuses */
2305 purple_strequal(ss_msg
, message
))
2307 gboolean found
= FALSE
;
2308 /* this status must have substatuses for all the active accts */
2309 for(tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2310 PurpleAccount
*acct
= tmp
->data
;
2311 PurpleSavedStatusSub
*sub
= purple_savedstatus_get_substatus(ss
, acct
);
2313 const PurpleStatusType
*sub_type
=
2314 purple_savedstatus_substatus_get_status_type(sub
);
2315 const char *subtype_status_id
= purple_status_type_get_id(sub_type
);
2316 if (purple_strequal(subtype_status_id
, id
)) {
2330 g_list_free(active_accts
);
2333 /* If we've used this type+message before, lookup the transient status */
2334 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2337 /* If this type+message is unique then create a new transient saved status */
2338 if (saved_status
== NULL
)
2340 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2341 purple_savedstatus_set_message(saved_status
, message
);
2342 if (status_box
->token_status_account
) {
2343 GList
*tmp
, *active_accts
= purple_accounts_get_all_active();
2344 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2345 purple_savedstatus_set_substatus(saved_status
,
2346 (PurpleAccount
*) tmp
->data
, acct_status_type
, message
);
2348 g_list_free(active_accts
);
2352 /* Set the status for each account */
2353 purple_savedstatus_activate(saved_status
);
2358 PurpleStatusType
*status_type
;
2359 PurpleStatus
*status
;
2360 const char *id
= NULL
;
2362 status
= purple_account_get_active_status(status_box
->account
);
2364 active
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(status_box
), "active"));
2366 status_type
= find_status_type_by_index(status_box
->account
, active
);
2367 id
= purple_status_type_get_id(status_type
);
2369 if (purple_strequal(id
, purple_status_get_id(status
)) &&
2370 purple_strequal(message
, purple_status_get_attr_string(status
, "message")))
2372 /* Selected status and previous status is the same */
2379 purple_account_set_status(status_box
->account
, id
,
2380 TRUE
, "message", message
, NULL
);
2382 purple_account_set_status(status_box
->account
, id
,
2385 saved_status
= purple_savedstatus_get_current();
2386 if (purple_savedstatus_is_transient(saved_status
))
2387 purple_savedstatus_set_substatus(saved_status
, status_box
->account
,
2388 status_type
, message
);
2396 static void update_size(PidginStatusBox
*status_box
)
2398 if (!status_box
->editor_visible
)
2400 if (status_box
->vbox
!= NULL
)
2401 gtk_widget_set_size_request(status_box
->vbox
, -1, -1);
2405 gtk_widget_set_size_request(status_box
->vbox
, -1, -1);
2408 static void remove_typing_cb(PidginStatusBox
*status_box
)
2410 if (status_box
->typing
== 0)
2412 /* Nothing has changed, so we don't need to do anything */
2413 status_menu_refresh_iter(status_box
, FALSE
);
2417 g_source_remove(status_box
->typing
);
2418 status_box
->typing
= 0;
2420 activate_currently_selected_status(status_box
);
2421 pidgin_status_box_refresh(status_box
);
2424 static void pidgin_status_box_changed(PidginStatusBox
*status_box
)
2426 GtkTreePath
*path
= gtk_tree_row_reference_get_path(status_box
->active_row
);
2428 PidginStatusBoxItemType type
;
2430 GList
*accounts
= NULL
, *node
;
2432 gboolean wastyping
= FALSE
;
2435 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
, path
))
2437 active
= gtk_tree_path_get_indices(path
)[0];
2438 gtk_tree_path_free(path
);
2439 g_object_set_data(G_OBJECT(status_box
), "active", GINT_TO_POINTER(active
));
2441 gtk_tree_model_get(GTK_TREE_MODEL(status_box
->dropdown_store
), &iter
,
2445 if ((wastyping
= (status_box
->typing
!= 0)))
2446 g_source_remove(status_box
->typing
);
2447 status_box
->typing
= 0;
2449 if (gtk_widget_get_sensitive(GTK_WIDGET(status_box
)))
2451 if (type
== PIDGIN_STATUS_BOX_TYPE_POPULAR
|| type
== PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR
)
2453 PurpleSavedStatus
*saved
;
2454 saved
= purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data
));
2455 g_return_if_fail(saved
!= NULL
);
2456 purple_savedstatus_activate(saved
);
2460 if (type
== PIDGIN_STATUS_BOX_TYPE_CUSTOM
)
2462 PurpleSavedStatus
*saved_status
;
2463 saved_status
= purple_savedstatus_get_current();
2464 if (purple_savedstatus_get_primitive_type(saved_status
) == PURPLE_STATUS_AVAILABLE
)
2465 saved_status
= purple_savedstatus_new(NULL
, PURPLE_STATUS_AWAY
);
2466 pidgin_status_editor_show(FALSE
,
2467 purple_savedstatus_is_transient(saved_status
)
2468 ? saved_status
: NULL
);
2469 status_menu_refresh_iter(status_box
, wastyping
);
2471 pidgin_status_box_refresh(status_box
);
2475 if (type
== PIDGIN_STATUS_BOX_TYPE_SAVED
)
2477 pidgin_status_window_show();
2478 status_menu_refresh_iter(status_box
, wastyping
);
2480 pidgin_status_box_refresh(status_box
);
2486 * Show the message box whenever the primitive allows for a
2487 * message attribute on any protocol that is enabled,
2488 * or our protocol, if we have account set
2490 if (status_box
->account
)
2491 accounts
= g_list_prepend(accounts
, status_box
->account
);
2493 accounts
= purple_accounts_get_all_active();
2494 status_box
->editor_visible
= FALSE
;
2495 for (node
= accounts
; node
!= NULL
; node
= node
->next
)
2497 PurpleAccount
*account
;
2498 PurpleStatusType
*status_type
;
2500 account
= node
->data
;
2501 status_type
= purple_account_get_status_type_with_primitive(account
, GPOINTER_TO_INT(data
));
2502 if ((status_type
!= NULL
) &&
2503 (purple_status_type_get_attr(status_type
, "message") != NULL
))
2505 status_box
->editor_visible
= TRUE
;
2509 g_list_free(accounts
);
2511 if (gtk_widget_get_sensitive(GTK_WIDGET(status_box
)))
2513 if (status_box
->editor_visible
)
2515 GtkTextIter start
, end
;
2517 gtk_widget_show_all(status_box
->vbox
);
2518 status_box
->typing
= g_timeout_add_seconds(TYPING_TIMEOUT
, (GSourceFunc
)remove_typing_cb
, status_box
);
2519 gtk_widget_grab_focus(status_box
->view
);
2521 gtk_text_buffer_get_start_iter(status_box
->buffer
, &start
);
2522 gtk_text_buffer_get_end_iter(status_box
->buffer
, &end
);
2523 gtk_text_buffer_select_range(status_box
->buffer
, &start
, &end
);
2527 gtk_widget_hide(status_box
->vbox
);
2528 activate_currently_selected_status(status_box
); /* This is where we actually set the status */
2531 pidgin_status_box_refresh(status_box
);
2535 get_statusbox_index(PidginStatusBox
*box
, PurpleSavedStatus
*saved_status
)
2539 switch (purple_savedstatus_get_primitive_type(saved_status
))
2541 /* In reverse order */
2542 case PURPLE_STATUS_OFFLINE
:
2545 case PURPLE_STATUS_INVISIBLE
:
2548 case PURPLE_STATUS_UNAVAILABLE
:
2551 case PURPLE_STATUS_AWAY
:
2554 case PURPLE_STATUS_AVAILABLE
:
2564 char *pidgin_status_box_get_message(PidginStatusBox
*status_box
)
2566 if (status_box
->editor_visible
)
2567 return g_strstrip(talkatu_markup_get_html(status_box
->buffer
, NULL
));