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