1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Collabora Ltd.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Xavier Claessens <xclaesse@gmail.com>
28 #include <glade/glade.h>
29 #include <glib/gi18n.h>
31 #include <libmissioncontrol/mission-control.h>
33 #include <libempathy/empathy-contact-list.h>
34 #include <libempathy/empathy-contact-manager.h>
35 #include <libempathy/gossip-contact.h>
36 #include <libempathy/gossip-debug.h>
37 #include <libempathy/gossip-utils.h>
38 #include <libempathy/gossip-conf.h>
39 #include <libempathy/empathy-idle.h>
41 #include "empathy-status-icon.h"
42 #include "gossip-presence-chooser.h"
43 #include "gossip-preferences.h"
44 #include "gossip-ui-utils.h"
45 #include "gossip-accounts-dialog.h"
48 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
49 EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
51 #define DEBUG_DOMAIN "StatusIcon"
53 /* Number of ms to wait when blinking */
54 #define BLINK_TIMEOUT 500
56 struct _EmpathyStatusIconPriv {
58 EmpathyContactManager *manager;
62 gboolean showing_state_icon;
66 GtkWidget *popup_menu;
67 GtkWidget *show_window_item;
68 GtkWidget *message_item;
69 GtkWidget *status_item;
72 typedef struct _StatusIconEvent StatusIconEvent;
74 typedef void (*EventActivatedFunc) (StatusIconEvent *event);
76 struct _StatusIconEvent {
79 EventActivatedFunc func;
84 static void empathy_status_icon_class_init (EmpathyStatusIconClass *klass);
85 static void empathy_status_icon_init (EmpathyStatusIcon *icon);
86 static void status_icon_finalize (GObject *object);
87 static void status_icon_idle_notify_cb (EmpathyIdle *idle,
89 EmpathyStatusIcon *icon);
90 static void status_icon_update_tooltip (EmpathyStatusIcon *icon);
91 static void status_icon_set_from_state (EmpathyStatusIcon *icon);
92 static void status_icon_toggle_visibility (EmpathyStatusIcon *icon);
93 static void status_icon_activate_cb (GtkStatusIcon *status_icon,
94 EmpathyStatusIcon *icon);
95 static gboolean status_icon_delete_event_cb (GtkWidget *widget,
97 EmpathyStatusIcon *icon);
98 static void status_icon_popup_menu_cb (GtkStatusIcon *status_icon,
101 EmpathyStatusIcon *icon);
102 static void status_icon_create_menu (EmpathyStatusIcon *icon);
103 static void status_icon_new_message_cb (GtkWidget *widget,
104 EmpathyStatusIcon *icon);
105 static void status_icon_quit_cb (GtkWidget *window,
106 EmpathyStatusIcon *icon);
107 static void status_icon_show_hide_window_cb (GtkWidget *widget,
108 EmpathyStatusIcon *icon);
109 static void status_icon_local_pending_cb (EmpathyContactManager *manager,
110 GossipContact *contact,
112 EmpathyStatusIcon *icon);
113 static void status_icon_event_subscribe_cb (StatusIconEvent *event);
114 static StatusIconEvent * status_icon_event_new (EmpathyStatusIcon *icon,
115 const gchar *icon_name,
116 const gchar *message);
117 static void status_icon_event_remove (EmpathyStatusIcon *icon,
118 StatusIconEvent *event);
119 static gboolean status_icon_event_timeout_cb (EmpathyStatusIcon *icon);
120 static void status_icon_event_free (StatusIconEvent *event);
122 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
125 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
127 GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 object_class->finalize = status_icon_finalize;
131 g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
135 empathy_status_icon_init (EmpathyStatusIcon *icon)
137 EmpathyStatusIconPriv *priv;
140 priv = GET_PRIV (icon);
142 priv->icon = gtk_status_icon_new ();
143 priv->idle = empathy_idle_new ();
144 priv->manager = empathy_contact_manager_new ();
145 priv->showing_state_icon = TRUE;
147 status_icon_create_menu (icon);
148 status_icon_set_from_state (icon);
149 status_icon_update_tooltip (icon);
151 g_signal_connect (priv->idle, "notify",
152 G_CALLBACK (status_icon_idle_notify_cb),
154 g_signal_connect (priv->icon, "activate",
155 G_CALLBACK (status_icon_activate_cb),
157 g_signal_connect (priv->icon, "popup-menu",
158 G_CALLBACK (status_icon_popup_menu_cb),
160 g_signal_connect (priv->manager, "local-pending",
161 G_CALLBACK (status_icon_local_pending_cb),
164 pending = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (priv->manager));
165 for (l = pending; l; l = l->next) {
166 EmpathyContactListInfo *info;
169 status_icon_local_pending_cb (priv->manager,
174 g_list_free (pending);
178 status_icon_finalize (GObject *object)
180 EmpathyStatusIconPriv *priv;
182 priv = GET_PRIV (object);
184 g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
185 g_list_free (priv->events);
187 if (priv->blink_timeout) {
188 g_source_remove (priv->blink_timeout);
191 g_object_unref (priv->icon);
192 g_object_unref (priv->window);
193 g_object_unref (priv->idle);
194 g_object_unref (priv->manager);
198 empathy_status_icon_new (GtkWindow *window)
200 EmpathyStatusIconPriv *priv;
201 EmpathyStatusIcon *icon;
202 gboolean should_hide;
205 g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
207 icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
208 priv = GET_PRIV (icon);
210 priv->window = g_object_ref (window);
212 g_signal_connect (priv->window, "delete-event",
213 G_CALLBACK (status_icon_delete_event_cb),
216 gossip_conf_get_bool (gossip_conf_get (),
217 GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN,
219 visible = gossip_window_get_is_visible (window);
221 if ((!should_hide && !visible) || (should_hide && visible)) {
222 status_icon_toggle_visibility (icon);
229 status_icon_idle_notify_cb (EmpathyIdle *idle,
231 EmpathyStatusIcon *icon)
233 EmpathyStatusIconPriv *priv;
235 priv = GET_PRIV (icon);
237 if (priv->showing_state_icon) {
238 status_icon_set_from_state (icon);
241 status_icon_update_tooltip (icon);
245 status_icon_update_tooltip (EmpathyStatusIcon *icon)
247 EmpathyStatusIconPriv *priv;
248 const gchar *tooltip = NULL;
250 priv = GET_PRIV (icon);
253 StatusIconEvent *event;
255 event = priv->events->data;
256 tooltip = event->message;
260 tooltip = empathy_idle_get_status (priv->idle);
263 gtk_status_icon_set_tooltip (priv->icon, tooltip);
267 status_icon_set_from_state (EmpathyStatusIcon *icon)
269 EmpathyStatusIconPriv *priv;
271 const gchar *icon_name;
273 priv = GET_PRIV (icon);
275 state = empathy_idle_get_state (priv->idle);
276 icon_name = gossip_icon_name_for_presence_state (state);
277 gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
281 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
283 EmpathyStatusIconPriv *priv;
286 priv = GET_PRIV (icon);
288 visible = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
291 gtk_widget_hide (GTK_WIDGET (priv->window));
292 gossip_conf_set_bool (gossip_conf_get (),
293 GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, TRUE);
297 gossip_window_present (GTK_WINDOW (priv->window), TRUE);
298 gossip_conf_set_bool (gossip_conf_get (),
299 GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, FALSE);
301 /* Show the accounts dialog if there is no enabled accounts */
302 accounts = mc_accounts_list_by_enabled (TRUE);
304 mc_accounts_list_free (accounts);
306 gossip_debug (DEBUG_DOMAIN,
307 "No enabled account, Showing account dialog");
308 gossip_accounts_dialog_show (GTK_WINDOW (priv->window));
314 status_icon_activate_cb (GtkStatusIcon *status_icon,
315 EmpathyStatusIcon *icon)
317 EmpathyStatusIconPriv *priv;
319 priv = GET_PRIV (icon);
322 status_icon_event_remove (icon, priv->events->data);
324 status_icon_toggle_visibility (icon);
329 status_icon_delete_event_cb (GtkWidget *widget,
331 EmpathyStatusIcon *icon)
333 status_icon_toggle_visibility (icon);
339 status_icon_popup_menu_cb (GtkStatusIcon *status_icon,
342 EmpathyStatusIcon *icon)
344 EmpathyStatusIconPriv *priv;
348 priv = GET_PRIV (icon);
350 show = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
352 g_signal_handlers_block_by_func (priv->show_window_item,
353 status_icon_show_hide_window_cb,
355 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
357 g_signal_handlers_unblock_by_func (priv->show_window_item,
358 status_icon_show_hide_window_cb,
361 submenu = gossip_presence_chooser_create_menu ();
362 gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
365 gtk_menu_popup (GTK_MENU (priv->popup_menu),
367 gtk_status_icon_position_menu,
374 status_icon_create_menu (EmpathyStatusIcon *icon)
376 EmpathyStatusIconPriv *priv;
379 priv = GET_PRIV (icon);
381 glade = gossip_glade_get_file ("empathy-status-icon.glade",
384 "tray_menu", &priv->popup_menu,
385 "tray_show_list", &priv->show_window_item,
386 "tray_new_message", &priv->message_item,
387 "tray_status", &priv->status_item,
390 gossip_glade_connect (glade,
392 "tray_new_message", "activate", status_icon_new_message_cb,
393 "tray_quit", "activate", status_icon_quit_cb,
396 g_signal_connect (priv->show_window_item, "toggled",
397 G_CALLBACK (status_icon_show_hide_window_cb),
400 g_object_unref (glade);
404 status_icon_new_message_cb (GtkWidget *widget,
405 EmpathyStatusIcon *icon)
407 EmpathyStatusIconPriv *priv;
409 priv = GET_PRIV (icon);
411 //gossip_new_message_dialog_show (GTK_WINDOW (priv->window));
415 status_icon_quit_cb (GtkWidget *window,
416 EmpathyStatusIcon *icon)
422 status_icon_show_hide_window_cb (GtkWidget *widget,
423 EmpathyStatusIcon *icon)
425 status_icon_toggle_visibility (icon);
429 status_icon_local_pending_cb (EmpathyContactManager *manager,
430 GossipContact *contact,
432 EmpathyStatusIcon *icon)
434 EmpathyStatusIconPriv *priv;
435 StatusIconEvent *event;
439 priv = GET_PRIV (icon);
441 for (l = priv->events; l; l = l->next) {
442 if (gossip_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) {
447 str = g_strdup_printf (_("Subscription requested for %s\n"
449 gossip_contact_get_name (contact),
452 event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str);
453 event->user_data = g_object_ref (contact);
454 event->func = status_icon_event_subscribe_cb;
460 status_icon_event_subscribe_cb (StatusIconEvent *event)
462 GossipContact *contact;
464 contact = GOSSIP_CONTACT (event->user_data);
466 g_object_unref (contact);
469 static StatusIconEvent *
470 status_icon_event_new (EmpathyStatusIcon *icon,
471 const gchar *icon_name,
472 const gchar *message)
474 EmpathyStatusIconPriv *priv;
475 StatusIconEvent *event;
477 priv = GET_PRIV (icon);
479 event = g_slice_new0 (StatusIconEvent);
480 event->icon_name = g_strdup (icon_name);
481 event->message = g_strdup (message);
483 priv->events = g_list_append (priv->events, event);
484 if (!priv->blink_timeout) {
485 priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
486 (GSourceFunc) status_icon_event_timeout_cb,
488 status_icon_event_timeout_cb (icon);
495 status_icon_event_remove (EmpathyStatusIcon *icon,
496 StatusIconEvent *event)
498 EmpathyStatusIconPriv *priv;
500 priv = GET_PRIV (icon);
505 priv->events = g_list_remove (priv->events, event);
506 status_icon_event_free (event);
507 status_icon_update_tooltip (icon);
513 status_icon_set_from_state (icon);
514 priv->showing_state_icon = TRUE;
516 if (priv->blink_timeout) {
517 g_source_remove (priv->blink_timeout);
518 priv->blink_timeout = 0;
524 status_icon_event_timeout_cb (EmpathyStatusIcon *icon)
526 EmpathyStatusIconPriv *priv;
528 priv = GET_PRIV (icon);
530 priv->showing_state_icon = !priv->showing_state_icon;
532 if (priv->showing_state_icon) {
533 status_icon_set_from_state (icon);
535 StatusIconEvent *event;
537 event = priv->events->data;
538 gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name);
540 status_icon_update_tooltip (icon);
546 status_icon_event_free (StatusIconEvent *event)
548 g_free (event->icon_name);
549 g_free (event->message);
550 g_slice_free (StatusIconEvent, event);