]> git.0d.be Git - empathy.git/blob - src/empathy-status-icon.c
keep track of connected status to disable menu item for "new conversation"
[empathy.git] / src / empathy-status-icon.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <gtk/gtk.h>
27 #include <glade/glade.h>
28 #include <glib/gi18n.h>
29
30 #include <libempathy/empathy-debug.h>
31 #include <libempathy/empathy-utils.h>
32 #include <libempathy/empathy-idle.h>
33
34 #include <libempathy-gtk/empathy-presence-chooser.h>
35 #include <libempathy-gtk/empathy-conf.h>
36 #include <libempathy-gtk/empathy-ui-utils.h>
37 #include <libempathy-gtk/empathy-accounts-dialog.h>
38 #include <libempathy-gtk/empathy-images.h>
39 #include <libempathy-gtk/empathy-new-message-dialog.h>
40
41 #include "empathy-status-icon.h"
42 #include "empathy-preferences.h"
43 #include "empathy-filter.h"
44
45 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
46                        EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
47
48 #define DEBUG_DOMAIN "StatusIcon"
49
50 /* Number of ms to wait when blinking */
51 #define BLINK_TIMEOUT 500
52
53 typedef struct _StatusIconEvent StatusIconEvent;
54
55 struct _EmpathyStatusIconPriv {
56         GtkStatusIcon      *icon;
57         EmpathyIdle        *idle;
58         MissionControl     *mc;
59         EmpathyFilter      *filter;
60         EmpathyFilterEvent *event;
61         gboolean            showing_event_icon;
62         guint               blink_timeout;
63         gpointer            token;
64
65         GtkWindow          *window;
66         GtkWidget          *popup_menu;
67         GtkWidget          *show_window_item;
68         GtkWidget          *message_item;
69         GtkWidget          *status_item;
70 };
71
72 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
73
74 static void
75 status_icon_set_visibility (EmpathyStatusIcon *icon,
76                             gboolean           visible,
77                             gboolean           store)
78 {
79         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
80
81         if (store) {
82                 empathy_conf_set_bool (empathy_conf_get (),
83                                        EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, !visible);
84         }
85
86         if (!visible) {
87                 empathy_window_iconify (priv->window, priv->icon);
88         } else {
89                 GList *accounts;
90
91                 empathy_window_present (GTK_WINDOW (priv->window), TRUE);
92         
93                 /* Show the accounts dialog if there is no enabled accounts */
94                 accounts = mc_accounts_list_by_enabled (TRUE);
95                 if (accounts) {
96                         mc_accounts_list_free (accounts);
97                 } else {
98                         empathy_debug (DEBUG_DOMAIN,
99                                       "No enabled account, Showing account dialog");
100                         empathy_accounts_dialog_show (GTK_WINDOW (priv->window));
101                 }
102         }
103 }
104
105 static void
106 status_icon_notify_visibility_cb (EmpathyConf *conf,
107                                   const gchar *key,
108                                   gpointer     user_data)
109 {
110         EmpathyStatusIcon *icon = user_data;
111         gboolean           hidden = FALSE;
112
113         if (empathy_conf_get_bool (conf, key, &hidden)) {
114                 status_icon_set_visibility (icon, !hidden, FALSE);
115         }
116 }
117
118 static void
119 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
120 {
121         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
122         gboolean               visible;
123
124         visible = gtk_window_is_active (priv->window);
125         status_icon_set_visibility (icon, !visible, TRUE);
126 }
127
128 static void
129 status_icon_update_tooltip (EmpathyStatusIcon *icon)
130 {
131         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
132         const gchar           *tooltip = NULL;
133
134         if (priv->event) {
135                 tooltip = priv->event->message;
136         }
137
138         if (!tooltip) {
139                 tooltip = empathy_idle_get_status (priv->idle);
140         }
141
142         gtk_status_icon_set_tooltip (priv->icon, tooltip);      
143 }
144
145 static void
146 status_icon_update_icon (EmpathyStatusIcon *icon)
147 {
148         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
149         const gchar           *icon_name;
150
151         if (priv->event && priv->showing_event_icon) {
152                 icon_name = priv->event->icon_name;
153         } else {
154                 McPresence state;
155
156                 state = empathy_idle_get_state (priv->idle);
157                 icon_name = empathy_icon_name_for_presence (state);
158         }
159
160         gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
161 }
162
163 static void
164 status_icon_idle_notify_cb (EmpathyStatusIcon *icon)
165 {
166         status_icon_update_icon (icon);
167         status_icon_update_tooltip (icon);
168 }
169
170 static gboolean
171 status_icon_delete_event_cb (GtkWidget         *widget,
172                              GdkEvent          *event,
173                              EmpathyStatusIcon *icon)
174 {
175         status_icon_set_visibility (icon, FALSE, TRUE);
176         return TRUE;
177 }
178
179 static void
180 status_icon_activate_cb (GtkStatusIcon     *status_icon,
181                          EmpathyStatusIcon *icon)
182 {
183         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
184
185         empathy_debug (DEBUG_DOMAIN, "Activated: %s",
186                        priv->event ? "event" : "toggle");
187
188         if (priv->event) {
189                 empathy_filter_activate_event (priv->filter, priv->event);
190                 priv->event = empathy_filter_get_top_event (priv->filter);
191                 status_icon_update_tooltip (icon);
192                 status_icon_update_icon (icon);
193
194                 if (!priv->event && priv->blink_timeout) {
195                         g_source_remove (priv->blink_timeout);
196                         priv->blink_timeout = 0;
197                 }
198         } else {
199                 status_icon_toggle_visibility (icon);
200         }
201 }
202
203 static void
204 status_icon_show_hide_window_cb (GtkWidget         *widget,
205                                  EmpathyStatusIcon *icon)
206 {
207         gboolean visible;
208
209         visible = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
210         status_icon_set_visibility (icon, visible, TRUE);
211 }
212
213 static void
214 status_icon_new_message_cb (GtkWidget         *widget,
215                             EmpathyStatusIcon *icon)
216 {
217         empathy_new_message_dialog_show (NULL);
218 }
219
220 static void
221 status_icon_quit_cb (GtkWidget         *window,
222                      EmpathyStatusIcon *icon)
223 {
224         gtk_main_quit ();
225 }
226
227 static void
228 status_icon_popup_menu_cb (GtkStatusIcon     *status_icon,
229                            guint              button,
230                            guint              activate_time,
231                            EmpathyStatusIcon *icon)
232 {
233         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
234         GtkWidget             *submenu;
235         gboolean               show;
236
237         show = empathy_window_get_is_visible (GTK_WINDOW (priv->window));
238
239         g_signal_handlers_block_by_func (priv->show_window_item,
240                                          status_icon_show_hide_window_cb,
241                                          icon);
242         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
243                                         show);
244         g_signal_handlers_unblock_by_func (priv->show_window_item,
245                                            status_icon_show_hide_window_cb,
246                                            icon);
247
248         submenu = empathy_presence_chooser_create_menu ();
249         gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
250                                    submenu);
251
252         gtk_menu_popup (GTK_MENU (priv->popup_menu),
253                         NULL, NULL,
254                         gtk_status_icon_position_menu,
255                         priv->icon,
256                         button,
257                         activate_time);
258 }
259
260 static void
261 status_icon_create_menu (EmpathyStatusIcon *icon)
262 {
263         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
264         GladeXML              *glade;
265         gchar                 *filename;
266
267         filename = empathy_file_lookup ("empathy-status-icon.glade", "src");
268         glade = empathy_glade_get_file (filename,
269                                        "tray_menu",
270                                        NULL,
271                                        "tray_menu", &priv->popup_menu,
272                                        "tray_show_list", &priv->show_window_item,
273                                        "tray_new_message", &priv->message_item,
274                                        "tray_status", &priv->status_item,
275                                        NULL);
276         g_free (filename);
277
278         empathy_glade_connect (glade,
279                               icon,
280                               "tray_show_list", "toggled", status_icon_show_hide_window_cb,
281                               "tray_new_message", "activate", status_icon_new_message_cb,
282                               "tray_quit", "activate", status_icon_quit_cb,
283                               NULL);
284
285         g_object_unref (glade);
286 }
287
288 static gboolean
289 status_icon_blink_timeout_cb (EmpathyStatusIcon *icon)
290 {
291         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
292
293         priv->showing_event_icon = !priv->showing_event_icon;
294         status_icon_update_icon (icon);
295
296         return TRUE;
297 }
298
299 static void
300 status_icon_top_event_notify_cb (EmpathyStatusIcon *icon)
301 {
302         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
303
304         priv->event = empathy_filter_get_top_event (priv->filter);
305         priv->showing_event_icon = priv->event != NULL;
306         status_icon_update_icon (icon);
307         status_icon_update_tooltip (icon);
308
309         if (!priv->blink_timeout) {
310                 priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
311                                                      (GSourceFunc) status_icon_blink_timeout_cb,
312                                                      icon);
313         }
314 }
315
316 static void
317 status_icon_finalize (GObject *object)
318 {
319         EmpathyStatusIconPriv *priv = GET_PRIV (object);
320
321         if (priv->blink_timeout) {
322                 g_source_remove (priv->blink_timeout);
323         }
324
325         empathy_disconnect_account_status_changed (priv->token);
326
327         g_object_unref (priv->icon);
328         g_object_unref (priv->idle);
329         g_object_unref (priv->filter);
330         g_object_unref (priv->mc);
331 }
332
333 static void
334 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
335 {
336         GObjectClass *object_class = G_OBJECT_CLASS (klass);
337
338         object_class->finalize = status_icon_finalize;
339
340         g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
341 }
342
343 static void
344 status_icon_status_changed_cb (MissionControl           *mc,
345                                TpConnectionStatus        status,
346                                McPresence                presence,
347                                TpConnectionStatusReason  reason,
348                                const gchar              *unique_name,
349                                EmpathyStatusIcon        *icon)
350 {
351         GList                 *accounts, *l;
352         guint                  connection_status;
353         EmpathyStatusIconPriv *priv;
354
355         priv = GET_PRIV (icon);
356
357         /* Check for a connected account */
358         accounts = mc_accounts_list_by_enabled (TRUE);
359         for (l = accounts; l; l = l->next) {
360                 connection_status = mission_control_get_connection_status (priv->mc,
361                                                                            l->data,
362                                                                            NULL);
363                 if (connection_status == 0)
364                         break;
365         }
366         mc_accounts_list_free (accounts);
367
368         gtk_widget_set_sensitive (priv->message_item, connection_status == 0);
369 }
370
371 static void
372 empathy_status_icon_init (EmpathyStatusIcon *icon)
373 {
374         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
375
376         priv->icon = gtk_status_icon_new ();
377         priv->mc = empathy_mission_control_new ();
378         priv->idle = empathy_idle_new ();
379         priv->filter = empathy_filter_new ();
380         priv->token = empathy_connect_to_account_status_changed (priv->mc,
381                         G_CALLBACK (status_icon_status_changed_cb),
382                         icon, NULL);
383
384         /* make icon listen and respond to MAIN_WINDOW_HIDDEN changes */
385         empathy_conf_notify_add (empathy_conf_get (),
386                                  EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
387                                  status_icon_notify_visibility_cb,
388                                  icon);
389
390         status_icon_create_menu (icon);
391         status_icon_idle_notify_cb (icon);
392
393         g_signal_connect_swapped (priv->idle, "notify",
394                                   G_CALLBACK (status_icon_idle_notify_cb),
395                                   icon);
396         g_signal_connect_swapped (priv->filter, "notify::top-event",
397                                   G_CALLBACK (status_icon_top_event_notify_cb),
398                                   icon);
399         g_signal_connect (priv->icon, "activate",
400                           G_CALLBACK (status_icon_activate_cb),
401                           icon);
402         g_signal_connect (priv->icon, "popup-menu",
403                           G_CALLBACK (status_icon_popup_menu_cb),
404                           icon);
405 }
406
407 EmpathyStatusIcon *
408 empathy_status_icon_new (GtkWindow *window)
409 {
410         EmpathyStatusIconPriv *priv;
411         EmpathyStatusIcon     *icon;
412         gboolean               should_hide;
413
414         g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
415
416         icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
417         priv = GET_PRIV (icon);
418
419         priv->window = g_object_ref (window);
420
421         g_signal_connect (priv->window, "delete-event",
422                           G_CALLBACK (status_icon_delete_event_cb),
423                           icon);
424
425         empathy_conf_get_bool (empathy_conf_get (),
426                               EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
427                               &should_hide);
428
429         if (gtk_window_is_active (priv->window) == should_hide) {
430                 status_icon_set_visibility (icon, !should_hide, FALSE);
431         }
432
433         return icon;
434 }
435