]> git.0d.be Git - empathy.git/blob - src/empathy-status-icon.c
Make use of tp-glib debug system.
[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-utils.h>
31 #include <libempathy/empathy-idle.h>
32
33 #include <libempathy-gtk/empathy-presence-chooser.h>
34 #include <libempathy-gtk/empathy-conf.h>
35 #include <libempathy-gtk/empathy-ui-utils.h>
36 #include <libempathy-gtk/empathy-accounts-dialog.h>
37 #include <libempathy-gtk/empathy-images.h>
38 #include <libempathy-gtk/empathy-new-message-dialog.h>
39
40 #include "empathy-status-icon.h"
41 #include "empathy-preferences.h"
42 #include "empathy-filter.h"
43
44 #define DEBUG_FLAG EMPATHY_DEBUG_FILTER
45 #include <libempathy/empathy-debug.h>
46
47 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
48                        EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
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                         DEBUG ("No enabled account, Showing account dialog");
99                         empathy_accounts_dialog_show (GTK_WINDOW (priv->window));
100                 }
101         }
102 }
103
104 static void
105 status_icon_notify_visibility_cb (EmpathyConf *conf,
106                                   const gchar *key,
107                                   gpointer     user_data)
108 {
109         EmpathyStatusIcon *icon = user_data;
110         gboolean           hidden = FALSE;
111
112         if (empathy_conf_get_bool (conf, key, &hidden)) {
113                 status_icon_set_visibility (icon, !hidden, FALSE);
114         }
115 }
116
117 static void
118 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
119 {
120         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
121         gboolean               visible;
122
123         visible = gtk_window_is_active (priv->window);
124         status_icon_set_visibility (icon, !visible, TRUE);
125 }
126
127 static void
128 status_icon_update_tooltip (EmpathyStatusIcon *icon)
129 {
130         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
131         const gchar           *tooltip = NULL;
132
133         if (priv->event) {
134                 tooltip = priv->event->message;
135         }
136
137         if (!tooltip) {
138                 tooltip = empathy_idle_get_status (priv->idle);
139         }
140
141         gtk_status_icon_set_tooltip (priv->icon, tooltip);      
142 }
143
144 static void
145 status_icon_update_icon (EmpathyStatusIcon *icon)
146 {
147         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
148         const gchar           *icon_name;
149
150         if (priv->event && priv->showing_event_icon) {
151                 icon_name = priv->event->icon_name;
152         } else {
153                 McPresence state;
154
155                 state = empathy_idle_get_state (priv->idle);
156                 icon_name = empathy_icon_name_for_presence (state);
157         }
158
159         gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
160 }
161
162 static void
163 status_icon_idle_notify_cb (EmpathyStatusIcon *icon)
164 {
165         status_icon_update_icon (icon);
166         status_icon_update_tooltip (icon);
167 }
168
169 static gboolean
170 status_icon_delete_event_cb (GtkWidget         *widget,
171                              GdkEvent          *event,
172                              EmpathyStatusIcon *icon)
173 {
174         status_icon_set_visibility (icon, FALSE, TRUE);
175         return TRUE;
176 }
177
178 static void
179 status_icon_activate_cb (GtkStatusIcon     *status_icon,
180                          EmpathyStatusIcon *icon)
181 {
182         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
183
184         DEBUG ("Activated: %s", priv->event ? "event" : "toggle");
185
186         if (priv->event) {
187                 empathy_filter_activate_event (priv->filter, priv->event);
188                 priv->event = empathy_filter_get_top_event (priv->filter);
189                 status_icon_update_tooltip (icon);
190                 status_icon_update_icon (icon);
191
192                 if (!priv->event && priv->blink_timeout) {
193                         g_source_remove (priv->blink_timeout);
194                         priv->blink_timeout = 0;
195                 }
196         } else {
197                 status_icon_toggle_visibility (icon);
198         }
199 }
200
201 static void
202 status_icon_show_hide_window_cb (GtkWidget         *widget,
203                                  EmpathyStatusIcon *icon)
204 {
205         gboolean visible;
206
207         visible = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
208         status_icon_set_visibility (icon, visible, TRUE);
209 }
210
211 static void
212 status_icon_new_message_cb (GtkWidget         *widget,
213                             EmpathyStatusIcon *icon)
214 {
215         empathy_new_message_dialog_show (NULL);
216 }
217
218 static void
219 status_icon_quit_cb (GtkWidget         *window,
220                      EmpathyStatusIcon *icon)
221 {
222         gtk_main_quit ();
223 }
224
225 static void
226 status_icon_popup_menu_cb (GtkStatusIcon     *status_icon,
227                            guint              button,
228                            guint              activate_time,
229                            EmpathyStatusIcon *icon)
230 {
231         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
232         GtkWidget             *submenu;
233         gboolean               show;
234
235         show = empathy_window_get_is_visible (GTK_WINDOW (priv->window));
236
237         g_signal_handlers_block_by_func (priv->show_window_item,
238                                          status_icon_show_hide_window_cb,
239                                          icon);
240         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
241                                         show);
242         g_signal_handlers_unblock_by_func (priv->show_window_item,
243                                            status_icon_show_hide_window_cb,
244                                            icon);
245
246         submenu = empathy_presence_chooser_create_menu ();
247         gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
248                                    submenu);
249
250         gtk_menu_popup (GTK_MENU (priv->popup_menu),
251                         NULL, NULL,
252                         gtk_status_icon_position_menu,
253                         priv->icon,
254                         button,
255                         activate_time);
256 }
257
258 static void
259 status_icon_create_menu (EmpathyStatusIcon *icon)
260 {
261         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
262         GladeXML              *glade;
263         gchar                 *filename;
264
265         filename = empathy_file_lookup ("empathy-status-icon.glade", "src");
266         glade = empathy_glade_get_file (filename,
267                                        "tray_menu",
268                                        NULL,
269                                        "tray_menu", &priv->popup_menu,
270                                        "tray_show_list", &priv->show_window_item,
271                                        "tray_new_message", &priv->message_item,
272                                        "tray_status", &priv->status_item,
273                                        NULL);
274         g_free (filename);
275
276         empathy_glade_connect (glade,
277                               icon,
278                               "tray_show_list", "toggled", status_icon_show_hide_window_cb,
279                               "tray_new_message", "activate", status_icon_new_message_cb,
280                               "tray_quit", "activate", status_icon_quit_cb,
281                               NULL);
282
283         g_object_unref (glade);
284 }
285
286 static gboolean
287 status_icon_blink_timeout_cb (EmpathyStatusIcon *icon)
288 {
289         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
290
291         priv->showing_event_icon = !priv->showing_event_icon;
292         status_icon_update_icon (icon);
293
294         return TRUE;
295 }
296
297 static void
298 status_icon_top_event_notify_cb (EmpathyStatusIcon *icon)
299 {
300         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
301
302         priv->event = empathy_filter_get_top_event (priv->filter);
303         priv->showing_event_icon = priv->event != NULL;
304         status_icon_update_icon (icon);
305         status_icon_update_tooltip (icon);
306
307         if (!priv->blink_timeout) {
308                 priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
309                                                      (GSourceFunc) status_icon_blink_timeout_cb,
310                                                      icon);
311         }
312 }
313
314 static void
315 status_icon_finalize (GObject *object)
316 {
317         EmpathyStatusIconPriv *priv = GET_PRIV (object);
318
319         if (priv->blink_timeout) {
320                 g_source_remove (priv->blink_timeout);
321         }
322
323         empathy_disconnect_account_status_changed (priv->token);
324
325         g_object_unref (priv->icon);
326         g_object_unref (priv->idle);
327         g_object_unref (priv->filter);
328         g_object_unref (priv->mc);
329 }
330
331 static void
332 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
333 {
334         GObjectClass *object_class = G_OBJECT_CLASS (klass);
335
336         object_class->finalize = status_icon_finalize;
337
338         g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
339 }
340
341 static void
342 status_icon_status_changed_cb (MissionControl           *mc,
343                                TpConnectionStatus        status,
344                                McPresence                presence,
345                                TpConnectionStatusReason  reason,
346                                const gchar              *unique_name,
347                                EmpathyStatusIcon        *icon)
348 {
349         GList                 *accounts, *l;
350         guint                  connection_status = 1;
351         EmpathyStatusIconPriv *priv;
352
353         priv = GET_PRIV (icon);
354
355         /* Check for a connected account */
356         accounts = mc_accounts_list_by_enabled (TRUE);
357         for (l = accounts; l; l = l->next) {
358                 connection_status = mission_control_get_connection_status (priv->mc,
359                                                                            l->data,
360                                                                            NULL);
361                 if (connection_status == 0)
362                         break;
363         }
364         mc_accounts_list_free (accounts);
365
366         gtk_widget_set_sensitive (priv->message_item, connection_status == 0);
367 }
368
369 static void
370 empathy_status_icon_init (EmpathyStatusIcon *icon)
371 {
372         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
373
374         priv->icon = gtk_status_icon_new ();
375         priv->mc = empathy_mission_control_new ();
376         priv->idle = empathy_idle_new ();
377         priv->filter = empathy_filter_new ();
378         priv->token = empathy_connect_to_account_status_changed (priv->mc,
379                         G_CALLBACK (status_icon_status_changed_cb),
380                         icon, NULL);
381
382         /* make icon listen and respond to MAIN_WINDOW_HIDDEN changes */
383         empathy_conf_notify_add (empathy_conf_get (),
384                                  EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
385                                  status_icon_notify_visibility_cb,
386                                  icon);
387
388         status_icon_create_menu (icon);
389         status_icon_idle_notify_cb (icon);
390
391         g_signal_connect_swapped (priv->idle, "notify",
392                                   G_CALLBACK (status_icon_idle_notify_cb),
393                                   icon);
394         g_signal_connect_swapped (priv->filter, "notify::top-event",
395                                   G_CALLBACK (status_icon_top_event_notify_cb),
396                                   icon);
397         g_signal_connect (priv->icon, "activate",
398                           G_CALLBACK (status_icon_activate_cb),
399                           icon);
400         g_signal_connect (priv->icon, "popup-menu",
401                           G_CALLBACK (status_icon_popup_menu_cb),
402                           icon);
403 }
404
405 EmpathyStatusIcon *
406 empathy_status_icon_new (GtkWindow *window)
407 {
408         EmpathyStatusIconPriv *priv;
409         EmpathyStatusIcon     *icon;
410         gboolean               should_hide;
411
412         g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
413
414         icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
415         priv = GET_PRIV (icon);
416
417         priv->window = g_object_ref (window);
418
419         g_signal_connect (priv->window, "delete-event",
420                           G_CALLBACK (status_icon_delete_event_cb),
421                           icon);
422
423         empathy_conf_get_bool (empathy_conf_get (),
424                               EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
425                               &should_hide);
426
427         if (gtk_window_is_active (priv->window) == should_hide) {
428                 status_icon_set_visibility (icon, !should_hide, FALSE);
429         }
430
431         return icon;
432 }
433