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/empathy-contact.h>
36 #include <libempathy/empathy-debug.h>
37 #include <libempathy/empathy-utils.h>
38 #include <libempathy/empathy-conf.h>
39 #include <libempathy/empathy-idle.h>
41 #include "empathy-status-icon.h"
42 #include "empathy-contact-dialogs.h"
43 #include "empathy-presence-chooser.h"
44 #include "empathy-preferences.h"
45 #include "empathy-ui-utils.h"
46 #include "empathy-accounts-dialog.h"
49 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
50 EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
52 #define DEBUG_DOMAIN "StatusIcon"
54 /* Number of ms to wait when blinking */
55 #define BLINK_TIMEOUT 500
57 typedef struct _StatusIconEvent StatusIconEvent;
59 struct _EmpathyStatusIconPriv {
61 EmpathyContactManager *manager;
65 StatusIconEvent *flash_state_event;
69 GtkWidget *popup_menu;
70 GtkWidget *show_window_item;
71 GtkWidget *message_item;
72 GtkWidget *status_item;
75 typedef void (*EventActivatedFunc) (StatusIconEvent *event);
77 struct _StatusIconEvent {
80 EventActivatedFunc func;
85 static void empathy_status_icon_class_init (EmpathyStatusIconClass *klass);
86 static void empathy_status_icon_init (EmpathyStatusIcon *icon);
87 static void status_icon_finalize (GObject *object);
88 static void status_icon_idle_notify_cb (EmpathyStatusIcon *icon);
89 static void status_icon_update_tooltip (EmpathyStatusIcon *icon);
90 static void status_icon_set_from_state (EmpathyStatusIcon *icon);
91 static void status_icon_toggle_visibility (EmpathyStatusIcon *icon);
92 static void status_icon_activate_cb (GtkStatusIcon *status_icon,
93 EmpathyStatusIcon *icon);
94 static gboolean status_icon_delete_event_cb (GtkWidget *widget,
96 EmpathyStatusIcon *icon);
97 static void status_icon_popup_menu_cb (GtkStatusIcon *status_icon,
100 EmpathyStatusIcon *icon);
101 static void status_icon_create_menu (EmpathyStatusIcon *icon);
102 static void status_icon_new_message_cb (GtkWidget *widget,
103 EmpathyStatusIcon *icon);
104 static void status_icon_quit_cb (GtkWidget *window,
105 EmpathyStatusIcon *icon);
106 static void status_icon_show_hide_window_cb (GtkWidget *widget,
107 EmpathyStatusIcon *icon);
108 static void status_icon_local_pending_cb (EmpathyContactManager *manager,
109 EmpathyContact *contact,
111 EmpathyStatusIcon *icon);
112 static void status_icon_event_subscribe_cb (StatusIconEvent *event);
113 static void status_icon_event_flash_state_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 ();
146 status_icon_create_menu (icon);
147 status_icon_idle_notify_cb (icon);
149 g_signal_connect_swapped (priv->idle, "notify",
150 G_CALLBACK (status_icon_idle_notify_cb),
152 g_signal_connect (priv->icon, "activate",
153 G_CALLBACK (status_icon_activate_cb),
155 g_signal_connect (priv->icon, "popup-menu",
156 G_CALLBACK (status_icon_popup_menu_cb),
158 g_signal_connect (priv->manager, "local-pending",
159 G_CALLBACK (status_icon_local_pending_cb),
162 pending = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (priv->manager));
163 for (l = pending; l; l = l->next) {
164 EmpathyContactListInfo *info;
167 status_icon_local_pending_cb (priv->manager,
172 g_list_free (pending);
176 status_icon_finalize (GObject *object)
178 EmpathyStatusIconPriv *priv;
180 priv = GET_PRIV (object);
182 g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
183 g_list_free (priv->events);
185 if (priv->blink_timeout) {
186 g_source_remove (priv->blink_timeout);
189 g_object_unref (priv->icon);
190 g_object_unref (priv->window);
191 g_object_unref (priv->idle);
192 g_object_unref (priv->manager);
196 empathy_status_icon_new (GtkWindow *window)
198 EmpathyStatusIconPriv *priv;
199 EmpathyStatusIcon *icon;
200 gboolean should_hide;
203 g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
205 icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
206 priv = GET_PRIV (icon);
208 priv->window = g_object_ref (window);
210 g_signal_connect (priv->window, "delete-event",
211 G_CALLBACK (status_icon_delete_event_cb),
214 empathy_conf_get_bool (empathy_conf_get (),
215 EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
217 visible = empathy_window_get_is_visible (window);
219 if ((!should_hide && !visible) || (should_hide && visible)) {
220 status_icon_toggle_visibility (icon);
227 status_icon_idle_notify_cb (EmpathyStatusIcon *icon)
229 EmpathyStatusIconPriv *priv;
230 McPresence flash_state;
232 priv = GET_PRIV (icon);
234 flash_state = empathy_idle_get_flash_state (priv->idle);
235 if (flash_state != MC_PRESENCE_UNSET) {
236 const gchar *icon_name;
238 icon_name = empathy_icon_name_for_presence_state (flash_state);
239 if (!priv->flash_state_event) {
240 /* We are now flashing */
241 priv->flash_state_event = status_icon_event_new (icon, icon_name, NULL);
242 priv->flash_state_event->user_data = icon;
243 priv->flash_state_event->func = status_icon_event_flash_state_cb;
246 /* We are still flashing but with another state */
247 g_free (priv->flash_state_event->icon_name);
248 priv->flash_state_event->icon_name = g_strdup (icon_name);
251 else if (priv->flash_state_event) {
252 /* We are no more flashing */
253 status_icon_event_remove (icon, priv->flash_state_event);
254 priv->flash_state_event = NULL;
257 if (!priv->current_event) {
258 status_icon_set_from_state (icon);
261 status_icon_update_tooltip (icon);
265 status_icon_update_tooltip (EmpathyStatusIcon *icon)
267 EmpathyStatusIconPriv *priv;
268 const gchar *tooltip = NULL;
270 priv = GET_PRIV (icon);
273 StatusIconEvent *event;
275 event = priv->events->data;
276 tooltip = event->message;
280 tooltip = empathy_idle_get_status (priv->idle);
283 gtk_status_icon_set_tooltip (priv->icon, tooltip);
287 status_icon_set_from_state (EmpathyStatusIcon *icon)
289 EmpathyStatusIconPriv *priv;
291 const gchar *icon_name;
293 priv = GET_PRIV (icon);
295 state = empathy_idle_get_state (priv->idle);
296 icon_name = empathy_icon_name_for_presence_state (state);
297 gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
301 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
303 EmpathyStatusIconPriv *priv;
306 priv = GET_PRIV (icon);
308 visible = empathy_window_get_is_visible (GTK_WINDOW (priv->window));
311 gtk_widget_hide (GTK_WIDGET (priv->window));
312 empathy_conf_set_bool (empathy_conf_get (),
313 EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, TRUE);
317 empathy_window_present (GTK_WINDOW (priv->window), TRUE);
318 empathy_conf_set_bool (empathy_conf_get (),
319 EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, FALSE);
321 /* Show the accounts dialog if there is no enabled accounts */
322 accounts = mc_accounts_list_by_enabled (TRUE);
324 mc_accounts_list_free (accounts);
326 empathy_debug (DEBUG_DOMAIN,
327 "No enabled account, Showing account dialog");
328 empathy_accounts_dialog_show (GTK_WINDOW (priv->window));
334 status_icon_activate_cb (GtkStatusIcon *status_icon,
335 EmpathyStatusIcon *icon)
337 EmpathyStatusIconPriv *priv;
339 priv = GET_PRIV (icon);
342 status_icon_event_remove (icon, priv->events->data);
344 status_icon_toggle_visibility (icon);
349 status_icon_delete_event_cb (GtkWidget *widget,
351 EmpathyStatusIcon *icon)
353 status_icon_toggle_visibility (icon);
359 status_icon_popup_menu_cb (GtkStatusIcon *status_icon,
362 EmpathyStatusIcon *icon)
364 EmpathyStatusIconPriv *priv;
368 priv = GET_PRIV (icon);
370 show = empathy_window_get_is_visible (GTK_WINDOW (priv->window));
372 g_signal_handlers_block_by_func (priv->show_window_item,
373 status_icon_show_hide_window_cb,
375 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
377 g_signal_handlers_unblock_by_func (priv->show_window_item,
378 status_icon_show_hide_window_cb,
381 submenu = empathy_presence_chooser_create_menu ();
382 gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
385 gtk_menu_popup (GTK_MENU (priv->popup_menu),
387 gtk_status_icon_position_menu,
394 status_icon_create_menu (EmpathyStatusIcon *icon)
396 EmpathyStatusIconPriv *priv;
399 priv = GET_PRIV (icon);
401 glade = empathy_glade_get_file ("empathy-status-icon.glade",
404 "tray_menu", &priv->popup_menu,
405 "tray_show_list", &priv->show_window_item,
406 "tray_new_message", &priv->message_item,
407 "tray_status", &priv->status_item,
410 empathy_glade_connect (glade,
412 "tray_new_message", "activate", status_icon_new_message_cb,
413 "tray_quit", "activate", status_icon_quit_cb,
416 g_signal_connect (priv->show_window_item, "toggled",
417 G_CALLBACK (status_icon_show_hide_window_cb),
420 g_object_unref (glade);
424 status_icon_new_message_cb (GtkWidget *widget,
425 EmpathyStatusIcon *icon)
427 EmpathyStatusIconPriv *priv;
429 priv = GET_PRIV (icon);
431 //empathy_new_message_dialog_show (GTK_WINDOW (priv->window));
435 status_icon_quit_cb (GtkWidget *window,
436 EmpathyStatusIcon *icon)
442 status_icon_show_hide_window_cb (GtkWidget *widget,
443 EmpathyStatusIcon *icon)
445 status_icon_toggle_visibility (icon);
449 status_icon_local_pending_cb (EmpathyContactManager *manager,
450 EmpathyContact *contact,
452 EmpathyStatusIcon *icon)
454 EmpathyStatusIconPriv *priv;
455 StatusIconEvent *event;
459 priv = GET_PRIV (icon);
461 for (l = priv->events; l; l = l->next) {
462 if (empathy_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) {
467 str = g_strdup_printf (_("Subscription requested for %s\n"
469 empathy_contact_get_name (contact),
472 event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str);
473 event->user_data = g_object_ref (contact);
474 event->func = status_icon_event_subscribe_cb;
480 status_icon_event_subscribe_cb (StatusIconEvent *event)
482 EmpathyContact *contact;
484 contact = EMPATHY_CONTACT (event->user_data);
486 empathy_subscription_dialog_show (contact, NULL);
488 g_object_unref (contact);
492 status_icon_event_flash_state_cb (StatusIconEvent *event)
494 EmpathyStatusIconPriv *priv;
496 priv = GET_PRIV (event->user_data);
498 empathy_idle_set_flash_state (priv->idle, MC_PRESENCE_UNSET);
502 static StatusIconEvent *
503 status_icon_event_new (EmpathyStatusIcon *icon,
504 const gchar *icon_name,
505 const gchar *message)
507 EmpathyStatusIconPriv *priv;
508 StatusIconEvent *event;
510 priv = GET_PRIV (icon);
512 event = g_slice_new0 (StatusIconEvent);
513 event->icon_name = g_strdup (icon_name);
514 event->message = g_strdup (message);
516 priv->events = g_list_append (priv->events, event);
517 if (!priv->blink_timeout) {
518 priv->current_event = NULL;
519 priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
520 (GSourceFunc) status_icon_event_timeout_cb,
522 status_icon_event_timeout_cb (icon);
529 status_icon_event_remove (EmpathyStatusIcon *icon,
530 StatusIconEvent *event)
532 EmpathyStatusIconPriv *priv;
534 priv = GET_PRIV (icon);
539 priv->events = g_list_remove (priv->events, event);
540 status_icon_event_free (event);
541 priv->current_event = NULL;
542 status_icon_update_tooltip (icon);
543 status_icon_set_from_state (icon);
549 if (priv->blink_timeout) {
550 g_source_remove (priv->blink_timeout);
551 priv->blink_timeout = 0;
556 status_icon_event_timeout_cb (EmpathyStatusIcon *icon)
558 EmpathyStatusIconPriv *priv;
560 priv = GET_PRIV (icon);
562 if (priv->current_event) {
563 priv->current_event = priv->current_event->next;
565 priv->current_event = priv->events;
568 if (!priv->current_event) {
569 status_icon_set_from_state (icon);
571 StatusIconEvent *event;
573 event = priv->current_event->data;
574 gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name);
576 status_icon_update_tooltip (icon);
582 status_icon_event_free (StatusIconEvent *event)
584 g_free (event->icon_name);
585 g_free (event->message);
586 g_slice_free (StatusIconEvent, event);