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