Remove inclusion of sys/socket.h from nntp-thread.c
[claws.git] / src / plugins / notification / notification_trayicon.c
blobd5a4e637d3efa67339e93861da6e2ecd67256bbf
1 /* Notification plugin for Claws Mail
2 * Copyright (C) 2005-2022 Holger Berndt and the Claws Mail Team.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /* This module is of course inspired by the trayicon plugin which is
19 * shipped with Claws Mail, copyrighted by the Claws Mail Team. */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 # include "claws-features.h"
24 #endif
26 #include <glib.h>
27 #include <glib/gi18n.h>
29 #ifdef NOTIFICATION_TRAYICON
31 #include "notification_trayicon.h"
32 #include "notification_prefs.h"
33 #include "notification_core.h"
34 #include "notification_hotkeys.h"
35 #include "notification_pixbuf.h"
36 #include "notification_foldercheck.h"
38 #include "main.h"
39 #include "account.h"
40 #include "mainwindow.h"
41 #include "prefs_common.h"
42 #include "alertpanel.h"
43 #include "gtk/menu.h"
44 #ifndef USE_ALT_ADDRBOOK
45 #include "addressbook.h"
46 #include "addrindex.h"
47 #else
48 #include "addressbook-dbus.h"
49 #endif
51 #include "gtk/manage_window.h"
52 #include "common/utils.h"
53 #include "gtk/gtkutils.h"
54 #include "inc.h"
56 static void notification_trayicon_account_list_reset(const gchar *,
57 gpointer,
58 gboolean);
60 static GdkPixbuf* notification_trayicon_create(void);
62 static void notification_trayicon_on_popup_menu(GtkStatusIcon*,guint,
63 guint,gpointer);
64 static gboolean notification_trayicon_on_size_changed(GtkStatusIcon*,
65 gint, gpointer);
67 static void trayicon_get_all_cb(GtkAction*, gpointer);
68 static void trayicon_get_from_account_cb(GtkAction*, gpointer);
69 static void trayicon_compose_cb(GtkAction*, gpointer);
70 static void trayicon_compose_acc_cb(GtkMenuItem*, gpointer);
71 static void trayicon_addressbook_cb(GtkAction*, gpointer);
72 static void trayicon_exit_cb(GtkAction*,gpointer);
73 static void trayicon_toggle_offline_cb(GtkAction*,gpointer);
74 #ifdef HAVE_LIBNOTIFY
75 static void trayicon_toggle_notify_cb(GtkAction*,gpointer);
76 #endif
78 #ifdef HAVE_LIBNOTIFY
79 #include <libnotify/notify.h>
81 #ifndef NOTIFY_CHECK_VERSION
82 # define NOTIFY_CHECK_VERSION(a,b,c) 0
83 #endif
85 typedef struct {
86 gint count;
87 gint num_mail;
88 gint num_news;
89 gint num_calendar;
90 gint num_rss;
91 gchar *msg_path;
92 NotifyNotification *notification;
93 GError *error;
94 } NotificationTrayiconPopup;
96 static NotificationTrayiconPopup popup;
98 static gboolean notification_trayicon_popup_add_msg(MsgInfo*,
99 NotificationFolderType);
100 static gboolean notification_trayicon_popup_create(MsgInfo*,
101 NotificationFolderType);
102 static void popup_timeout_fun(NotifyNotification*, gpointer);
103 static void notification_trayicon_popup_free_func(gpointer);
104 static void notification_trayicon_popup_default_action_cb(NotifyNotification*,
105 const char*,void*);
106 static gchar* notification_trayicon_popup_assemble_summary(void);
107 static gchar* notification_trayicon_popup_assemble_body(MsgInfo*);
108 static void notification_trayicon_popup_count_msgs(NotificationFolderType);
110 G_LOCK_DEFINE_STATIC(trayicon_popup);
112 #endif
114 static GtkStatusIcon *trayicon;
115 static gboolean updating_menu = FALSE;
116 static GtkWidget *traymenu_popup;
117 static GtkWidget *focused_widget = NULL;
119 static GtkActionEntry trayicon_popup_menu_entries[] = {
120 {"SysTrayiconPopup", NULL, "SysTrayiconPopup", NULL, NULL, NULL },
121 {"SysTrayiconPopup/GetMail", NULL, N_("_Get Mail"), NULL, NULL, G_CALLBACK(trayicon_get_all_cb) },
122 {"SysTrayiconPopup/GetMailAcc", NULL, N_("_Get Mail from account"), NULL, NULL, NULL },
123 {"SysTrayiconPopup/---", NULL, "---", NULL, NULL, NULL },
124 {"SysTrayiconPopup/Email", NULL, N_("_Email"), NULL, NULL, G_CALLBACK(trayicon_compose_cb) },
125 {"SysTrayiconPopup/EmailAcc", NULL, N_("E_mail from account"), NULL, NULL, NULL },
126 {"SysTrayiconPopup/OpenAB", NULL, N_("Open A_ddressbook"), NULL, NULL, G_CALLBACK(trayicon_addressbook_cb) },
127 {"SysTrayiconPopup/Exit", NULL, N_("E_xit Claws Mail"), NULL, NULL, G_CALLBACK(trayicon_exit_cb) },
130 static GtkToggleActionEntry trayicon_popup_toggle_menu_entries[] =
132 {"SysTrayiconPopup/ToggleOffline", NULL, N_("_Work Offline"), NULL, NULL, G_CALLBACK(trayicon_toggle_offline_cb), FALSE },
133 #ifdef HAVE_LIBNOTIFY
134 {"SysTrayiconPopup/ShowBubbles", NULL, N_("Show Trayicon Notifications"), NULL, NULL, G_CALLBACK(trayicon_toggle_notify_cb), FALSE },
135 #endif
139 void notification_trayicon_msg(MsgInfo *msginfo)
141 #ifndef HAVE_LIBNOTIFY
142 return;
144 #else
145 FolderType ftype;
146 NotificationFolderType nftype;
147 gchar *uistr;
149 nftype = F_TYPE_MAIL;
151 if(!msginfo || !notify_config.trayicon_enabled ||
152 !notify_config.trayicon_popup_enabled ||
153 !MSG_IS_NEW(msginfo->flags))
154 return;
156 if(notify_config.trayicon_folder_specific) {
157 guint id;
158 GSList *list;
159 gchar *identifier;
160 gboolean found = FALSE;
162 if(!(msginfo->folder))
163 return;
165 identifier = folder_item_get_identifier(msginfo->folder);
167 id =
168 notification_register_folder_specific_list
169 (TRAYICON_SPECIFIC_FOLDER_ID_STR);
170 list = notification_foldercheck_get_list(id);
171 for(; (list != NULL) && !found; list = g_slist_next(list)) {
172 gchar *list_identifier;
173 FolderItem *list_item = (FolderItem*) list->data;
175 list_identifier = folder_item_get_identifier(list_item);
176 if(!g_strcmp0(list_identifier, identifier))
177 found = TRUE;
179 g_free(list_identifier);
181 g_free(identifier);
183 if(!found)
184 return;
185 } /* folder specific */
187 ftype = msginfo->folder->folder->klass->type;
189 G_LOCK(trayicon_popup);
190 /* Check out which type to notify about */
191 switch(ftype) {
192 case F_MH:
193 case F_MBOX:
194 case F_MAILDIR:
195 case F_IMAP:
196 nftype = F_TYPE_MAIL;
197 break;
198 case F_NEWS:
199 nftype = F_TYPE_NEWS;
200 break;
201 case F_UNKNOWN:
202 if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
203 G_UNLOCK(trayicon_popup);
204 return;
206 else if(!strcmp(uistr, "vCalendar"))
207 nftype = F_TYPE_CALENDAR;
208 else if(!strcmp(uistr, "RSSyl"))
209 nftype = F_TYPE_RSS;
210 else {
211 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
212 G_UNLOCK(trayicon_popup);
213 return;
215 break;
216 default:
217 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
218 G_UNLOCK(trayicon_popup);
219 return;
223 notification_trayicon_popup_add_msg(msginfo, nftype);
225 G_UNLOCK(trayicon_popup);
227 #endif /* HAVE_LIBNOTIFY */
230 void notification_trayicon_destroy(void)
232 if(trayicon) {
233 gtk_status_icon_set_visible(trayicon, FALSE);
234 g_object_unref(trayicon);
235 trayicon = NULL;
239 void notification_update_trayicon()
241 gchar *buf;
242 static GdkPixbuf *old_icon = NULL;
243 GdkPixbuf *new_icon;
244 gint offset;
245 NotificationMsgCount count;
246 GSList *list;
248 if(!notify_config.trayicon_enabled)
249 return;
251 if(notify_config.trayicon_folder_specific) {
252 guint id;
253 id =
254 notification_register_folder_specific_list
255 (TRAYICON_SPECIFIC_FOLDER_ID_STR);
256 list = notification_foldercheck_get_list(id);
258 else
259 list = NULL;
261 notification_core_get_msg_count(list, &count);
263 if(!trayicon) {
265 #ifdef NOTIFICATION_HOTKEYS
266 notification_hotkeys_update_bindings();
267 #endif
269 old_icon = notification_trayicon_create();
270 if(!trayicon) {
271 debug_print("Notification plugin: Could not create trayicon\n");
272 return;
276 /* Tooltip */
277 buf = g_strdup_printf(_("New %d, Unread: %d, Total: %d"),
278 count.new_msgs, count.unread_msgs,
279 count.total_msgs);
280 gtk_status_icon_set_tooltip_text(trayicon, buf);
282 g_free(buf);
284 /* Pixmap */
285 (prefs_common_get_prefs()->work_offline) ? (offset = 1) : (offset = 0);
287 if((count.new_msgs > 0) && (count.unreadmarked_msgs > 0))
288 new_icon =
289 notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMARKEDMAIL+offset);
290 else if(count.new_msgs > 0)
291 new_icon =
292 notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMAIL+offset);
293 else if(count.unreadmarked_msgs > 0)
294 new_icon =
295 notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMARKEDMAIL+offset);
296 else if(count.unread_msgs > 0)
297 new_icon =
298 notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMAIL+offset);
299 else
300 new_icon =
301 notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL+offset);
303 if(new_icon != old_icon) {
304 gtk_status_icon_set_from_pixbuf(trayicon, new_icon);
305 old_icon = new_icon;
309 gboolean notification_trayicon_main_window_close(gpointer source, gpointer data)
311 if(!notify_config.trayicon_enabled)
312 return FALSE;
314 if(source) {
315 gboolean *close_allowed = (gboolean*)source;
317 if(notify_config.trayicon_close_to_tray) {
318 MainWindow *mainwin = mainwindow_get_mainwindow();
320 *close_allowed = FALSE;
321 if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))) {
322 focused_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window));
323 main_window_hide(mainwin);
327 return FALSE;
330 gboolean notification_trayicon_main_window_got_iconified(gpointer source,
331 gpointer data)
333 MainWindow *mainwin = mainwindow_get_mainwindow();
335 if(!notify_config.trayicon_enabled)
336 return FALSE;
338 if(notify_config.trayicon_hide_when_iconified &&
339 mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))
340 && !gtk_window_get_skip_taskbar_hint(GTK_WINDOW(mainwin->window))) {
341 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
343 return FALSE;
346 static void notification_trayicon_account_list_reset(const gchar *menuname,
347 gpointer callback,
348 gboolean receive)
350 GList *cur_ac;
351 GtkWidget *menu, *submenu;
352 GtkWidget *menuitem;
353 PrefsAccount *ac_prefs;
355 GList *account_list = account_get_list();
357 menu = gtk_ui_manager_get_widget(gtkut_ui_manager(), menuname);
358 gtk_widget_show(menu);
360 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), NULL);
361 submenu = gtk_menu_new();
363 for(cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) {
364 ac_prefs = (PrefsAccount *)cur_ac->data;
366 /* accounts list for receiving: skip SMTP-only accounts */
367 if (receive && ac_prefs->protocol == A_NONE)
368 continue;
370 menuitem = gtk_menu_item_new_with_label
371 (ac_prefs->account_name ? ac_prefs->account_name
372 : _("Untitled"));
373 gtk_widget_show(menuitem);
374 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
375 g_signal_connect(G_OBJECT(menuitem), "activate",
376 G_CALLBACK(callback),
377 ac_prefs);
379 gtk_widget_show(submenu);
380 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu);
383 gboolean notification_trayicon_account_list_changed(gpointer source,
384 gpointer data)
386 if (notify_config.trayicon_enabled) {
387 notification_trayicon_account_list_reset("/Menus/SysTrayiconPopup/GetMailAcc",
388 (gpointer)trayicon_get_from_account_cb, TRUE);
389 notification_trayicon_account_list_reset("/Menus/SysTrayiconPopup/EmailAcc",
390 (gpointer)trayicon_compose_acc_cb, FALSE);
392 return FALSE;
395 static GdkPixbuf* notification_trayicon_create(void)
397 GdkPixbuf *trayicon_nomail;
398 GtkActionGroup *action_group;
400 trayicon_nomail = notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL);
402 notification_trayicon_destroy();
404 trayicon = gtk_status_icon_new_from_pixbuf(trayicon_nomail);
406 g_signal_connect(G_OBJECT(trayicon), "activate",
407 G_CALLBACK(notification_trayicon_on_activate), NULL);
408 g_signal_connect(G_OBJECT(trayicon), "popup-menu",
409 G_CALLBACK(notification_trayicon_on_popup_menu), NULL);
410 g_signal_connect(G_OBJECT(trayicon), "size-changed",
411 G_CALLBACK(notification_trayicon_on_size_changed), NULL);
413 /* Popup-Menu */
414 action_group = cm_menu_create_action_group("SysTrayiconPopup", trayicon_popup_menu_entries,
415 G_N_ELEMENTS(trayicon_popup_menu_entries), NULL);
416 gtk_action_group_add_toggle_actions(action_group, trayicon_popup_toggle_menu_entries,
417 G_N_ELEMENTS(trayicon_popup_toggle_menu_entries), NULL);
419 MENUITEM_ADDUI("/Menus", "SysTrayiconPopup", "SysTrayiconPopup", GTK_UI_MANAGER_MENU)
420 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "GetMail", "SysTrayiconPopup/GetMail", GTK_UI_MANAGER_MENUITEM)
421 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "GetMailAcc", "SysTrayiconPopup/GetMailAcc", GTK_UI_MANAGER_MENU)
422 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator1", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
423 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Email", "SysTrayiconPopup/Email", GTK_UI_MANAGER_MENUITEM)
424 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "EmailAcc", "SysTrayiconPopup/EmailAcc", GTK_UI_MANAGER_MENU)
425 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator2", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
426 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "OpenAB", "SysTrayiconPopup/OpenAB", GTK_UI_MANAGER_MENUITEM)
427 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator3", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
428 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ToggleOffline", "SysTrayiconPopup/ToggleOffline", GTK_UI_MANAGER_MENUITEM)
429 #ifdef HAVE_LIBNOTIFY
430 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ShowBubbles", "SysTrayiconPopup/ShowBubbles", GTK_UI_MANAGER_MENUITEM)
431 #endif
432 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator4", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
433 MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Exit", "SysTrayiconPopup/Exit", GTK_UI_MANAGER_MENUITEM)
435 traymenu_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
436 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup")));
439 return trayicon_nomail;
442 void notification_trayicon_on_activate(GtkStatusIcon *status_icon, gpointer user_data)
444 MainWindow *mainwin = mainwindow_get_mainwindow();
446 if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
447 focused_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window));
449 notification_toggle_hide_show_window();
451 if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
452 gtk_window_set_focus(GTK_WINDOW(mainwin->window), focused_widget);
455 static void notification_trayicon_on_popup_menu(GtkStatusIcon *status_icon,
456 guint button, guint activate_time,
457 gpointer user_data)
459 MainWindow *mainwin = mainwindow_get_mainwindow();
461 if(!mainwin)
462 return;
464 /* tell callbacks to skip any event */
465 updating_menu = TRUE;
466 /* initialize checkitems according to current states */
467 cm_toggle_menu_set_active("SysTrayiconPopup/ToggleOffline", prefs_common_get_prefs()->work_offline);
468 #ifdef HAVE_LIBNOTIFY
469 cm_toggle_menu_set_active("SysTrayiconPopup/ShowBubbles", notify_config.trayicon_popup_enabled);
470 #endif
471 cm_menu_set_sensitive("SysTrayiconPopup/GetMail", mainwin->lock_count == 0);
472 cm_menu_set_sensitive("SysTrayiconPopup/GetMailAcc", mainwin->lock_count == 0);
473 cm_menu_set_sensitive("SysTrayiconPopup/Exit", mainwin->lock_count == 0);
475 updating_menu = FALSE;
477 gtk_menu_popup_at_pointer(GTK_MENU(traymenu_popup), NULL);
480 static gboolean notification_trayicon_on_size_changed(GtkStatusIcon *icon,
481 gint size,
482 gpointer user_data)
484 notification_update_msg_counts(NULL);
485 return FALSE;
488 /* popup menu callbacks */
489 static void trayicon_get_all_cb(GtkAction *action, gpointer data)
491 MainWindow *mainwin = mainwindow_get_mainwindow();
492 inc_all_account_mail_cb(mainwin, 0, NULL);
495 static void trayicon_get_from_account_cb(GtkAction *action, gpointer data)
497 MainWindow *mainwin = mainwindow_get_mainwindow();
498 PrefsAccount *account = (PrefsAccount *)data;
499 inc_account_mail(mainwin, account);
502 static void trayicon_compose_cb(GtkAction *action, gpointer data)
504 MainWindow *mainwin = mainwindow_get_mainwindow();
505 compose_mail_cb(mainwin, 0, NULL);
508 static void trayicon_compose_acc_cb(GtkMenuItem *menuitem, gpointer data)
510 compose_new((PrefsAccount *)data, NULL, NULL);
513 static void trayicon_addressbook_cb(GtkAction *action, gpointer data)
515 #ifndef USE_ALT_ADDRBOOK
516 addressbook_open(NULL);
517 #else
518 GError* error = NULL;
520 addressbook_dbus_open(FALSE, &error);
521 if (error) {
522 g_warning("%s", error->message);
523 g_error_free(error);
525 #endif
528 static void trayicon_toggle_offline_cb(GtkAction *action, gpointer data)
530 /* toggle offline mode if menu checkitem has been clicked */
531 if(!updating_menu) {
532 MainWindow *mainwin = mainwindow_get_mainwindow();
533 main_window_toggle_work_offline(mainwin, !prefs_common_get_prefs()->work_offline, TRUE);
537 #ifdef HAVE_LIBNOTIFY
538 static void trayicon_toggle_notify_cb(GtkAction *action, gpointer data)
540 if(!updating_menu) {
541 notify_config.trayicon_popup_enabled = !notify_config.trayicon_popup_enabled;
544 #endif
546 static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
548 if(prefs_common_get_prefs()->confirm_on_exit) {
549 if(alertpanel(_("Exit"), _("Exit Claws Mail?"),
550 NULL, _("_Cancel"), NULL, _("_OK"),
551 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE) {
552 return;
554 manage_window_focus_in(mainwin->window, NULL, NULL);
557 if (prefs_common_get_prefs()->clean_on_exit) {
558 if (!main_window_empty_trash(mainwin, prefs_common_get_prefs()->ask_on_clean, TRUE))
559 return;
562 app_will_exit(NULL, mainwin);
565 static void trayicon_exit_cb(GtkAction *action, gpointer data)
567 MainWindow *mainwin = mainwindow_get_mainwindow();
569 if(mainwin->lock_count == 0) {
570 app_exit_cb(mainwin, 0, NULL);
574 #ifdef HAVE_LIBNOTIFY
575 static gboolean notification_trayicon_popup_add_msg(MsgInfo *msginfo,
576 NotificationFolderType nftype)
578 gchar *summary;
579 gchar *utf8_str;
580 gboolean retval;
581 GdkPixbuf *pixbuf;
583 g_return_val_if_fail(msginfo, FALSE);
585 if(!popup.notification)
586 return notification_trayicon_popup_create(msginfo,nftype);
588 /* Count messages */
589 notification_trayicon_popup_count_msgs(nftype);
591 if(popup.msg_path) {
592 g_free(popup.msg_path);
593 popup.msg_path = NULL;
596 summary = notification_trayicon_popup_assemble_summary();
597 utf8_str = notification_trayicon_popup_assemble_body(msginfo);
599 /* make sure we show a logo on many msg arrival */
600 pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
601 if(pixbuf)
602 notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
604 retval = notify_notification_update(popup.notification, summary,
605 utf8_str, NULL);
606 g_free(summary);
607 g_free(utf8_str);
608 if(!retval) {
609 debug_print("Notification Plugin: Failed to update notification.\n");
610 return FALSE;
612 /* Show the popup */
613 notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
614 if(!notify_notification_show(popup.notification, &(popup.error))) {
615 debug_print("Notification Plugin: Failed to send updated notification: "
616 "%s\n", popup.error->message);
617 g_clear_error(&(popup.error));
618 return FALSE;
621 debug_print("Notification Plugin: Popup successfully modified "
622 "with libnotify.\n");
624 return TRUE;
627 static gboolean notification_trayicon_popup_create(MsgInfo *msginfo,
628 NotificationFolderType nftype)
630 gchar *summary = NULL;
631 gchar *utf8_str = NULL;
632 GdkPixbuf *pixbuf;
633 GList *caps = NULL;
634 gboolean support_actions = FALSE;
636 /* init libnotify if necessary */
637 if(!notify_is_initted()) {
638 if(!notify_init("claws-mail")) {
639 debug_print("Notification Plugin: Failed to initialize libnotify. "
640 "No popups will be shown.\n");
641 return FALSE;
645 /* Count messages */
646 notification_trayicon_popup_count_msgs(nftype);
648 summary = notification_trayicon_popup_assemble_summary();
649 utf8_str = notification_trayicon_popup_assemble_body(msginfo);
651 #if NOTIFY_CHECK_VERSION(0, 7, 0)
652 popup.notification = notify_notification_new(summary, utf8_str, NULL);
653 #else
654 popup.notification = notify_notification_new(summary, utf8_str, NULL, NULL);
655 notify_notification_attach_to_status_icon(popup.notification, trayicon);
656 #endif
658 g_free(summary);
659 g_free(utf8_str);
661 caps = notify_get_server_caps();
662 if(caps != NULL) {
663 GList *c;
664 for(c = caps; c != NULL; c = c->next) {
665 if(strcmp((char*)c->data, "actions") == 0 ) {
666 support_actions = TRUE;
667 break;
671 g_list_foreach(caps, (GFunc)g_free, NULL);
672 g_list_free(caps);
675 /* Default action */
676 if (support_actions)
677 notify_notification_add_action(popup.notification,
678 "default", _("Present main window"),
679 (NotifyActionCallback)
680 notification_trayicon_popup_default_action_cb,
681 GINT_TO_POINTER(nftype),
682 notification_trayicon_popup_free_func);
684 if(popup.notification == NULL) {
685 debug_print("Notification Plugin: Failed to create a new notification.\n");
686 return FALSE;
689 /* Icon */
690 pixbuf = NULL;
691 #ifndef USE_ALT_ADDRBOOK
692 if(msginfo && msginfo->from) {
693 gchar *icon_path;
694 icon_path = addrindex_get_picture_file(msginfo->from);
695 if(is_file_exist(icon_path)) {
696 GError *error = NULL;
697 gint w, h;
699 gdk_pixbuf_get_file_info(icon_path, &w, &h);
700 if((w > 64) || (h > 64))
701 pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
702 64, 64, TRUE, &error);
703 else
704 pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
706 if(!pixbuf) {
707 debug_print("Could not load picture file: %s\n",
708 error ? error->message : "no details");
709 g_error_free(error);
712 else
713 debug_print("Picture path does not exist: %s\n",icon_path);
714 g_free(icon_path);
716 #endif
717 if(!pixbuf)
718 pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
720 if(pixbuf) {
721 notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
722 g_object_unref(pixbuf);
724 else /* This is not fatal */
725 debug_print("Notification plugin: Icon could not be loaded.\n");
727 /* timeout */
728 notify_notification_set_timeout(popup.notification, notify_config.trayicon_popup_timeout);
730 /* Category */
731 notify_notification_set_category(popup.notification, "email.arrived");
733 /* get notified on bubble close */
734 g_signal_connect(G_OBJECT(popup.notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
736 /* Show the popup */
737 notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
738 if(!notify_notification_show(popup.notification, &(popup.error))) {
739 debug_print("Notification Plugin: Failed to send notification: %s\n",
740 popup.error->message);
741 g_clear_error(&(popup.error));
742 g_object_unref(G_OBJECT(popup.notification));
743 popup.notification = NULL;
744 return FALSE;
747 /* Store path to message */
748 if(nftype == F_TYPE_MAIL) {
749 if(msginfo && msginfo->folder) {
750 gchar *ident;
751 ident = folder_item_get_identifier(msginfo->folder);
752 popup.msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
753 msginfo->msgnum);
754 g_free(ident);
756 else
757 popup.msg_path = NULL;
760 debug_print("Notification Plugin: Popup created with libnotify.\n");
762 return TRUE;
765 static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
767 G_LOCK(trayicon_popup);
769 g_object_unref(G_OBJECT(popup.notification));
771 popup.notification = NULL;
772 g_clear_error(&(popup.error));
774 popup.count = 0;
775 popup.num_mail = 0;
776 popup.num_news = 0;
777 popup.num_calendar = 0;
778 popup.num_rss = 0;
780 if(popup.msg_path) {
781 g_free(popup.msg_path);
782 popup.msg_path = NULL;
785 G_UNLOCK(trayicon_popup);
788 static void notification_trayicon_popup_free_func(gpointer data)
790 if(popup.msg_path) {
791 g_free(popup.msg_path);
792 popup.msg_path = NULL;
795 debug_print("Freed notification data\n");
798 static void notification_trayicon_popup_default_action_cb(NotifyNotification
799 *notification,
800 const char *action,
801 void *user_data)
803 if(strcmp("default", action))
804 return;
806 MainWindow *mainwin;
807 mainwin = mainwindow_get_mainwindow();
808 if(mainwin) {
809 NotificationFolderType nftype;
811 /* Let mainwindow pop up */
812 notification_show_mainwindow(mainwin);
813 /* If there is only one new mail message, jump to this message */
814 nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
815 if((popup.count == 1) && (nftype == F_TYPE_MAIL)) {
816 gchar *select_str;
817 G_LOCK(trayicon_popup);
818 select_str = g_strdup(popup.msg_path);
819 G_UNLOCK(trayicon_popup);
820 debug_print("Notification plugin: Select message %s\n", select_str);
821 mainwindow_jump_to(select_str, FALSE);
822 g_free(select_str);
827 static void notification_trayicon_popup_count_msgs(NotificationFolderType nftype)
829 switch(nftype) {
830 case F_TYPE_MAIL:
831 popup.num_mail++;
832 break;
833 case F_TYPE_NEWS:
834 popup.num_news++;
835 break;
836 case F_TYPE_CALENDAR:
837 popup.num_calendar++;
838 break;
839 case F_TYPE_RSS:
840 popup.num_rss++;
841 break;
842 default:
843 debug_print("Notification plugin: Unknown folder type\n");
844 return;
846 popup.count++;
849 /* The returned value has to be freed by the caller */
850 static gchar* notification_trayicon_popup_assemble_summary(void)
852 gchar *summary = NULL;
854 if(popup.count == 1) {
855 if(popup.num_mail)
856 summary = g_strdup(_("New mail message"));
857 else if(popup.num_news)
858 summary = g_strdup(_("New news post"));
859 else if(popup.num_calendar)
860 summary = g_strdup(_("New calendar message"));
861 else
862 summary = g_strdup(_("New article in RSS feed"));
863 } /* One new message */
864 else {
865 summary = g_strdup(_("New messages arrived"));
866 } /* Many new messages */
868 return summary;
871 /* The returned value has to be freed by the caller */
872 static gchar* notification_trayicon_popup_assemble_body(MsgInfo *msginfo)
874 gchar *utf8_str;
876 if(popup.count == 1) {
877 if(popup.num_mail || popup.num_news) {
878 gchar *from;
879 gchar *subj;
880 gchar *text;
881 gchar *foldname = NULL;
883 from = notification_libnotify_sanitize_str(msginfo->from ?
884 msginfo->from :
885 _("(No From)"));
886 subj = notification_libnotify_sanitize_str(msginfo->subject ?
887 msginfo->subject :
888 _("(No Subject)"));
889 if (notify_config.trayicon_display_folder_name) {
890 foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
891 text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
893 else
894 text = g_strconcat(from, "\n\n",subj, NULL);
897 /* Make sure text is valid UTF8 */
898 utf8_str = notification_validate_utf8_str(text);
900 g_free(text);
901 g_free(from);
902 g_free(subj);
903 if(foldname) g_free(foldname);
905 else if(popup.num_calendar) {
906 utf8_str = g_strdup(_("A new calendar message arrived"));
908 else {
909 utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
911 } /* One message */
913 else {
914 gchar *msg;
915 gchar *tmp;
916 gboolean str_empty = TRUE;
918 utf8_str = g_strdup("");
920 if(popup.num_mail) {
921 msg = g_strdup_printf(ngettext("%d new mail message arrived",
922 "%d new mail messages arrived",
923 popup.num_mail),
924 popup.num_mail);
925 tmp = g_strdup_printf("%s%s%s",utf8_str,"",msg);
926 g_free(msg);
927 g_free(utf8_str);
928 utf8_str = tmp;
929 str_empty = FALSE;
931 if(popup.num_news) {
932 msg = g_strdup_printf(ngettext("%d new news post arrived",
933 "%d new news posts arrived",
934 popup.num_news),
935 popup.num_news);
936 tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
937 g_free(msg);
938 g_free(utf8_str);
939 utf8_str = tmp;
940 str_empty = FALSE;
942 if(popup.num_calendar) {
943 msg = g_strdup_printf(ngettext("%d new calendar message arrived",
944 "%d new calendar messages arrived",
945 popup.num_calendar),
946 popup.num_calendar);
947 tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
948 g_free(msg);
949 g_free(utf8_str);
950 utf8_str = tmp;
951 str_empty = FALSE;
953 if(popup.num_rss) {
954 msg = g_strdup_printf(ngettext("%d new article in RSS feeds arrived",
955 "%d new articles in RSS feeds arrived",
956 popup.num_rss),
957 popup.num_rss);
958 tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
959 g_free(msg);
960 g_free(utf8_str);
961 utf8_str = tmp;
962 str_empty = FALSE;
964 } /* Many messages */
966 return utf8_str;
969 #endif /* HAVE_LIBNOTIFY */
971 gboolean notification_trayicon_is_available(void)
973 gboolean is_available;
974 is_available = FALSE;
976 if(trayicon) {
977 if(gtk_status_icon_is_embedded(trayicon) &&
978 gtk_status_icon_get_visible(trayicon))
979 is_available = TRUE;
982 return is_available;
985 #endif /* NOTIFICATION_TRAYICON */