68029584b4a6330834099db15c2925966208b4e1
[claws.git] / src / plugins / notification / notification_trayicon.c
1 /* Notification plugin for Claws-Mail
2  * Copyright (C) 2005-2007 Holger Berndt and the Claws Mail Team.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 /* This module is of course inspired by the trayicon plugin which is
19  * shipped with Claws-Mail, copyrighted by the Claws-Mail Team. */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #  include "claws-features.h"
24 #endif
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28
29 #ifdef NOTIFICATION_TRAYICON
30
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"
37
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
50
51 #include "gtk/manage_window.h"
52 #include "common/utils.h"
53 #include "gtk/gtkutils.h"
54
55 static GdkPixbuf* notification_trayicon_create(void);
56
57 static void notification_trayicon_on_popup_menu(GtkStatusIcon*,guint,
58                                                 guint,gpointer);
59 static gboolean notification_trayicon_on_size_changed(GtkStatusIcon*,
60                                                       gint, gpointer);
61
62 static void trayicon_get_all_cb(GtkAction*, gpointer);
63 static void trayicon_compose_cb(GtkAction*, gpointer);
64 static void trayicon_compose_acc_cb(GtkMenuItem*, gpointer);
65 static void trayicon_addressbook_cb(GtkAction*, gpointer);
66 static void trayicon_exit_cb(GtkAction*,gpointer);
67 static void trayicon_toggle_offline_cb(GtkAction*,gpointer);
68 #ifdef HAVE_LIBNOTIFY
69 static void trayicon_toggle_notify_cb(GtkAction*,gpointer);
70 #endif
71
72 #ifdef HAVE_LIBNOTIFY
73 #include <libnotify/notify.h>
74
75 #ifndef NOTIFY_CHECK_VERSION
76 # define NOTIFY_CHECK_VERSION(a,b,c) 0
77 #endif
78
79 typedef struct {
80   gint count;
81   gint num_mail;
82   gint num_news;
83   gint num_calendar;
84   gint num_rss;
85   gchar *msg_path;
86   NotifyNotification *notification;
87   GError *error;
88 } NotificationTrayiconPopup;
89
90 static NotificationTrayiconPopup popup;
91
92 static gboolean notification_trayicon_popup_add_msg(MsgInfo*,
93                                                     NotificationFolderType);
94 static gboolean notification_trayicon_popup_create(MsgInfo*,
95                                                    NotificationFolderType);
96 static void popup_timeout_fun(NotifyNotification*, gpointer);
97 static void notification_trayicon_popup_free_func(gpointer);
98 static void notification_trayicon_popup_default_action_cb(NotifyNotification*,
99                                                           const char*,void*);
100 static gchar* notification_trayicon_popup_assemble_summary(void);
101 static gchar* notification_trayicon_popup_assemble_body(MsgInfo*);
102 static void   notification_trayicon_popup_count_msgs(NotificationFolderType);
103
104 G_LOCK_DEFINE_STATIC(trayicon_popup);
105
106 #endif
107
108 static GtkStatusIcon *trayicon;
109 static gboolean updating_menu = FALSE;
110 static GtkWidget *traymenu_popup;
111 static GtkWidget *focused_widget = NULL;
112
113 static GtkActionEntry trayicon_popup_menu_entries[] = {
114         {"SysTrayiconPopup", NULL, "SysTrayiconPopup" },
115         {"SysTrayiconPopup/GetMail", NULL, N_("_Get Mail"), NULL, NULL, G_CALLBACK(trayicon_get_all_cb) },
116         {"SysTrayiconPopup/---", NULL, "---" },
117         {"SysTrayiconPopup/Email", NULL, N_("_Email"), NULL, NULL, G_CALLBACK(trayicon_compose_cb) },
118         {"SysTrayiconPopup/EmailAcc", NULL, N_("E_mail from account"), NULL, NULL, NULL },
119         {"SysTrayiconPopup/OpenAB", NULL, N_("Open A_ddressbook"), NULL, NULL, G_CALLBACK(trayicon_addressbook_cb) },
120         {"SysTrayiconPopup/Exit", NULL, N_("E_xit Claws Mail"), NULL, NULL, G_CALLBACK(trayicon_exit_cb) },
121 };
122
123 static GtkToggleActionEntry trayicon_popup_toggle_menu_entries[] =
124 {
125         {"SysTrayiconPopup/ToggleOffline",              NULL, N_("_Work Offline"), NULL, NULL, G_CALLBACK(trayicon_toggle_offline_cb) },
126 #ifdef HAVE_LIBNOTIFY
127         {"SysTrayiconPopup/ShowBubbles", NULL, N_("Show Trayicon Notifications"), NULL, NULL, G_CALLBACK(trayicon_toggle_notify_cb) },
128 #endif
129 };
130
131
132 void notification_trayicon_msg(MsgInfo *msginfo)
133 {
134 #ifndef HAVE_LIBNOTIFY
135   return;
136
137 #else
138   FolderType ftype;
139   NotificationFolderType nftype;
140   gchar *uistr;
141
142   nftype = F_TYPE_MAIL;
143
144   if(!msginfo || !notify_config.trayicon_enabled ||
145      !notify_config.trayicon_popup_enabled ||
146      !MSG_IS_NEW(msginfo->flags))
147     return;
148
149   if(notify_config.trayicon_folder_specific) {
150     guint id;
151     GSList *list;
152     gchar *identifier;
153     gboolean found = FALSE;
154
155     if(!(msginfo->folder))
156       return;
157
158     identifier = folder_item_get_identifier(msginfo->folder);
159
160     id =
161       notification_register_folder_specific_list
162       (TRAYICON_SPECIFIC_FOLDER_ID_STR);
163     list = notification_foldercheck_get_list(id);
164     for(; (list != NULL) && !found; list = g_slist_next(list)) {
165       gchar *list_identifier;
166       FolderItem *list_item = (FolderItem*) list->data;
167
168       list_identifier = folder_item_get_identifier(list_item);
169       if(!strcmp2(list_identifier, identifier))
170         found = TRUE;
171
172       g_free(list_identifier);
173     }
174     g_free(identifier);
175
176     if(!found)
177       return;
178   } /* folder specific */
179
180   ftype = msginfo->folder->folder->klass->type;
181
182   G_LOCK(trayicon_popup);
183   /* Check out which type to notify about */
184   switch(ftype) {
185   case F_MH:
186   case F_MBOX:
187   case F_MAILDIR:
188   case F_IMAP:
189     nftype = F_TYPE_MAIL;
190     break;
191   case F_NEWS:
192     nftype = F_TYPE_NEWS;
193     break;
194   case F_UNKNOWN:
195     if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
196       G_UNLOCK(trayicon_popup);
197       return;
198     }
199     else if(!strcmp(uistr, "vCalendar"))
200       nftype = F_TYPE_CALENDAR;
201     else if(!strcmp(uistr, "RSSyl"))
202       nftype = F_TYPE_RSS;
203     else {
204       debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
205       G_UNLOCK(trayicon_popup);
206       return;
207     }
208     break;
209   default:
210     debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
211     G_UNLOCK(trayicon_popup);
212     return;
213   }
214
215
216   notification_trayicon_popup_add_msg(msginfo, nftype);
217
218   G_UNLOCK(trayicon_popup);
219
220 #endif /* HAVE_LIBNOTIFY */
221 }
222
223 void notification_trayicon_destroy(void)
224 {
225   if(trayicon) {
226     gtk_status_icon_set_visible(trayicon, FALSE);
227     g_object_unref(trayicon);
228     trayicon = NULL;
229   }
230 }
231
232 void notification_update_trayicon()
233 {
234   gchar *buf;
235   static GdkPixbuf *old_icon = NULL;
236   GdkPixbuf *new_icon;
237   gint offset;
238   NotificationMsgCount count;
239   GSList *list;
240
241   if(!notify_config.trayicon_enabled)
242     return;
243
244   if(notify_config.trayicon_folder_specific) {
245     guint id;
246     id =
247       notification_register_folder_specific_list
248       (TRAYICON_SPECIFIC_FOLDER_ID_STR);
249     list = notification_foldercheck_get_list(id);
250   }
251   else
252     list = NULL;
253
254   notification_core_get_msg_count(list, &count);
255
256   if(!trayicon) {
257
258 #ifdef NOTIFICATION_HOTKEYS
259     notification_hotkeys_update_bindings();
260 #endif
261
262     old_icon = notification_trayicon_create();
263     if(!trayicon) {
264       debug_print("Notification plugin: Could not create trayicon\n");
265       return;
266     }
267   }
268
269   /* Tooltip */
270   buf = g_strdup_printf(_("New %d, Unread: %d, Total: %d"),
271                         count.new_msgs, count.unread_msgs,
272                         count.total_msgs);
273   gtk_status_icon_set_tooltip_text(trayicon, buf);
274
275   g_free(buf);
276
277   /* Pixmap */
278   (prefs_common_get_prefs()->work_offline) ? (offset = 1) : (offset = 0);
279
280   if((count.new_msgs > 0) && (count.unreadmarked_msgs > 0))
281     new_icon =
282       notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMARKEDMAIL+offset);
283   else if(count.new_msgs > 0)
284     new_icon =
285       notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMAIL+offset);
286   else if(count.unreadmarked_msgs > 0)
287     new_icon =
288       notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMARKEDMAIL+offset);
289   else if(count.unread_msgs > 0)
290     new_icon =
291       notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMAIL+offset);
292   else
293     new_icon =
294       notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL+offset);
295
296   if(new_icon != old_icon) {
297     gtk_status_icon_set_from_pixbuf(trayicon, new_icon);
298     old_icon = new_icon;
299   }
300 }
301
302 gboolean notification_trayicon_main_window_close(gpointer source, gpointer data)
303 {
304   if(!notify_config.trayicon_enabled)
305     return FALSE;
306
307   if(source) {
308     gboolean *close_allowed = (gboolean*)source;
309
310     if(notify_config.trayicon_close_to_tray) {
311       MainWindow *mainwin = mainwindow_get_mainwindow();
312
313       *close_allowed = FALSE;
314       if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))) {
315         focused_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window));
316         main_window_hide(mainwin);
317       }
318     }
319   }
320   return FALSE;
321 }
322
323 gboolean notification_trayicon_main_window_got_iconified(gpointer source,
324                                                          gpointer data)
325 {
326   MainWindow *mainwin = mainwindow_get_mainwindow();
327
328   if(!notify_config.trayicon_enabled)
329     return FALSE;
330
331   if(notify_config.trayicon_hide_when_iconified &&
332      mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))
333      && !gtk_window_get_skip_taskbar_hint(GTK_WINDOW(mainwin->window))) {
334     gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
335   }
336   return FALSE;
337 }
338
339 gboolean notification_trayicon_account_list_changed(gpointer source,
340                                                     gpointer data)
341 {
342   GList *cur_ac;
343         GtkWidget *menu, *submenu;
344   GtkWidget *menuitem;
345   PrefsAccount *ac_prefs;
346
347   GList *account_list = account_get_list();
348
349   if(!notify_config.trayicon_enabled)
350     return FALSE;
351
352         menu = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup/EmailAcc");
353         gtk_widget_show(menu);
354
355         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), NULL);
356         submenu = gtk_menu_new();
357
358   for(cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) {
359     ac_prefs = (PrefsAccount *)cur_ac->data;
360
361     menuitem = gtk_menu_item_new_with_label
362       (ac_prefs->account_name ? ac_prefs->account_name
363        : _("Untitled"));
364     gtk_widget_show(menuitem);
365                 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
366     g_signal_connect(G_OBJECT(menuitem), "activate",
367                      G_CALLBACK(trayicon_compose_acc_cb),
368                      ac_prefs);
369   }
370         gtk_widget_show(submenu);
371         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu);
372   return FALSE;
373 }
374
375 static GdkPixbuf* notification_trayicon_create(void)
376 {
377   GdkPixbuf *trayicon_nomail;
378         GtkActionGroup *action_group;
379
380   trayicon_nomail = notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL);
381
382   notification_trayicon_destroy();
383
384   trayicon = gtk_status_icon_new_from_pixbuf(trayicon_nomail);
385
386   g_signal_connect(G_OBJECT(trayicon), "activate",
387                    G_CALLBACK(notification_trayicon_on_activate), NULL);
388   g_signal_connect(G_OBJECT(trayicon), "popup-menu",
389                    G_CALLBACK(notification_trayicon_on_popup_menu), NULL);
390   g_signal_connect(G_OBJECT(trayicon), "size-changed",
391                    G_CALLBACK(notification_trayicon_on_size_changed), NULL);
392
393   /* Popup-Menu */
394         action_group = cm_menu_create_action_group("SysTrayiconPopup", trayicon_popup_menu_entries,
395                                                                                                                                                                                  G_N_ELEMENTS(trayicon_popup_menu_entries), NULL);
396         gtk_action_group_add_toggle_actions(action_group, trayicon_popup_toggle_menu_entries,
397                                                                                                                                                         G_N_ELEMENTS(trayicon_popup_toggle_menu_entries), NULL);
398
399         MENUITEM_ADDUI("/Menus", "SysTrayiconPopup", "SysTrayiconPopup", GTK_UI_MANAGER_MENU)
400         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "GetMail", "SysTrayiconPopup/GetMail", GTK_UI_MANAGER_MENUITEM)
401         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator1", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
402         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Email", "SysTrayiconPopup/Email", GTK_UI_MANAGER_MENUITEM)
403         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "EmailAcc", "SysTrayiconPopup/EmailAcc", GTK_UI_MANAGER_MENU)
404         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator2", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
405         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "OpenAB", "SysTrayiconPopup/OpenAB", GTK_UI_MANAGER_MENUITEM)
406         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator3", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
407         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ToggleOffline", "SysTrayiconPopup/ToggleOffline", GTK_UI_MANAGER_MENUITEM)
408 #ifdef HAVE_LIBNOTIFY
409         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ShowBubbles", "SysTrayiconPopup/ShowBubbles", GTK_UI_MANAGER_MENUITEM)
410 #endif
411         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator4", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
412         MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Exit", "SysTrayiconPopup/Exit", GTK_UI_MANAGER_MENUITEM)
413
414         traymenu_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
415                                 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup")));
416
417
418   return trayicon_nomail;
419 }
420
421 void notification_trayicon_on_activate(GtkStatusIcon *status_icon, gpointer user_data)
422 {
423   MainWindow *mainwin = mainwindow_get_mainwindow();
424
425   if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
426     focused_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window));
427
428   notification_toggle_hide_show_window();
429
430   if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
431     gtk_window_set_focus(GTK_WINDOW(mainwin->window), focused_widget);
432 }
433
434 static void notification_trayicon_on_popup_menu(GtkStatusIcon *status_icon,
435                                                 guint button, guint activate_time,
436                                                 gpointer user_data)
437 {
438   MainWindow *mainwin = mainwindow_get_mainwindow();
439
440   if(!mainwin)
441     return;
442
443   /* tell callbacks to skip any event */
444   updating_menu = TRUE;
445   /* initialize checkitems according to current states */
446         cm_toggle_menu_set_active("SysTrayiconPopup/ToggleOffline", prefs_common_get_prefs()->work_offline);
447 #ifdef HAVE_LIBNOTIFY
448         cm_toggle_menu_set_active("SysTrayiconPopup/ShowBubbles", notify_config.trayicon_popup_enabled);
449 #endif
450         cm_menu_set_sensitive("SysTrayiconPopup/GetMail", mainwin->lock_count == 0);
451
452   updating_menu = FALSE;
453
454   gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
455                  button, activate_time);
456 }
457
458 static gboolean notification_trayicon_on_size_changed(GtkStatusIcon *icon,
459                                                       gint size,
460                                                       gpointer user_data)
461 {
462   notification_update_msg_counts(NULL);
463   return FALSE;
464 }
465
466 /* popup menu callbacks */
467 static void trayicon_get_all_cb(GtkAction *action, gpointer data)
468 {
469   MainWindow *mainwin = mainwindow_get_mainwindow();
470   inc_all_account_mail_cb(mainwin, 0, NULL);
471 }
472
473 static void trayicon_compose_cb(GtkAction *action, gpointer data)
474 {
475   MainWindow *mainwin = mainwindow_get_mainwindow();
476   compose_mail_cb(mainwin, 0, NULL);
477 }
478
479 static void trayicon_compose_acc_cb(GtkMenuItem *menuitem, gpointer data)
480 {
481   compose_new((PrefsAccount *)data, NULL, NULL);
482 }
483
484 static void trayicon_addressbook_cb(GtkAction *action, gpointer data)
485 {
486 #ifndef USE_ALT_ADDRBOOK
487     addressbook_open(NULL);
488 #else
489     GError* error = NULL;
490
491     addressbook_dbus_open(FALSE, &error);
492     if (error) {
493         g_warning("%s", error->message);
494         g_error_free(error);
495     }
496 #endif
497 }
498
499 static void trayicon_toggle_offline_cb(GtkAction *action, gpointer data)
500 {
501   /* toggle offline mode if menu checkitem has been clicked */
502   if(!updating_menu) {
503     MainWindow *mainwin = mainwindow_get_mainwindow();
504     main_window_toggle_work_offline(mainwin, !prefs_common_get_prefs()->work_offline, TRUE);
505   }
506 }
507
508 #ifdef HAVE_LIBNOTIFY
509 static void trayicon_toggle_notify_cb(GtkAction *action, gpointer data)
510 {
511   if(!updating_menu) {
512     notify_config.trayicon_popup_enabled = !notify_config.trayicon_popup_enabled;
513   }
514 }
515 #endif
516
517 static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
518 {
519   if(prefs_common_get_prefs()->confirm_on_exit) {
520     if(alertpanel(_("Exit"), _("Exit Claws Mail?"),
521                   GTK_STOCK_CANCEL, GTK_STOCK_OK,
522                   NULL) != G_ALERTALTERNATE) {
523       return;
524     }
525     manage_window_focus_in(mainwin->window, NULL, NULL);
526   }
527
528   if (prefs_common_get_prefs()->clean_on_exit) {
529     if (!main_window_empty_trash(mainwin, prefs_common_get_prefs()->ask_on_clean, TRUE))
530       return;
531   }
532
533   app_will_exit(NULL, mainwin);
534 }
535
536 static void trayicon_exit_cb(GtkAction *action, gpointer data)
537 {
538   MainWindow *mainwin = mainwindow_get_mainwindow();
539
540   if(mainwin->lock_count == 0) {
541     app_exit_cb(mainwin, 0, NULL);
542   }
543 }
544
545 #ifdef HAVE_LIBNOTIFY
546 static gboolean notification_trayicon_popup_add_msg(MsgInfo *msginfo,
547                                                     NotificationFolderType nftype)
548 {
549   gchar *summary;
550   gchar *utf8_str;
551   gboolean retval;
552   GdkPixbuf *pixbuf;
553
554   g_return_val_if_fail(msginfo, FALSE);
555
556   if(!popup.notification)
557     return notification_trayicon_popup_create(msginfo,nftype);
558
559   /* Count messages */
560   notification_trayicon_popup_count_msgs(nftype);
561
562   if(popup.msg_path) {
563     g_free(popup.msg_path);
564     popup.msg_path = NULL;
565   }
566
567   summary  = notification_trayicon_popup_assemble_summary();
568   utf8_str = notification_trayicon_popup_assemble_body(msginfo);
569
570   /* make sure we show a logo on many msg arrival */
571   pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
572   if(pixbuf)
573     notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
574
575   retval = notify_notification_update(popup.notification, summary,
576                                       utf8_str, NULL);
577   g_free(summary);
578   g_free(utf8_str);
579   if(!retval) {
580     debug_print("Notification Plugin: Failed to update notification.\n");
581     return FALSE;
582   }
583   /* Show the popup */
584   notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
585   if(!notify_notification_show(popup.notification, &(popup.error))) {
586     debug_print("Notification Plugin: Failed to send updated notification: "
587                 "%s\n", popup.error->message);
588     g_clear_error(&(popup.error));
589     return FALSE;
590   }
591
592   debug_print("Notification Plugin: Popup successfully modified "
593               "with libnotify.\n");
594
595   return TRUE;
596 }
597
598 static gboolean notification_trayicon_popup_create(MsgInfo *msginfo,
599                                                    NotificationFolderType nftype)
600 {
601   gchar *summary = NULL;
602   gchar *utf8_str = NULL;
603   GdkPixbuf *pixbuf;
604   GList *caps = NULL;
605   gboolean support_actions = FALSE;
606
607   /* init libnotify if necessary */
608   if(!notify_is_initted()) {
609     if(!notify_init("claws-mail")) {
610       debug_print("Notification Plugin: Failed to initialize libnotify. "
611                   "No popups will be shown.\n");
612       return FALSE;
613     }
614   }
615
616   /* Count messages */
617   notification_trayicon_popup_count_msgs(nftype);
618
619   summary  = notification_trayicon_popup_assemble_summary();
620   utf8_str = notification_trayicon_popup_assemble_body(msginfo);
621
622 #if NOTIFY_CHECK_VERSION(0, 7, 0)
623   popup.notification = notify_notification_new(summary, utf8_str, NULL);
624 #else
625   popup.notification = notify_notification_new(summary, utf8_str, NULL, NULL);
626   notify_notification_attach_to_status_icon(popup.notification, trayicon);
627 #endif
628
629   g_free(summary);
630   g_free(utf8_str);
631
632   caps = notify_get_server_caps();
633     if(caps != NULL) {
634       GList *c;
635       for(c = caps; c != NULL; c = c->next) {
636         if(strcmp((char*)c->data, "actions") == 0 ) {
637           support_actions = TRUE;
638           break;
639         }
640       }
641
642     g_list_foreach(caps, (GFunc)g_free, NULL);
643     g_list_free(caps);
644   }
645
646   /* Default action */
647   if (support_actions)
648     notify_notification_add_action(popup.notification,
649                                    "default", _("Present main window"),
650                                    (NotifyActionCallback)
651                                    notification_trayicon_popup_default_action_cb,
652                                    GINT_TO_POINTER(nftype),
653                                    notification_trayicon_popup_free_func);
654
655   if(popup.notification == NULL) {
656     debug_print("Notification Plugin: Failed to create a new notification.\n");
657     return FALSE;
658   }
659
660   /* Icon */
661   pixbuf = NULL;
662 #ifndef USE_ALT_ADDRBOOK
663   if(msginfo && msginfo->from) {
664     gchar *icon_path;
665     icon_path = addrindex_get_picture_file(msginfo->from);
666     if(is_file_exist(icon_path)) {
667       GError *error = NULL;
668       gint w, h;
669
670       gdk_pixbuf_get_file_info(icon_path, &w, &h);
671       if((w > 64) || (h > 64))
672         pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
673                                                    64, 64, TRUE, &error);
674       else
675         pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
676
677       if(!pixbuf) {
678         debug_print("Could not load picture file: %s\n",
679                     error ? error->message : "no details");
680         g_error_free(error);
681       }
682     }
683     else
684       debug_print("Picture path does not exist: %s\n",icon_path);
685     g_free(icon_path);
686   }
687 #endif
688   if(!pixbuf)
689     pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
690
691   if(pixbuf) {
692     notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
693     g_object_unref(pixbuf);
694   }
695   else /* This is not fatal */
696     debug_print("Notification plugin: Icon could not be loaded.\n");
697
698   /* timeout */
699   notify_notification_set_timeout(popup.notification, notify_config.trayicon_popup_timeout);
700
701   /* Category */
702   notify_notification_set_category(popup.notification, "email.arrived");
703
704   /* get notified on bubble close */
705   g_signal_connect(G_OBJECT(popup.notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
706
707   /* Show the popup */
708   notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
709   if(!notify_notification_show(popup.notification, &(popup.error))) {
710     debug_print("Notification Plugin: Failed to send notification: %s\n",
711                 popup.error->message);
712     g_clear_error(&(popup.error));
713     g_object_unref(G_OBJECT(popup.notification));
714     popup.notification = NULL;
715     return FALSE;
716   }
717
718   /* Store path to message */
719   if(nftype == F_TYPE_MAIL) {
720     if(msginfo && msginfo->folder) {
721       gchar *ident;
722       ident = folder_item_get_identifier(msginfo->folder);
723       popup.msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
724                                        msginfo->msgnum);
725       g_free(ident);
726     }
727     else
728       popup.msg_path = NULL;
729   }
730
731   debug_print("Notification Plugin: Popup created with libnotify.\n");
732
733   return TRUE;
734 }
735
736 static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
737 {
738   G_LOCK(trayicon_popup);
739
740   g_object_unref(G_OBJECT(popup.notification));
741
742   popup.notification = NULL;
743   g_clear_error(&(popup.error));
744
745   popup.count = 0;
746   popup.num_mail = 0;
747   popup.num_news = 0;
748   popup.num_calendar = 0;
749   popup.num_rss = 0;
750
751   if(popup.msg_path) {
752     g_free(popup.msg_path);
753     popup.msg_path = NULL;
754   }
755
756   G_UNLOCK(trayicon_popup);
757 }
758
759 static void notification_trayicon_popup_free_func(gpointer data)
760 {
761   if(popup.msg_path) {
762     g_free(popup.msg_path);
763     popup.msg_path = NULL;
764   }
765
766   debug_print("Freed notification data\n");
767 }
768
769 static void notification_trayicon_popup_default_action_cb(NotifyNotification
770                                                           *notification,
771                                                           const char *action,
772                                                           void *user_data)
773 {
774   if(strcmp("default", action))
775     return;
776
777   MainWindow *mainwin;
778   mainwin = mainwindow_get_mainwindow();
779   if(mainwin) {
780     NotificationFolderType nftype;
781
782     /* Let mainwindow pop up */
783     notification_show_mainwindow(mainwin);
784     /* If there is only one new mail message, jump to this message */
785     nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
786     if((popup.count == 1) && (nftype == F_TYPE_MAIL)) {
787       gchar *select_str;
788       G_LOCK(trayicon_popup);
789       select_str = g_strdup(popup.msg_path);
790       G_UNLOCK(trayicon_popup);
791       debug_print("Notification plugin: Select message %s\n", select_str);
792       mainwindow_jump_to(select_str, FALSE);
793       g_free(select_str);
794     }
795   }
796 }
797
798 static void notification_trayicon_popup_count_msgs(NotificationFolderType nftype)
799 {
800   switch(nftype) {
801   case F_TYPE_MAIL:
802     popup.num_mail++;
803     break;
804   case F_TYPE_NEWS:
805     popup.num_news++;
806     break;
807   case F_TYPE_CALENDAR:
808     popup.num_calendar++;
809     break;
810   case F_TYPE_RSS:
811     popup.num_rss++;
812     break;
813   default:
814     debug_print("Notification plugin: Unknown folder type\n");
815     return;
816   }
817   popup.count++;
818 }
819
820 /* The returned value has to be freed by the caller */
821 static gchar* notification_trayicon_popup_assemble_summary(void)
822 {
823   gchar *summary = NULL;
824
825   if(popup.count == 1) {
826     if(popup.num_mail)
827       summary = g_strdup(_("New mail message"));
828     else if(popup.num_news)
829       summary = g_strdup(_("New news post"));
830     else if(popup.num_calendar)
831       summary = g_strdup(_("New calendar message"));
832     else
833       summary = g_strdup(_("New article in RSS feed"));
834   } /* One new message */
835   else {
836     summary = g_strdup(_("New messages arrived"));
837   } /* Many new messages */
838
839   return summary;
840 }
841
842 /* The returned value has to be freed by the caller */
843 static gchar* notification_trayicon_popup_assemble_body(MsgInfo *msginfo)
844 {
845   gchar *utf8_str;
846
847   if(popup.count == 1) {
848     if(popup.num_mail || popup.num_news) {
849       gchar *from;
850       gchar *subj;
851       gchar *text;
852           gchar *foldname = NULL;
853
854       from = notification_libnotify_sanitize_str(msginfo->from ?
855                                                  msginfo->from :
856                                                  _("(No From)"));
857       subj = notification_libnotify_sanitize_str(msginfo->subject ?
858                                                  msginfo->subject :
859                                                  _("(No Subject)"));
860         if (notify_config.trayicon_display_folder_name) {
861         foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
862         text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
863         }
864     else
865         text = g_strconcat(from, "\n\n",subj, NULL);
866
867
868       /* Make sure text is valid UTF8 */
869       utf8_str = notification_validate_utf8_str(text);
870       g_free(text);
871
872       if(from) g_free(from);
873       if(subj) g_free(subj);
874           if(foldname) g_free(foldname);
875     }
876     else if(popup.num_calendar) {
877       utf8_str = g_strdup(_("A new calendar message arrived"));
878     }
879     else {
880       utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
881     }
882   } /* One message */
883
884   else {
885     gchar *msg;
886     gchar *tmp;
887     gboolean str_empty = TRUE;
888
889     utf8_str = g_strdup("");
890
891     if(popup.num_mail) {
892       msg = g_strdup_printf(ngettext("%d new mail message arrived",
893                                      "%d new mail messages arrived",
894                             popup.num_mail),
895                             popup.num_mail);
896       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
897       g_free(msg);
898       g_free(utf8_str);
899       utf8_str = tmp;
900       str_empty = FALSE;
901     }
902     if(popup.num_news) {
903       msg = g_strdup_printf(ngettext("%d new news post arrived",
904                                      "%d new news posts arrived",
905                             popup.num_news),
906                             popup.num_news);
907       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
908       g_free(msg);
909       g_free(utf8_str);
910       utf8_str = tmp;
911       str_empty = FALSE;
912     }
913     if(popup.num_calendar) {
914       msg = g_strdup_printf(ngettext("%d new calendar message arrived",
915                                      "%d new calendar messages arrived",
916                             popup.num_calendar),
917                             popup.num_calendar);
918       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
919       g_free(msg);
920       g_free(utf8_str);
921       utf8_str = tmp;
922       str_empty = FALSE;
923     }
924     if(popup.num_rss) {
925       msg = g_strdup_printf(ngettext("%d new article in RSS feeds arrived",
926                                      "%d new articles in RSS feeds arrived",
927                             popup.num_rss),
928                             popup.num_rss);
929       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
930       g_free(msg);
931       g_free(utf8_str);
932       utf8_str = tmp;
933       str_empty = FALSE;
934     }
935   } /* Many messages */
936
937   return utf8_str;
938 }
939
940 #endif /* HAVE_LIBNOTIFY */
941
942 gboolean notification_trayicon_is_available(void)
943 {
944         gboolean is_available;
945         is_available = FALSE;
946
947         if(trayicon) {
948                 if(gtk_status_icon_is_embedded(trayicon) &&
949                          gtk_status_icon_get_visible(trayicon))
950                         is_available = TRUE;
951         }
952
953         return is_available;
954 }
955
956 #endif /* NOTIFICATION_TRAYICON */