]> git.0d.be Git - empathy.git/blob - src/empathy-status-icon.c
Drop Chandler and Filter, do not use MC for dispatching channels, do it ourself.
[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         EmpathyFilter      *filter;
59         EmpathyFilterEvent *event;
60         gboolean            showing_event_icon;
61         guint               blink_timeout;
62
63         GtkWindow          *window;
64         GtkWidget          *popup_menu;
65         GtkWidget          *show_window_item;
66         GtkWidget          *message_item;
67         GtkWidget          *status_item;
68 };
69
70 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
71
72 static void
73 status_icon_set_visibility (EmpathyStatusIcon *icon,
74                             gboolean           visible,
75                             gboolean           store)
76 {
77         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
78
79         if (store) {
80                 empathy_conf_set_bool (empathy_conf_get (),
81                                        EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, !visible);
82         }
83
84         if (!visible) {
85                 empathy_window_iconify (priv->window, priv->icon);
86         } else {
87                 GList *accounts;
88
89                 empathy_window_present (GTK_WINDOW (priv->window), TRUE);
90         
91                 /* Show the accounts dialog if there is no enabled accounts */
92                 accounts = mc_accounts_list_by_enabled (TRUE);
93                 if (accounts) {
94                         mc_accounts_list_free (accounts);
95                 } else {
96                         empathy_debug (DEBUG_DOMAIN,
97                                       "No enabled account, Showing account dialog");
98                         empathy_accounts_dialog_show (GTK_WINDOW (priv->window));
99                 }
100         }
101 }
102
103 static void
104 status_icon_notify_visibility_cb (EmpathyConf *conf,
105                                   const gchar *key,
106                                   gpointer     user_data)
107 {
108         EmpathyStatusIcon *icon = user_data;
109         gboolean           hidden = FALSE;
110
111         if (empathy_conf_get_bool (conf, key, &hidden)) {
112                 status_icon_set_visibility (icon, !hidden, FALSE);
113         }
114 }
115
116 static void
117 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
118 {
119         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
120         gboolean               visible;
121
122         visible = gtk_window_is_active (priv->window);
123         status_icon_set_visibility (icon, !visible, TRUE);
124 }
125
126 static void
127 status_icon_update_tooltip (EmpathyStatusIcon *icon)
128 {
129         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
130         const gchar           *tooltip = NULL;
131
132         if (priv->event) {
133                 tooltip = priv->event->message;
134         }
135
136         if (!tooltip) {
137                 tooltip = empathy_idle_get_status (priv->idle);
138         }
139
140         gtk_status_icon_set_tooltip (priv->icon, tooltip);      
141 }
142
143 static void
144 status_icon_update_icon (EmpathyStatusIcon *icon)
145 {
146         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
147         const gchar           *icon_name;
148
149         if (priv->event && priv->showing_event_icon) {
150                 icon_name = priv->event->icon_name;
151         } else {
152                 McPresence state;
153
154                 state = empathy_idle_get_state (priv->idle);
155                 icon_name = empathy_icon_name_for_presence (state);
156         }
157
158         gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
159 }
160
161 static void
162 status_icon_idle_notify_cb (EmpathyStatusIcon *icon)
163 {
164         status_icon_update_icon (icon);
165         status_icon_update_tooltip (icon);
166 }
167
168 static gboolean
169 status_icon_delete_event_cb (GtkWidget         *widget,
170                              GdkEvent          *event,
171                              EmpathyStatusIcon *icon)
172 {
173         status_icon_set_visibility (icon, FALSE, TRUE);
174         return TRUE;
175 }
176
177 static void
178 status_icon_activate_cb (GtkStatusIcon     *status_icon,
179                          EmpathyStatusIcon *icon)
180 {
181         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
182
183         empathy_debug (DEBUG_DOMAIN, "Activated: %s",
184                        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         g_object_unref (priv->icon);
324         g_object_unref (priv->idle);
325         g_object_unref (priv->filter);
326 }
327
328 static void
329 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
330 {
331         GObjectClass *object_class = G_OBJECT_CLASS (klass);
332
333         object_class->finalize = status_icon_finalize;
334
335         g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
336 }
337
338 static void
339 empathy_status_icon_init (EmpathyStatusIcon *icon)
340 {
341         EmpathyStatusIconPriv *priv = GET_PRIV (icon);
342
343         priv->icon = gtk_status_icon_new ();
344         priv->idle = empathy_idle_new ();
345         priv->filter = empathy_filter_new ();
346
347         /* make icon listen and respond to MAIN_WINDOW_HIDDEN changes */
348         empathy_conf_notify_add (empathy_conf_get (),
349                                  EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
350                                  status_icon_notify_visibility_cb,
351                                  icon);
352
353         status_icon_create_menu (icon);
354         status_icon_idle_notify_cb (icon);
355
356         g_signal_connect_swapped (priv->idle, "notify",
357                                   G_CALLBACK (status_icon_idle_notify_cb),
358                                   icon);
359         g_signal_connect_swapped (priv->filter, "notify::top-event",
360                                   G_CALLBACK (status_icon_top_event_notify_cb),
361                                   icon);
362         g_signal_connect (priv->icon, "activate",
363                           G_CALLBACK (status_icon_activate_cb),
364                           icon);
365         g_signal_connect (priv->icon, "popup-menu",
366                           G_CALLBACK (status_icon_popup_menu_cb),
367                           icon);
368 }
369
370 EmpathyStatusIcon *
371 empathy_status_icon_new (GtkWindow *window)
372 {
373         EmpathyStatusIconPriv *priv;
374         EmpathyStatusIcon     *icon;
375         gboolean               should_hide;
376
377         g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
378
379         icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
380         priv = GET_PRIV (icon);
381
382         priv->window = g_object_ref (window);
383
384         g_signal_connect (priv->window, "delete-event",
385                           G_CALLBACK (status_icon_delete_event_cb),
386                           icon);
387
388         empathy_conf_get_bool (empathy_conf_get (),
389                               EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN,
390                               &should_hide);
391
392         if (gtk_window_is_active (priv->window) == should_hide) {
393                 status_icon_set_visibility (icon, !should_hide, FALSE);
394         }
395
396         return icon;
397 }
398