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