1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gtk/gtkwindow.h>
27 #include <gtk/gtkeventbox.h>
28 #include <gtk/gtktooltips.h>
29 #include <gtk/gtkimage.h>
30 #include <gtk/gtkmenu.h>
31 #include <gtk/gtkmenuitem.h>
32 #include <gtk/gtkversion.h>
33
34 #include "eggtrayicon.h"
35 #include "trayicon.h"
36 #include "mainwindow.h"
37 #include "utils.h"
38 #include "gtkutils.h"
39 #include "stock_pixmap.h"
40 #include "menu.h"
41 #include "main.h"
42 #include "inc.h"
43 #include "compose.h"
44 #include "prefs_common.h"
45
46 #if GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11)
47
48 #if GTK_CHECK_VERSION(2, 10, 0)
49 #include <gtk/gtkstatusicon.h>
50 #endif
51
52 #ifdef G_OS_WIN32
53 #define TRAYICON_IMAGE STOCK_PIXMAP_SYLPHEED_SMALL
54 #define TRAYICON_NEW_IMAGE STOCK_PIXMAP_SYLPHEED_NEWMAIL_SMALL
55 #else
56 #define TRAYICON_IMAGE STOCK_PIXMAP_SYLPHEED
57 #define TRAYICON_NEW_IMAGE STOCK_PIXMAP_SYLPHEED_NEWMAIL
58 #endif
59
60 #define TRAYICON_NOTIFY_PERIOD 10000
61
62 static TrayIcon trayicon;
63 static GtkWidget *trayicon_menu;
64 static gboolean on_notify = FALSE;
65 static gboolean default_tooltip = FALSE;
66
67 #if GTK_CHECK_VERSION(2, 10, 0)
68
69 static void trayicon_activated (GtkStatusIcon *status_icon,
70 gpointer data);
71 static void trayicon_popup_menu_cb (GtkStatusIcon *status_icon,
72 guint button,
73 guint activate_time,
74 gpointer data);
75
76 #else
77
78 static GtkWidget *trayicon_img;
79 static GtkWidget *eventbox;
80 static GtkTooltips *trayicon_tip;
81
82 static void trayicon_button_pressed (GtkWidget *widget,
83 GdkEventButton *event,
84 gpointer data);
85 static void trayicon_destroy_cb (GtkWidget *widget,
86 gpointer data);
87
88 #endif
89
90 static void trayicon_present (GtkWidget *widget,
91 gpointer data);
92 static void trayicon_inc (GtkWidget *widget,
93 gpointer data);
94 static void trayicon_inc_all (GtkWidget *widget,
95 gpointer data);
96 static void trayicon_send (GtkWidget *widget,
97 gpointer data);
98 static void trayicon_compose (GtkWidget *widget,
99 gpointer data);
100 static void trayicon_app_exit (GtkWidget *widget,
101 gpointer data);
102
trayicon_create(MainWindow * mainwin)103 TrayIcon *trayicon_create(MainWindow *mainwin)
104 {
105 GtkWidget *menuitem;
106
107 #if GTK_CHECK_VERSION(2, 10, 0)
108 GdkPixbuf *pixbuf;
109
110 stock_pixbuf_gdk(NULL, TRAYICON_IMAGE, &pixbuf);
111 trayicon.status_icon = gtk_status_icon_new_from_pixbuf(pixbuf);
112
113 g_signal_connect(G_OBJECT(trayicon.status_icon), "activate",
114 G_CALLBACK(trayicon_activated), mainwin);
115 g_signal_connect(G_OBJECT(trayicon.status_icon), "popup-menu",
116 G_CALLBACK(trayicon_popup_menu_cb), mainwin);
117 #else
118 trayicon.widget = GTK_WIDGET(egg_tray_icon_new("Sylpheed"));
119 g_signal_connect(G_OBJECT(trayicon.widget), "destroy",
120 G_CALLBACK(trayicon_destroy_cb), mainwin);
121
122 eventbox = gtk_event_box_new();
123 gtk_widget_show(eventbox);
124 gtk_container_add(GTK_CONTAINER(trayicon.widget), eventbox);
125 g_signal_connect(G_OBJECT(eventbox), "button_press_event",
126 G_CALLBACK(trayicon_button_pressed), mainwin);
127 trayicon_img = stock_pixbuf_widget_scale(NULL, TRAYICON_IMAGE, 24, 24);
128 gtk_widget_show(trayicon_img);
129 gtk_container_add(GTK_CONTAINER(eventbox), trayicon_img);
130
131 trayicon_tip = gtk_tooltips_new();
132 #endif
133 on_notify = FALSE;
134 default_tooltip = FALSE;
135 trayicon_set_tooltip(NULL);
136
137 if (!trayicon_menu) {
138 trayicon_menu = gtk_menu_new();
139 gtk_widget_show(trayicon_menu);
140 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
141 _("_Display Sylpheed"), 0);
142 g_signal_connect(G_OBJECT(menuitem), "activate",
143 G_CALLBACK(trayicon_present), mainwin);
144 MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0);
145 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
146 _("Get from _current account"), 0);
147 g_signal_connect(G_OBJECT(menuitem), "activate",
148 G_CALLBACK(trayicon_inc), mainwin);
149 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
150 _("Get from _all accounts"), 0);
151 g_signal_connect(G_OBJECT(menuitem), "activate",
152 G_CALLBACK(trayicon_inc_all), mainwin);
153 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
154 _("_Send queued messages"), 0);
155 g_signal_connect(G_OBJECT(menuitem), "activate",
156 G_CALLBACK(trayicon_send), mainwin);
157
158 MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0);
159 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
160 _("Compose _new message"), 0);
161 g_signal_connect(G_OBJECT(menuitem), "activate",
162 G_CALLBACK(trayicon_compose), mainwin);
163
164 MENUITEM_ADD(trayicon_menu, menuitem, NULL, 0);
165 MENUITEM_ADD_WITH_MNEMONIC(trayicon_menu, menuitem,
166 _("E_xit"), 0);
167 g_signal_connect(G_OBJECT(menuitem), "activate",
168 G_CALLBACK(trayicon_app_exit), mainwin);
169 }
170
171 return &trayicon;
172 }
173
174 #if GTK_CHECK_VERSION(2, 10, 0)
175
trayicon_show(TrayIcon * tray_icon)176 void trayicon_show(TrayIcon *tray_icon)
177 {
178 gtk_status_icon_set_visible(tray_icon->status_icon, TRUE);
179 };
180
trayicon_hide(TrayIcon * tray_icon)181 void trayicon_hide(TrayIcon *tray_icon)
182 {
183 gtk_status_icon_set_visible(tray_icon->status_icon, FALSE);
184 }
185
trayicon_destroy(TrayIcon * tray_icon)186 void trayicon_destroy(TrayIcon *tray_icon)
187 {
188 g_object_unref(tray_icon->status_icon);
189 tray_icon->status_icon = NULL;
190 }
191
trayicon_set_tooltip(const gchar * text)192 void trayicon_set_tooltip(const gchar *text)
193 {
194 if (text) {
195 default_tooltip = FALSE;
196 gtk_status_icon_set_tooltip(trayicon.status_icon, text);
197 } else if (!default_tooltip) {
198 default_tooltip = TRUE;
199 gtk_status_icon_set_tooltip(trayicon.status_icon,
200 _("Sylpheed"));
201 }
202 }
203
204 static guint notify_tag = 0;
205
notify_timeout_cb(gpointer data)206 gboolean notify_timeout_cb(gpointer data)
207 {
208 gdk_threads_enter();
209 gtk_status_icon_set_blinking(trayicon.status_icon, FALSE);
210 notify_tag = 0;
211 gdk_threads_leave();
212
213 return FALSE;
214 }
215
trayicon_set_notify(gboolean enabled)216 void trayicon_set_notify(gboolean enabled)
217 {
218 if (enabled && !on_notify) {
219 trayicon_set_stock_icon(TRAYICON_NEW_IMAGE);
220 on_notify = TRUE;
221 } else if (!enabled && on_notify) {
222 trayicon_set_stock_icon(TRAYICON_IMAGE);
223 on_notify = FALSE;
224 }
225
226 if (enabled && notify_tag == 0) {
227 gtk_status_icon_set_blinking(trayicon.status_icon, enabled);
228 notify_tag = g_timeout_add(TRAYICON_NOTIFY_PERIOD, notify_timeout_cb, NULL);
229 } else if (!enabled && notify_tag > 0) {
230 g_source_remove(notify_tag);
231 notify_timeout_cb(NULL);
232 }
233 }
234
trayicon_set_stock_icon(StockPixmap icon)235 void trayicon_set_stock_icon(StockPixmap icon)
236 {
237 GdkPixbuf *pixbuf;
238
239 stock_pixbuf_gdk(NULL, icon, &pixbuf);
240 gtk_status_icon_set_from_pixbuf(trayicon.status_icon, pixbuf);
241 }
242
trayicon_activated(GtkStatusIcon * status_icon,gpointer data)243 static void trayicon_activated(GtkStatusIcon *status_icon, gpointer data)
244 {
245 MainWindow *mainwin = (MainWindow *)data;
246
247 #ifdef G_OS_WIN32
248 if (prefs_common.toggle_window_on_trayicon_click &&
249 !mainwin->window_hidden && !mainwin->window_obscured)
250 gtk_window_iconify(GTK_WINDOW(mainwin->window));
251 else
252 main_window_popup(mainwin);
253 #else
254 if (prefs_common.toggle_window_on_trayicon_click &&
255 gtk_window_is_active(GTK_WINDOW(mainwin->window)))
256 gtk_window_iconify(GTK_WINDOW(mainwin->window));
257 else
258 main_window_popup(mainwin);
259 #endif
260 }
261
trayicon_popup_menu_cb(GtkStatusIcon * status_icon,guint button,guint activate_time,gpointer data)262 static void trayicon_popup_menu_cb(GtkStatusIcon *status_icon, guint button,
263 guint activate_time, gpointer data)
264 {
265 gtk_menu_popup(GTK_MENU(trayicon_menu), NULL, NULL, NULL, NULL,
266 button, activate_time);
267 }
268
269 #else /* GTK_CHECK_VERSION(2, 10, 0) */
270
trayicon_show(TrayIcon * tray_icon)271 void trayicon_show(TrayIcon *tray_icon)
272 {
273 gtk_widget_show(tray_icon->widget);
274 };
275
trayicon_hide(TrayIcon * tray_icon)276 void trayicon_hide(TrayIcon *tray_icon)
277 {
278 gtk_widget_destroy(tray_icon->widget);
279 tray_icon->widget = NULL;
280 }
281
trayicon_destroy(TrayIcon * tray_icon)282 void trayicon_destroy(TrayIcon *tray_icon)
283 {
284 g_signal_handlers_disconnect_by_func(G_OBJECT(tray_icon->widget),
285 G_CALLBACK(trayicon_destroy_cb),
286 main_window_get());
287 gtk_widget_destroy(tray_icon->widget);
288 tray_icon->widget = NULL;
289 }
290
trayicon_set_tooltip(const gchar * text)291 void trayicon_set_tooltip(const gchar *text)
292 {
293 if (text) {
294 default_tooltip = FALSE;
295 gtk_tooltips_set_tip(trayicon_tip, trayicon.widget, text,
296 NULL);
297 } else if (!default_tooltip) {
298 default_tooltip = TRUE;
299 gtk_tooltips_set_tip(trayicon_tip, trayicon.widget,
300 _("Sylpheed"), NULL);
301 }
302 }
303
trayicon_set_notify(gboolean enabled)304 void trayicon_set_notify(gboolean enabled)
305 {
306 if (enabled && !on_notify) {
307 trayicon_set_stock_icon(TRAYICON_NEW_IMAGE);
308 on_notify = TRUE;
309 } else if (!enabled && on_notify) {
310 trayicon_set_stock_icon(TRAYICON_IMAGE);
311 on_notify = FALSE;
312 }
313 }
314
trayicon_set_stock_icon(StockPixmap icon)315 void trayicon_set_stock_icon(StockPixmap icon)
316 {
317 GdkPixbuf *pixbuf;
318 GdkPixbuf *scaled_pixbuf;
319
320 stock_pixbuf_gdk(NULL, icon, &pixbuf);
321 scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, 24, 24,
322 GDK_INTERP_HYPER);
323 gtk_image_set_from_pixbuf(GTK_IMAGE(trayicon_img), scaled_pixbuf);
324 g_object_unref(scaled_pixbuf);
325 }
326
trayicon_button_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)327 static void trayicon_button_pressed(GtkWidget *widget, GdkEventButton *event,
328 gpointer data)
329 {
330 MainWindow *mainwin = (MainWindow *)data;
331
332 if (!event)
333 return;
334
335 if (event->button == 1) {
336 if (prefs_common.toggle_window_on_trayicon_click &&
337 gtk_window_is_active(GTK_WINDOW(mainwin->window)))
338 gtk_window_iconify(GTK_WINDOW(mainwin->window));
339 else
340 main_window_popup(mainwin);
341 } else if (event->button == 3) {
342 gtk_menu_popup(GTK_MENU(trayicon_menu), NULL, NULL, NULL, NULL,
343 event->button, event->time);
344 }
345 }
346
trayicon_restore(gpointer data)347 static gboolean trayicon_restore(gpointer data)
348 {
349 MainWindow *mainwin = (MainWindow *)data;
350
351 gdk_threads_enter();
352 mainwin->tray_icon = trayicon_create(mainwin);
353 gdk_threads_leave();
354 return FALSE;
355 }
356
trayicon_destroy_cb(GtkWidget * widget,gpointer data)357 static void trayicon_destroy_cb(GtkWidget *widget, gpointer data)
358 {
359 g_idle_add(trayicon_restore, data);
360 }
361
362 #endif /* GTK_CHECK_VERSION(2, 10, 0) */
363
trayicon_present(GtkWidget * widget,gpointer data)364 static void trayicon_present(GtkWidget *widget, gpointer data)
365 {
366 MainWindow *mainwin = (MainWindow *)data;
367
368 main_window_popup(mainwin);
369 }
370
trayicon_inc(GtkWidget * widget,gpointer data)371 static void trayicon_inc(GtkWidget *widget, gpointer data)
372 {
373 if (!inc_is_active() && !gtkut_window_modal_exist())
374 inc_mail((MainWindow *)data);
375 }
376
trayicon_inc_all(GtkWidget * widget,gpointer data)377 static void trayicon_inc_all(GtkWidget *widget, gpointer data)
378 {
379 if (!inc_is_active() && !gtkut_window_modal_exist())
380 inc_all_account_mail((MainWindow *)data, FALSE);
381 }
382
trayicon_send(GtkWidget * widget,gpointer data)383 static void trayicon_send(GtkWidget *widget, gpointer data)
384 {
385 if (!gtkut_window_modal_exist())
386 main_window_send_queue((MainWindow *)data);
387 }
388
trayicon_compose(GtkWidget * widget,gpointer data)389 static void trayicon_compose(GtkWidget *widget, gpointer data)
390 {
391 if (!gtkut_window_modal_exist())
392 compose_new(NULL, NULL, NULL, NULL);
393 }
394
trayicon_app_exit(GtkWidget * widget,gpointer data)395 static void trayicon_app_exit(GtkWidget *widget, gpointer data)
396 {
397 MainWindow *mainwin = (MainWindow *)data;
398
399 if (mainwin->lock_count == 0 && !gtkut_window_modal_exist())
400 app_will_exit(FALSE);
401 }
402
403 #else /* GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11) */
404
trayicon_create(MainWindow * mainwin)405 TrayIcon *trayicon_create(MainWindow *mainwin)
406 {
407 return NULL;
408 }
409
trayicon_show(TrayIcon * tray_icon)410 void trayicon_show(TrayIcon *tray_icon)
411 {
412 }
413
trayicon_hide(TrayIcon * tray_icon)414 void trayicon_hide(TrayIcon *tray_icon)
415 {
416 }
417
trayicon_destroy(TrayIcon * tray_icon)418 void trayicon_destroy(TrayIcon *tray_icon)
419 {
420 }
421
trayicon_set_tooltip(const gchar * text)422 void trayicon_set_tooltip(const gchar *text)
423 {
424 }
425
trayicon_set_notify(gboolean enabled)426 void trayicon_set_notify(gboolean enabled)
427 {
428 }
429
trayicon_set_stock_icon(StockPixmap icon)430 void trayicon_set_stock_icon(StockPixmap icon)
431 {
432 }
433
434 #endif /* GTK_CHECK_VERSION(2, 10, 0) || defined(GDK_WINDOWING_X11) */
435