01a5424938db60d036a1105318502cae8885e6cd
[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
30 #include <libmissioncontrol/mission-control.h>
31
32 #include <libempathy/gossip-debug.h>
33 #include <libempathy/gossip-utils.h>
34 #include <libempathy/gossip-conf.h>
35 #include <libempathy/empathy-idle.h>
36
37 #include "empathy-status-icon.h"
38 #include "gossip-presence-chooser.h"
39 #include "gossip-preferences.h"
40 #include "gossip-ui-utils.h"
41 #include "gossip-accounts-dialog.h"
42
43
44 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
45                        EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv))
46
47 #define DEBUG_DOMAIN "StatusIcon"
48
49 struct _EmpathyStatusIconPriv {
50         MissionControl *mc;
51         GtkStatusIcon  *icon;
52         EmpathyIdle    *idle;
53
54         GtkWindow      *window;
55
56         GtkWidget      *popup_menu;
57         GtkWidget      *show_window_item;
58         GtkWidget      *message_item;
59         GtkWidget      *status_item;
60 };
61
62 static void     empathy_status_icon_class_init  (EmpathyStatusIconClass *klass);
63 static void     empathy_status_icon_init        (EmpathyStatusIcon      *icon);
64 static void     status_icon_finalize            (GObject                *object);
65 static void     status_icon_presence_changed_cb (MissionControl         *mc,
66                                                  McPresence              state,
67                                                  EmpathyStatusIcon      *icon);
68 static void     status_icon_toggle_visibility   (EmpathyStatusIcon      *icon);
69 static void     status_icon_activate_cb         (GtkStatusIcon          *status_icon,
70                                                  EmpathyStatusIcon      *icon);
71 static gboolean status_icon_delete_event_cb     (GtkWidget              *widget,
72                                                  GdkEvent               *event,
73                                                  EmpathyStatusIcon      *icon);
74 static void     status_icon_popup_menu_cb       (GtkStatusIcon          *status_icon,
75                                                  guint                   button,
76                                                  guint                   activate_time,
77                                                  EmpathyStatusIcon      *icon);
78 static void     status_icon_create_menu         (EmpathyStatusIcon      *icon);
79 static void     status_icon_new_message_cb      (GtkWidget              *widget,
80                                                  EmpathyStatusIcon      *icon);
81 static void     status_icon_quit_cb             (GtkWidget              *window,
82                                                  EmpathyStatusIcon      *icon);
83 static void     status_icon_show_hide_window_cb (GtkWidget              *widget,
84                                                  EmpathyStatusIcon      *icon);
85
86 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
87
88 static void
89 empathy_status_icon_class_init (EmpathyStatusIconClass *klass)
90 {
91         GObjectClass *object_class = G_OBJECT_CLASS (klass);
92
93         object_class->finalize = status_icon_finalize;
94
95         g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv));
96 }
97
98 static void
99 empathy_status_icon_init (EmpathyStatusIcon *icon)
100 {
101         EmpathyStatusIconPriv *priv;
102         McPresence             state;
103
104         priv = GET_PRIV (icon);
105
106         priv->icon = gtk_status_icon_new ();
107         priv->mc = gossip_mission_control_new ();
108         priv->idle = empathy_idle_new ();
109
110         status_icon_create_menu (icon);
111
112         state = mission_control_get_presence_actual (priv->mc, NULL);
113         status_icon_presence_changed_cb (priv->mc, state, icon);
114         
115         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
116                                      "PresenceStatusActual",
117                                      G_CALLBACK (status_icon_presence_changed_cb),
118                                      icon, NULL);
119         g_signal_connect (priv->icon, "activate",
120                           G_CALLBACK (status_icon_activate_cb),
121                           icon);
122         g_signal_connect (priv->icon, "popup-menu",
123                           G_CALLBACK (status_icon_popup_menu_cb),
124                           icon);
125 }
126
127 static void
128 status_icon_finalize (GObject *object)
129 {
130         EmpathyStatusIconPriv *priv;
131
132         priv = GET_PRIV (object);
133
134         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
135                                         "PresenceStatusActual",
136                                         G_CALLBACK (status_icon_presence_changed_cb),
137                                         object);
138
139         g_object_unref (priv->mc);
140         g_object_unref (priv->icon);
141         g_object_unref (priv->window);
142         g_object_unref (priv->idle);
143 }
144
145 EmpathyStatusIcon *
146 empathy_status_icon_new (GtkWindow *window)
147 {
148         EmpathyStatusIconPriv *priv;
149         EmpathyStatusIcon     *icon;
150         gboolean               should_hide;
151         gboolean               visible;
152
153         g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
154
155         icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL);
156         priv = GET_PRIV (icon);
157
158         priv->window = g_object_ref (window);
159
160         g_signal_connect (priv->window, "delete-event",
161                           G_CALLBACK (status_icon_delete_event_cb),
162                           icon);
163
164         gossip_conf_get_bool (gossip_conf_get (),
165                               GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN,
166                               &should_hide);
167         visible = gossip_window_get_is_visible (window);
168
169         if ((!should_hide && !visible) || (should_hide && visible)) {
170                 status_icon_toggle_visibility (icon);
171         }
172
173         return icon;
174 }
175
176 static void
177 status_icon_presence_changed_cb (MissionControl    *mc,
178                                  McPresence         state,
179                                  EmpathyStatusIcon *icon)
180 {
181         EmpathyStatusIconPriv *priv;
182         const gchar           *icon_name;
183         gchar                 *status;
184
185         priv = GET_PRIV (icon);
186
187         icon_name = gossip_icon_name_for_presence_state (state);
188         status = mission_control_get_presence_message_actual (priv->mc, NULL);
189         if (G_STR_EMPTY (status)) {
190                 g_free (status);
191                 status = g_strdup (gossip_presence_state_get_default_status (state));
192         }
193
194         gtk_status_icon_set_from_icon_name (priv->icon, icon_name);
195         gtk_status_icon_set_tooltip (priv->icon, status);
196
197         g_free (status);
198
199         if (state < MC_PRESENCE_AVAILABLE) {
200                 gtk_widget_set_sensitive (priv->message_item, FALSE);
201         } else {
202                 gtk_widget_set_sensitive (priv->message_item, TRUE);
203         }
204 }
205
206 static void
207 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
208 {
209         EmpathyStatusIconPriv *priv;
210         gboolean               visible;
211
212         priv = GET_PRIV (icon);
213
214         visible = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
215
216         if (visible) {
217                 gtk_widget_hide (GTK_WIDGET (priv->window));
218                 gossip_conf_set_bool (gossip_conf_get (),
219                                       GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, TRUE);
220         } else {
221                 GList *accounts;
222
223                 gossip_window_present (GTK_WINDOW (priv->window), TRUE);
224                 gossip_conf_set_bool (gossip_conf_get (),
225                                       GOSSIP_PREFS_UI_MAIN_WINDOW_HIDDEN, FALSE);
226         
227                 /* Show the accounts dialog if there is no enabled accounts */
228                 accounts = mc_accounts_list_by_enabled (TRUE);
229                 if (accounts) {
230                         mc_accounts_list_free (accounts);
231                 } else {
232                         gossip_debug (DEBUG_DOMAIN,
233                                       "No enabled account, Showing account dialog");
234                         gossip_accounts_dialog_show (GTK_WINDOW (priv->window));
235                 }
236         }
237 }
238
239 static void
240 status_icon_activate_cb (GtkStatusIcon     *status_icon,
241                          EmpathyStatusIcon *icon)
242 {
243         status_icon_toggle_visibility (icon);
244 }
245
246 static gboolean
247 status_icon_delete_event_cb (GtkWidget         *widget,
248                              GdkEvent          *event,
249                              EmpathyStatusIcon *icon)
250 {
251         status_icon_toggle_visibility (icon);
252
253         return TRUE;
254 }
255
256 static void
257 status_icon_popup_menu_cb (GtkStatusIcon     *status_icon,
258                            guint              button,
259                            guint              activate_time,
260                            EmpathyStatusIcon *icon)
261 {
262         EmpathyStatusIconPriv *priv;
263         GtkWidget             *submenu;
264         gboolean               show;
265
266         priv = GET_PRIV (icon);
267
268         show = gossip_window_get_is_visible (GTK_WINDOW (priv->window));
269
270         g_signal_handlers_block_by_func (priv->show_window_item,
271                                          status_icon_show_hide_window_cb,
272                                          icon);
273         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item),
274                                         show);
275         g_signal_handlers_unblock_by_func (priv->show_window_item,
276                                            status_icon_show_hide_window_cb,
277                                            icon);
278
279         submenu = gossip_presence_chooser_create_menu ();
280         gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item),
281                                    submenu);
282
283         gtk_menu_popup (GTK_MENU (priv->popup_menu),
284                         NULL, NULL,
285                         gtk_status_icon_position_menu,
286                         priv->icon,
287                         button,
288                         activate_time);
289 }
290
291 static void
292 status_icon_create_menu (EmpathyStatusIcon *icon)
293 {
294         EmpathyStatusIconPriv *priv;
295         GladeXML              *glade;
296
297         priv = GET_PRIV (icon);
298
299         glade = gossip_glade_get_file ("empathy-status-icon.glade",
300                                        "tray_menu",
301                                        NULL,
302                                        "tray_menu", &priv->popup_menu,
303                                        "tray_show_list", &priv->show_window_item,
304                                        "tray_new_message", &priv->message_item,
305                                        "tray_status", &priv->status_item,
306                                        NULL);
307
308         gossip_glade_connect (glade,
309                               icon,
310                               "tray_new_message", "activate", status_icon_new_message_cb,
311                               "tray_quit", "activate", status_icon_quit_cb,
312                               NULL);
313
314         g_signal_connect (priv->show_window_item, "toggled",
315                           G_CALLBACK (status_icon_show_hide_window_cb),
316                           icon);
317
318         g_object_unref (glade);
319 }
320
321 static void
322 status_icon_new_message_cb (GtkWidget         *widget,
323                             EmpathyStatusIcon *icon)
324 {
325         EmpathyStatusIconPriv *priv;
326
327         priv = GET_PRIV (icon);
328
329         //gossip_new_message_dialog_show (GTK_WINDOW (priv->window));
330 }
331
332 static void
333 status_icon_quit_cb (GtkWidget         *window,
334                      EmpathyStatusIcon *icon)
335 {
336         gtk_main_quit ();
337 }
338
339 static void
340 status_icon_show_hide_window_cb (GtkWidget         *widget,
341                                  EmpathyStatusIcon *icon)
342 {
343         status_icon_toggle_visibility (icon);
344 }
345