]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-status-icon.c
Use gtk-info for contact informaiton.
[empathy.git] / libempathy-gtk / empathy-status-icon.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <gtk/gtk.h>
28 #include <glade/glade.h>
29 #include <glib/gi18n.h>
30
31 #include <libmissioncontrol/mission-control.h>
32
33 #include <libempathy/empathy-contact-list.h>
34 #include <libempathy/empathy-contact-manager.h>
35 #include <libempathy/gossip-contact.h>
36 #include <libempathy/gossip-debug.h>
37 #include <libempathy/gossip-utils.h>
38 #include <libempathy/gossip-conf.h>
39 #include <libempathy/empathy-idle.h>
40
41 #include "empathy-status-icon.h"
42 #include "empathy-contact-dialogs.h"
43 #include "gossip-presence-chooser.h"
44 #include "gossip-preferences.h"
45 #include "gossip-ui-utils.h"
46 #include "gossip-accounts-dialog.h"
47
48
49 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
50                        EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
51
52 #define DEBUG_DOMAIN "StatusIcon"
53
54 /* Number of ms to wait when blinking */
55 #define BLINK_TIMEOUT 500
56
57 struct _EmpathyStatusIconPriv {
58         GtkStatusIcon         *icon;
59         EmpathyContactManager *manager;
60         EmpathyIdle           *idle;
61         GList                 *events;
62         guint                  blink_timeout;
63         gboolean               showing_state_icon;
64
65         GtkWindow             *window;
66
67         GtkWidget             *popup_menu;
68         GtkWidget             *show_window_item;
69         GtkWidget             *message_item;
70         GtkWidget             *status_item;
71 };
72
73 typedef struct _StatusIconEvent StatusIconEvent;
74
75 typedef void (*EventActivatedFunc) (StatusIconEvent *event);
76
77 struct _StatusIconEvent {
78         gchar              *icon_name;
79         gchar              *message;
80         EventActivatedFunc  func;
81         gpointer            user_data;
82 };
83
84
85 static void       empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
86 static void       empathy_status_icon_init        (EmpathyStatusIcon      *icon);
87 static void       status_icon_finalize            (GObject                *object);
88 static void       status_icon_idle_notify_cb      (EmpathyIdle            *idle,
89                                                    GParamSpec             *param,
90                                                    EmpathyStatusIcon      *icon);
91 static void       status_icon_update_tooltip      (EmpathyStatusIcon      *icon);
92 static void       status_icon_set_from_state      (EmpathyStatusIcon      *icon);
93 static void       status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
94 static void       status_icon_activate_cb         (GtkStatusIcon          *status_icon,
95                                                    EmpathyStatusIcon      *icon);
96 static gboolean   status_icon_delete_event_cb     (GtkWidget              *widget,
97                                                    GdkEvent               *event,
98                                                    EmpathyStatusIcon      *icon);
99 static void       status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
100                                                    guint                   button,
101                                                    guint                   activate_time,
102                                                    EmpathyStatusIcon      *icon);
103 static void       status_icon_create_menu         (EmpathyStatusIcon      *icon);
104 static void       status_icon_new_message_cb      (GtkWidget              *widget,
105                                                    EmpathyStatusIcon      *icon);
106 static void       status_icon_quit_cb             (GtkWidget              *window,
107                                                    EmpathyStatusIcon      *icon);
108 static void       status_icon_show_hide_window_cb (GtkWidget              *widget,
109                                                    EmpathyStatusIcon      *icon);
110 static void       status_icon_local_pending_cb    (EmpathyContactManager  *manager,
111                                                    GossipContact          *contact,
112                                                    gchar                  *message,
113                                                    EmpathyStatusIcon      *icon);
114 static void       status_icon_event_subscribe_cb  (StatusIconEvent        *event);
115 static StatusIconEvent * status_icon_event_new    (EmpathyStatusIcon      *icon,
116                                                    const gchar            *icon_name,
117                                                    const gchar            *message);
118 static void       status_icon_event_remove        (EmpathyStatusIcon      *icon,
119                                                    StatusIconEvent        *event);
120 static gboolean   status_icon_event_timeout_cb    (EmpathyStatusIcon      *icon);
121 static void       status_icon_event_free          (StatusIconEvent        *event);
122
123 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
124
125 static void
126 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
127 {
128         GObjectClass *object_class = G_OBJECT_CLASS (klass);
129
130         object_class->finalize = status_icon_finalize;
131
132         g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
133 }
134
135 static void
136 empathy_status_icon_init (EmpathyStatusIcon *icon)
137 {
138         EmpathyStatusIconPriv *priv;
139         GList                 *pending, *l;
140
141         priv = GET_PRIV (icon);
142
143         priv->icon = gtk_status_icon_new ();
144         priv->idle = empathy_idle_new ();
145         priv->manager = empathy_contact_manager_new ();
146         priv->showing_state_icon = TRUE;
147
148         status_icon_create_menu (icon);
149         status_icon_set_from_state (icon);
150         status_icon_update_tooltip (icon);
151
152         g_signal_connect (priv->idle, "notify",
153                           G_CALLBACK (status_icon_idle_notify_cb),
154                           icon);
155         g_signal_connect (priv->icon, "activate",
156                           G_CALLBACK (status_icon_activate_cb),
157                           icon);
158         g_signal_connect (priv->icon, "popup-menu",
159                           G_CALLBACK (status_icon_popup_menu_cb),
160                           icon);
161         g_signal_connect (priv->manager, "local-pending",
162                           G_CALLBACK (status_icon_local_pending_cb),
163                           icon);
164
165         pending = empathy_contact_list_get_local_pending (EMPATHY_CONTACT_LIST (priv->manager));
166         for (l = pending; l; l = l->next) {
167                 EmpathyContactListInfo *info;
168
169                 info = l->data;
170                 status_icon_local_pending_cb (priv->manager,
171                                               info->contact,
172                                               info->message,
173                                               icon);
174         }
175         g_list_free (pending);
176 }
177
178 static void
179 status_icon_finalize (GObject *object)
180 {
181         EmpathyStatusIconPriv *priv;
182
183         priv = GET_PRIV (object);
184
185         g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL);
186         g_list_free (priv->events);
187
188         if (priv->blink_timeout) {
189                 g_source_remove (priv->blink_timeout);
190         }
191
192         g_object_unref (priv->icon);
193         g_object_unref (priv->window);
194         g_object_unref (priv->idle);
195         g_object_unref (priv->manager);
196 }
197
198 EmpathyStatusIcon *
199 empathy_status_icon_new (GtkWindow *window)
200 {
201         EmpathyStatusIconPriv *priv;
202         EmpathyStatusIcon     *icon;
203         gboolean               should_hide;
204         gboolean               visible;
205
206         g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
207
208         icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
209         priv = GET_PRIV (icon);
210
211         priv->window = g_object_ref (window);
212
213         g_signal_connect (priv->window, "delete-event",
214                           G_CALLBACK (status_icon_delete_event_cb),
215                           icon);
216
217         gossip_conf_get_bool (gossip_conf_get (),
218                               GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN,
219                               &should_hide);
220         visible = gossip_window_get_is_visible (window);
221
222         if ((!should_hide && !visible) || (should_hide && visible)) {
223                 status_icon_toggle_visibility (icon);
224         }
225
226         return icon;
227 }
228
229 static void
230 status_icon_idle_notify_cb (EmpathyIdle       *idle,
231                             GParamSpec        *param,
232                             EmpathyStatusIcon *icon)
233 {
234         EmpathyStatusIconPriv *priv;
235
236         priv = GET_PRIV (icon);
237
238         if (priv->showing_state_icon) {
239                 status_icon_set_from_state (icon);
240         }
241
242         status_icon_update_tooltip (icon);
243 }
244
245 static void
246 status_icon_update_tooltip (EmpathyStatusIcon *icon)
247 {
248         EmpathyStatusIconPriv *priv;
249         const gchar           *tooltip = NULL;
250
251         priv = GET_PRIV (icon);
252
253         if (priv->events) {
254                 StatusIconEvent *event;
255
256                 event = priv->events->data;
257                 tooltip = event->message;
258         }
259
260         if (!tooltip) {
261                 tooltip = empathy_idle_get_status (priv->idle);
262         }
263
264         gtk_status_icon_set_tooltip (priv->icon, tooltip);      
265 }
266
267 static void
268 status_icon_set_from_state (EmpathyStatusIcon *icon)
269 {
270         EmpathyStatusIconPriv *priv;
271         McPresence             state;
272         const gchar           *icon_name;
273
274         priv = GET_PRIV (icon);
275
276         state = empathy_idle_get_state (priv->idle);
277         icon_name = gossip_icon_name_for_presence_state (state);
278         gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
279 }
280
281 static void
282 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
283 {
284         EmpathyStatusIconPriv *priv;
285         gboolean               visible;
286
287         priv = GET_PRIV (icon);
288
289         visible = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
290
291         if (visible) {
292                 gtk_widget_hide (GTK_WIDGET (priv->window));
293                 gossip_conf_set_bool (gossip_conf_get (),
294                                       GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, TRUE);
295         } else {
296                 GList *accounts;
297
298                 gossip_window_present (GTK_WINDOW (priv->window), TRUE);
299                 gossip_conf_set_bool (gossip_conf_get (),
300                                       GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, FALSE);
301         
302                 /* Show the accounts dialog if there is no enabled accounts */
303                 accounts = mc_accounts_list_by_enabled (TRUE);
304                 if (accounts) {
305                         mc_accounts_list_free (accounts);
306                 } else {
307                         gossip_debug (DEBUG_DOMAIN,
308                                       "No enabled account, Showing account dialog");
309                         gossip_accounts_dialog_show (GTK_WINDOW (priv->window));
310                 }
311         }
312 }
313
314 static void
315 status_icon_activate_cb (GtkStatusIcon     *status_icon,
316                          EmpathyStatusIcon *icon)
317 {
318         EmpathyStatusIconPriv *priv;
319
320         priv = GET_PRIV (icon);
321
322         if (priv->events) {
323                 status_icon_event_remove (icon, priv->events->data);
324         } else {
325                 status_icon_toggle_visibility (icon);
326         }
327 }
328
329 static gboolean
330 status_icon_delete_event_cb (GtkWidget         *widget,
331                              GdkEvent          *event,
332                              EmpathyStatusIcon *icon)
333 {
334         status_icon_toggle_visibility (icon);
335
336         return TRUE;
337 }
338
339 static void
340 status_icon_popup_menu_cb (GtkStatusIcon     *status_icon,
341                            guint              button,
342                            guint              activate_time,
343                            EmpathyStatusIcon *icon)
344 {
345         EmpathyStatusIconPriv *priv;
346         GtkWidget             *submenu;
347         gboolean               show;
348
349         priv = GET_PRIV (icon);
350
351         show = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
352
353         g_signal_handlers_block_by_func (priv->show_window_item,
354                                          status_icon_show_hide_window_cb,
355                                          icon);
356         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
357                                         show);
358         g_signal_handlers_unblock_by_func (priv->show_window_item,
359                                            status_icon_show_hide_window_cb,
360                                            icon);
361
362         submenu = gossip_presence_chooser_create_menu ();
363         gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
364                                    submenu);
365
366         gtk_menu_popup (GTK_MENU (priv->popup_menu),
367                         NULL, NULL,
368                         gtk_status_icon_position_menu,
369                         priv->icon,
370                         button,
371                         activate_time);
372 }
373
374 static void
375 status_icon_create_menu (EmpathyStatusIcon *icon)
376 {
377         EmpathyStatusIconPriv *priv;
378         GladeXML              *glade;
379
380         priv = GET_PRIV (icon);
381
382         glade = gossip_glade_get_file ("empathy-status-icon.glade",
383                                        "tray_menu",
384                                        NULL,
385                                        "tray_menu", &priv->popup_menu,
386                                        "tray_show_list", &priv->show_window_item,
387                                        "tray_new_message", &priv->message_item,
388                                        "tray_status", &priv->status_item,
389                                        NULL);
390
391         gossip_glade_connect (glade,
392                               icon,
393                               "tray_new_message", "activate", status_icon_new_message_cb,
394                               "tray_quit", "activate", status_icon_quit_cb,
395                               NULL);
396
397         g_signal_connect (priv->show_window_item, "toggled",
398                           G_CALLBACK (status_icon_show_hide_window_cb),
399                           icon);
400
401         g_object_unref (glade);
402 }
403
404 static void
405 status_icon_new_message_cb (GtkWidget         *widget,
406                             EmpathyStatusIcon *icon)
407 {
408         EmpathyStatusIconPriv *priv;
409
410         priv = GET_PRIV (icon);
411
412         //gossip_new_message_dialog_show (GTK_WINDOW (priv->window));
413 }
414
415 static void
416 status_icon_quit_cb (GtkWidget         *window,
417                      EmpathyStatusIcon *icon)
418 {
419         gtk_main_quit ();
420 }
421
422 static void
423 status_icon_show_hide_window_cb (GtkWidget         *widget,
424                                  EmpathyStatusIcon *icon)
425 {
426         status_icon_toggle_visibility (icon);
427 }
428
429 static void
430 status_icon_local_pending_cb (EmpathyContactManager *manager,
431                               GossipContact         *contact,
432                               gchar                 *message,
433                               EmpathyStatusIcon     *icon)
434 {
435         EmpathyStatusIconPriv *priv;
436         StatusIconEvent       *event;
437         gchar                 *str;
438         GList                 *l;
439
440         priv = GET_PRIV (icon);
441
442         for (l = priv->events; l; l = l->next) {
443                 if (gossip_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) {
444                         return;
445                 }
446         }
447
448         str = g_strdup_printf (_("Subscription requested for %s\n"
449                                  "Message: %s"),
450                                gossip_contact_get_name (contact),
451                                message);
452
453         event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str);
454         event->user_data = g_object_ref (contact);
455         event->func = status_icon_event_subscribe_cb;
456
457         g_free (str);
458 }
459
460 static void
461 status_icon_event_subscribe_cb (StatusIconEvent *event)
462 {
463         GossipContact *contact;
464
465         contact = GOSSIP_CONTACT (event->user_data);
466
467         empathy_subscription_dialog_show (contact, NULL);
468
469         g_object_unref (contact);
470 }
471
472 static StatusIconEvent *
473 status_icon_event_new (EmpathyStatusIcon *icon,
474                        const gchar       *icon_name,
475                        const gchar       *message)
476 {
477         EmpathyStatusIconPriv *priv;
478         StatusIconEvent       *event;
479
480         priv = GET_PRIV (icon);
481
482         event = g_slice_new0 (StatusIconEvent);
483         event->icon_name = g_strdup (icon_name);        
484         event->message = g_strdup (message);
485
486         priv->events = g_list_append (priv->events, event);
487         if (!priv->blink_timeout) {
488                 priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT,
489                                                      (GSourceFunc) status_icon_event_timeout_cb,
490                                                      icon);
491                 status_icon_event_timeout_cb (icon);
492         }
493
494         return event;
495 }
496
497 static void
498 status_icon_event_remove (EmpathyStatusIcon *icon,
499                           StatusIconEvent   *event)
500 {
501         EmpathyStatusIconPriv *priv;
502
503         priv = GET_PRIV (icon);
504
505         if (event->func) {
506                 event->func (event);
507         }
508         priv->events = g_list_remove (priv->events, event);
509         status_icon_event_free (event);
510         status_icon_update_tooltip (icon);
511
512         if (priv->events) {
513                 return;
514         }
515
516         status_icon_set_from_state (icon);
517         priv->showing_state_icon = TRUE;
518
519         if (priv->blink_timeout) {
520                 g_source_remove (priv->blink_timeout);
521                 priv->blink_timeout = 0;
522
523         }
524 }
525
526 static gboolean
527 status_icon_event_timeout_cb (EmpathyStatusIcon *icon)
528 {
529         EmpathyStatusIconPriv *priv;
530
531         priv = GET_PRIV (icon);
532
533         priv->showing_state_icon = !priv->showing_state_icon;
534
535         if (priv->showing_state_icon) {
536                 status_icon_set_from_state (icon);
537         } else {
538                 StatusIconEvent *event;
539
540                 event = priv->events->data;
541                 gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name);
542         }
543         status_icon_update_tooltip (icon);
544
545         return TRUE;
546 }
547
548 static void
549 status_icon_event_free (StatusIconEvent *event)
550 {
551         g_free (event->icon_name);
552         g_free (event->message);
553         g_slice_free (StatusIconEvent, event);
554 }
555