]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-status-icon.c
Add support for blinking when there is an event. Make use of EmpathyIdle
[empathy.git] / libempathy-gtk / empathy-status-icon.c
index 01a5424938db60d036a1105318502cae8885e6cd..5c364083b1791a76809f146f5ac97fd47663fc19 100644 (file)
 
 #include <gtk/gtk.h>
 #include <glade/glade.h>
+#include <glib/gi18n.h>
 
 #include <libmissioncontrol/mission-control.h>
 
+#include <libempathy/empathy-contact-list.h>
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/gossip-contact.h>
 #include <libempathy/gossip-debug.h>
 #include <libempathy/gossip-utils.h>
 #include <libempathy/gossip-conf.h>
 
 #define DEBUG_DOMAIN "StatusIcon"
 
+/* Number of ms to wait when blinking */
+#define BLINK_TIMEOUT 500
+
 struct _EmpathyStatusIconPriv {
-       MissionControl *mc;
-       GtkStatusIcon  *icon;
-       EmpathyIdle    *idle;
+       GtkStatusIcon         *icon;
+       EmpathyContactManager *manager;
+       EmpathyIdle           *idle;
+       GList                 *events;
+       guint                  blink_timeout;
+       gboolean               showing_state_icon;
+
+       GtkWindow             *window;
+
+       GtkWidget             *popup_menu;
+       GtkWidget             *show_window_item;
+       GtkWidget             *message_item;
+       GtkWidget             *status_item;
+};
 
-       GtkWindow      *window;
+typedef struct _StatusIconEvent StatusIconEvent;
 
-       GtkWidget      *popup_menu;
-       GtkWidget      *show_window_item;
-       GtkWidget      *message_item;
-       GtkWidget      *status_item;
+typedef void (*EventActivatedFunc) (StatusIconEvent *event);
+
+struct _StatusIconEvent {
+       gchar              *icon_name;
+       gchar              *message;
+       EventActivatedFunc  func;
+       gpointer            user_data;
 };
 
-static void     empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
-static void     empathy_status_icon_init        (EmpathyStatusIcon      *icon);
-static void     status_icon_finalize            (GObject                *object);
-static void     status_icon_presence_changed_cb (MissionControl         *mc,
-                                                McPresence              state,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
-static void     status_icon_activate_cb         (GtkStatusIcon          *status_icon,
-                                                EmpathyStatusIcon      *icon);
-static gboolean status_icon_delete_event_cb     (GtkWidget              *widget,
-                                                GdkEvent               *event,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
-                                                guint                   button,
-                                                guint                   activate_time,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_create_menu         (EmpathyStatusIcon      *icon);
-static void     status_icon_new_message_cb      (GtkWidget              *widget,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_quit_cb             (GtkWidget              *window,
-                                                EmpathyStatusIcon      *icon);
-static void     status_icon_show_hide_window_cb (GtkWidget              *widget,
-                                                EmpathyStatusIcon      *icon);
+
+static void       empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
+static void       empathy_status_icon_init        (EmpathyStatusIcon      *icon);
+static void       status_icon_finalize            (GObject                *object);
+static void       status_icon_idle_notify_cb      (EmpathyIdle            *idle,
+                                                  GParamSpec             *param,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_update_tooltip      (EmpathyStatusIcon      *icon);
+static void       status_icon_set_from_state      (EmpathyStatusIcon      *icon);
+static void       status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
+static void       status_icon_activate_cb         (GtkStatusIcon          *status_icon,
+                                                  EmpathyStatusIcon      *icon);
+static gboolean   status_icon_delete_event_cb     (GtkWidget              *widget,
+                                                  GdkEvent               *event,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
+                                                  guint                   button,
+                                                  guint                   activate_time,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_create_menu         (EmpathyStatusIcon      *icon);
+static void       status_icon_new_message_cb      (GtkWidget              *widget,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_quit_cb             (GtkWidget              *window,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_show_hide_window_cb (GtkWidget              *widget,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_local_pending_cb    (EmpathyContactManager  *manager,
+                                                  GossipContact          *contact,
+                                                  gchar                  *message,
+                                                  EmpathyStatusIcon      *icon);
+static void       status_icon_event_subscribe_cb  (StatusIconEvent        *event);
+static StatusIconEvent * status_icon_event_new    (EmpathyStatusIcon      *icon,
+                                                  const gchar            *icon_name,
+                                                  const gchar            *message);
+static void       status_icon_event_remove        (EmpathyStatusIcon      *icon,
+                                                  StatusIconEvent        *event);
+static gboolean   status_icon_event_timeout_cb    (EmpathyStatusIcon      *icon);
+static void       status_icon_event_free          (StatusIconEvent        *event);
 
 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
 
@@ -99,29 +135,43 @@ static void
 empathy_status_icon_init (EmpathyStatusIcon *icon)
 {
        EmpathyStatusIconPriv *priv;
-       McPresence             state;
+       GList                 *pending, *l;
 
        priv = GET_PRIV (icon);
 
        priv->icon = gtk_status_icon_new ();
-       priv->mc = gossip_mission_control_new ();
        priv->idle = empathy_idle_new ();
+       priv->manager = empathy_contact_manager_new ();
+       priv->showing_state_icon = TRUE;
 
        status_icon_create_menu (icon);
+       status_icon_set_from_state (icon);
+       status_icon_update_tooltip (icon);
 
-       state = mission_control_get_presence_actual (priv->mc, NULL);
-       status_icon_presence_changed_cb (priv->mc, state, icon);
-       
-       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
-                                    "PresenceStatusActual",
-                                    G_CALLBACK (status_icon_presence_changed_cb),
-                                    icon, NULL);
+       g_signal_connect (priv->idle, "notify",
+                         G_CALLBACK (status_icon_idle_notify_cb),
+                         icon);
        g_signal_connect (priv->icon, "activate",
                          G_CALLBACK (status_icon_activate_cb),
                          icon);
        g_signal_connect (priv->icon, "popup-menu",
                          G_CALLBACK (status_icon_popup_menu_cb),
                          icon);
+       g_signal_connect (priv->manager, "local-pending",
+                         G_CALLBACK (status_icon_local_pending_cb),
+                         icon);
+
+       pending = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (priv->manager));
+       for (l = pending; l; l = l->next) {
+               EmpathyContactListInfo *info;
+
+               info = l->data;
+               status_icon_local_pending_cb (priv->manager,
+                                             info->contact,
+                                             info->message,
+                                             icon);
+       }
+       g_list_free (pending);
 }
 
 static void
@@ -131,15 +181,17 @@ status_icon_finalize (GObject *object)
 
        priv = GET_PRIV (object);
 
-       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
-                                       "PresenceStatusActual",
-                                       G_CALLBACK (status_icon_presence_changed_cb),
-                                       object);
+       g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
+       g_list_free (priv->events);
+
+       if (priv->blink_timeout) {
+               g_source_remove (priv->blink_timeout);
+       }
 
-       g_object_unref (priv->mc);
        g_object_unref (priv->icon);
        g_object_unref (priv->window);
        g_object_unref (priv->idle);
+       g_object_unref (priv->manager);
 }
 
 EmpathyStatusIcon *
@@ -174,33 +226,55 @@ empathy_status_icon_new (GtkWindow *window)
 }
 
 static void
-status_icon_presence_changed_cb (MissionControl    *mc,
-                                McPresence         state,
-                                EmpathyStatusIcon *icon)
+status_icon_idle_notify_cb (EmpathyIdle       *idle,
+                           GParamSpec        *param,
+                           EmpathyStatusIcon *icon)
 {
        EmpathyStatusIconPriv *priv;
-       const gchar           *icon_name;
-       gchar                 *status;
 
        priv = GET_PRIV (icon);
 
-       icon_name = gossip_icon_name_for_presence_state (state);
-       status = mission_control_get_presence_message_actual (priv->mc, NULL);
-       if (G_STR_EMPTY (status)) {
-               g_free (status);
-               status = g_strdup (gossip_presence_state_get_default_status (state));
+       if (priv->showing_state_icon) {
+               status_icon_set_from_state (icon);
        }
 
-       gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
-       gtk_status_icon_set_tooltip (priv->icon, status);
+       status_icon_update_tooltip (icon);
+}
+
+static void
+status_icon_update_tooltip (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       const gchar           *tooltip = NULL;
 
-       g_free (status);
+       priv = GET_PRIV (icon);
 
-       if (state < MC_PRESENCE_AVAILABLE) {
-               gtk_widget_set_sensitive (priv->message_item, FALSE);
-       } else {
-               gtk_widget_set_sensitive (priv->message_item, TRUE);
+       if (priv->events) {
+               StatusIconEvent *event;
+
+               event = priv->events->data;
+               tooltip = event->message;
+       }
+
+       if (!tooltip) {
+               tooltip = empathy_idle_get_status (priv->idle);
        }
+
+       gtk_status_icon_set_tooltip (priv->icon, tooltip);      
+}
+
+static void
+status_icon_set_from_state (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       McPresence             state;
+       const gchar           *icon_name;
+
+       priv = GET_PRIV (icon);
+
+       state = empathy_idle_get_state (priv->idle);
+       icon_name = gossip_icon_name_for_presence_state (state);
+       gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
 }
 
 static void
@@ -240,7 +314,15 @@ static void
 status_icon_activate_cb (GtkStatusIcon     *status_icon,
                         EmpathyStatusIcon *icon)
 {
-       status_icon_toggle_visibility (icon);
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       if (priv->events) {
+               status_icon_event_remove (icon, priv->events->data);
+       } else {
+               status_icon_toggle_visibility (icon);
+       }
 }
 
 static gboolean
@@ -343,3 +425,128 @@ status_icon_show_hide_window_cb (GtkWidget         *widget,
        status_icon_toggle_visibility (icon);
 }
 
+static void
+status_icon_local_pending_cb (EmpathyContactManager *manager,
+                             GossipContact         *contact,
+                             gchar                 *message,
+                             EmpathyStatusIcon     *icon)
+{
+       EmpathyStatusIconPriv *priv;
+       StatusIconEvent       *event;
+       gchar                 *str;
+       GList                 *l;
+
+       priv = GET_PRIV (icon);
+
+       for (l = priv->events; l; l = l->next) {
+               if (gossip_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) {
+                       return;
+               }
+       }
+
+       str = g_strdup_printf (_("Subscription requested for %s\n"
+                                "Message: %s"),
+                              gossip_contact_get_name (contact),
+                              message);
+
+       event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str);
+       event->user_data = g_object_ref (contact);
+       event->func = status_icon_event_subscribe_cb;
+
+       g_free (str);
+}
+
+static void
+status_icon_event_subscribe_cb (StatusIconEvent *event)
+{
+       GossipContact *contact;
+
+       contact = GOSSIP_CONTACT (event->user_data);
+
+       g_object_unref (contact);
+}
+
+static StatusIconEvent *
+status_icon_event_new (EmpathyStatusIcon *icon,
+                      const gchar       *icon_name,
+                      const gchar       *message)
+{
+       EmpathyStatusIconPriv *priv;
+       StatusIconEvent       *event;
+
+       priv = GET_PRIV (icon);
+
+       event = g_slice_new0 (StatusIconEvent);
+       event->icon_name = g_strdup (icon_name);        
+       event->message = g_strdup (message);
+
+       priv->events = g_list_append (priv->events, event);
+       if (!priv->blink_timeout) {
+               priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
+                                                    (GSourceFunc) status_icon_event_timeout_cb,
+                                                    icon);
+               status_icon_event_timeout_cb (icon);
+       }
+
+       return event;
+}
+
+static void
+status_icon_event_remove (EmpathyStatusIcon *icon,
+                         StatusIconEvent   *event)
+{
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       if (event->func) {
+               event->func (event);
+       }
+       priv->events = g_list_remove (priv->events, event);
+       status_icon_event_free (event);
+       status_icon_update_tooltip (icon);
+
+       if (priv->events) {
+               return;
+       }
+
+       status_icon_set_from_state (icon);
+       priv->showing_state_icon = TRUE;
+
+       if (priv->blink_timeout) {
+               g_source_remove (priv->blink_timeout);
+               priv->blink_timeout = 0;
+
+       }
+}
+
+static gboolean
+status_icon_event_timeout_cb (EmpathyStatusIcon *icon)
+{
+       EmpathyStatusIconPriv *priv;
+
+       priv = GET_PRIV (icon);
+
+       priv->showing_state_icon = !priv->showing_state_icon;
+
+       if (priv->showing_state_icon) {
+               status_icon_set_from_state (icon);
+       } else {
+               StatusIconEvent *event;
+
+               event = priv->events->data;
+               gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name);
+       }
+       status_icon_update_tooltip (icon);
+
+       return TRUE;
+}
+
+static void
+status_icon_event_free (StatusIconEvent *event)
+{
+       g_free (event->icon_name);
+       g_free (event->message);
+       g_slice_free (StatusIconEvent, event);
+}
+