Fixes Coverity CIDs: 1220388 1372362 1220222 1358399 1223756 1223757 1220305 1220269...
[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 #include "inc.h"
55
56 static void notification_trayicon_account_list_reset(const gchar *,
57                                                                                                                 gpointer,
58                                                                                                                 gboolean);
59
60 static GdkPixbuf* notification_trayicon_create(void);
61
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);
66
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
77
78 #ifdef HAVE_LIBNOTIFY
79 #include <libnotify/notify.h>
80
81 #ifndef NOTIFY_CHECK_VERSION
82 # define NOTIFY_CHECK_VERSION(a,b,c) 0
83 #endif
84
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;
95
96 static NotificationTrayiconPopup popup;
97
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);
109
110 G_LOCK_DEFINE_STATIC(trayicon_popup);
111
112 #endif
113
114 static GtkStatusIcon *trayicon;
115 static gboolean updating_menu = FALSE;
116 static GtkWidget *traymenu_popup;
117 static GtkWidget *focused_widget = NULL;
118
119 static GtkActionEntry trayicon_popup_menu_entries[] = {
120         {"SysTrayiconPopup", NULL, "SysTrayiconPopup" },
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, "---" },
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) },
128 };
129
130 static GtkToggleActionEntry trayicon_popup_toggle_menu_entries[] =
131 {
132         {"SysTrayiconPopup/ToggleOffline",              NULL, N_("_Work Offline"), NULL, NULL, G_CALLBACK(trayicon_toggle_offline_cb) },
133 #ifdef HAVE_LIBNOTIFY
134         {"SysTrayiconPopup/ShowBubbles", NULL, N_("Show Trayicon Notifications"), NULL, NULL, G_CALLBACK(trayicon_toggle_notify_cb) },
135 #endif
136 };
137
138
139 void notification_trayicon_msg(MsgInfo *msginfo)
140 {
141 #ifndef HAVE_LIBNOTIFY
142   return;
143
144 #else
145   FolderType ftype;
146   NotificationFolderType nftype;
147   gchar *uistr;
148
149   nftype = F_TYPE_MAIL;
150
151   if(!msginfo || !notify_config.trayicon_enabled ||
152      !notify_config.trayicon_popup_enabled ||
153      !MSG_IS_NEW(msginfo->flags))
154     return;
155
156   if(notify_config.trayicon_folder_specific) {
157     guint id;
158     GSList *list;
159     gchar *identifier;
160     gboolean found = FALSE;
161
162     if(!(msginfo->folder))
163       return;
164
165     identifier = folder_item_get_identifier(msginfo->folder);
166
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;
174
175       list_identifier = folder_item_get_identifier(list_item);
176       if(!strcmp2(list_identifier, identifier))
177         found = TRUE;
178
179       g_free(list_identifier);
180     }
181     g_free(identifier);
182
183     if(!found)
184       return;
185   } /* folder specific */
186
187   ftype = msginfo->folder->folder->klass->type;
188
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;
205     }
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;
214     }
215     break;
216   default:
217     debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
218     G_UNLOCK(trayicon_popup);
219     return;
220   }
221
222
223   notification_trayicon_popup_add_msg(msginfo, nftype);
224
225   G_UNLOCK(trayicon_popup);
226
227 #endif /* HAVE_LIBNOTIFY */
228 }
229
230 void notification_trayicon_destroy(void)
231 {
232   if(trayicon) {
233     gtk_status_icon_set_visible(trayicon, FALSE);
234     g_object_unref(trayicon);
235     trayicon = NULL;
236   }
237 }
238
239 void notification_update_trayicon()
240 {
241   gchar *buf;
242   static GdkPixbuf *old_icon = NULL;
243   GdkPixbuf *new_icon;
244   gint offset;
245   NotificationMsgCount count;
246   GSList *list;
247
248   if(!notify_config.trayicon_enabled)
249     return;
250
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);
257   }
258   else
259     list = NULL;
260
261   notification_core_get_msg_count(list, &count);
262
263   if(!trayicon) {
264
265 #ifdef NOTIFICATION_HOTKEYS
266     notification_hotkeys_update_bindings();
267 #endif
268
269     old_icon = notification_trayicon_create();
270     if(!trayicon) {
271       debug_print("Notification plugin: Could not create trayicon\n");
272       return;
273     }
274   }
275
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);
281
282   g_free(buf);
283
284   /* Pixmap */
285   (prefs_common_get_prefs()->work_offline) ? (offset = 1) : (offset = 0);
286
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);
302
303   if(new_icon != old_icon) {
304     gtk_status_icon_set_from_pixbuf(trayicon, new_icon);
305     old_icon = new_icon;
306   }
307 }
308
309 gboolean notification_trayicon_main_window_close(gpointer source, gpointer data)
310 {
311   if(!notify_config.trayicon_enabled)
312     return FALSE;
313
314   if(source) {
315     gboolean *close_allowed = (gboolean*)source;
316
317     if(notify_config.trayicon_close_to_tray) {
318       MainWindow *mainwin = mainwindow_get_mainwindow();
319
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);
324       }
325     }
326   }
327   return FALSE;
328 }
329
330 gboolean notification_trayicon_main_window_got_iconified(gpointer source,
331                                                          gpointer data)
332 {
333   MainWindow *mainwin = mainwindow_get_mainwindow();
334
335   if(!notify_config.trayicon_enabled)
336     return FALSE;
337
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);
342   }
343   return FALSE;
344 }
345
346 static void notification_trayicon_account_list_reset(const gchar *menuname,
347                                                                                                         gpointer callback,
348                                                                                                         gboolean receive)
349 {
350     GList *cur_ac;
351         GtkWidget *menu, *submenu;
352     GtkWidget *menuitem;
353     PrefsAccount *ac_prefs;
354
355     GList *account_list = account_get_list();
356
357         menu = gtk_ui_manager_get_widget(gtkut_ui_manager(), menuname);
358         gtk_widget_show(menu);
359
360         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), NULL);
361         submenu = gtk_menu_new();
362
363         for(cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) {
364                 ac_prefs = (PrefsAccount *)cur_ac->data;
365
366                 /* accounts list for receiving: skip SMTP-only accounts */
367                 if (receive && ac_prefs->protocol == A_NONE)
368                         continue;
369
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);
378         }
379         gtk_widget_show(submenu);
380         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu);
381 }
382
383 gboolean notification_trayicon_account_list_changed(gpointer source,
384                                                     gpointer data)
385 {
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);
391         }
392         return FALSE;
393 }
394
395 static GdkPixbuf* notification_trayicon_create(void)
396 {
397   GdkPixbuf *trayicon_nomail;
398         GtkActionGroup *action_group;
399
400   trayicon_nomail = notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL);
401
402   notification_trayicon_destroy();
403
404   trayicon = gtk_status_icon_new_from_pixbuf(trayicon_nomail);
405
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);
412
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);
418
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)
434
435         traymenu_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
436                                 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup")));
437
438
439   return trayicon_nomail;
440 }
441
442 void notification_trayicon_on_activate(GtkStatusIcon *status_icon, gpointer user_data)
443 {
444   MainWindow *mainwin = mainwindow_get_mainwindow();
445
446   if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
447     focused_widget = gtk_window_get_focus(GTK_WINDOW(mainwin->window));
448
449   notification_toggle_hide_show_window();
450
451   if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)) == TRUE)
452     gtk_window_set_focus(GTK_WINDOW(mainwin->window), focused_widget);
453 }
454
455 static void notification_trayicon_on_popup_menu(GtkStatusIcon *status_icon,
456                                                 guint button, guint activate_time,
457                                                 gpointer user_data)
458 {
459   MainWindow *mainwin = mainwindow_get_mainwindow();
460
461   if(!mainwin)
462     return;
463
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
473   updating_menu = FALSE;
474
475   gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
476                  button, activate_time);
477 }
478
479 static gboolean notification_trayicon_on_size_changed(GtkStatusIcon *icon,
480                                                       gint size,
481                                                       gpointer user_data)
482 {
483   notification_update_msg_counts(NULL);
484   return FALSE;
485 }
486
487 /* popup menu callbacks */
488 static void trayicon_get_all_cb(GtkAction *action, gpointer data)
489 {
490   MainWindow *mainwin = mainwindow_get_mainwindow();
491   inc_all_account_mail_cb(mainwin, 0, NULL);
492 }
493
494 static void trayicon_get_from_account_cb(GtkAction *action, gpointer data)
495 {
496   MainWindow *mainwin = mainwindow_get_mainwindow();
497   PrefsAccount *account = (PrefsAccount *)data;
498   inc_account_mail(mainwin, account);
499 }
500
501 static void trayicon_compose_cb(GtkAction *action, gpointer data)
502 {
503   MainWindow *mainwin = mainwindow_get_mainwindow();
504   compose_mail_cb(mainwin, 0, NULL);
505 }
506
507 static void trayicon_compose_acc_cb(GtkMenuItem *menuitem, gpointer data)
508 {
509   compose_new((PrefsAccount *)data, NULL, NULL);
510 }
511
512 static void trayicon_addressbook_cb(GtkAction *action, gpointer data)
513 {
514 #ifndef USE_ALT_ADDRBOOK
515     addressbook_open(NULL);
516 #else
517     GError* error = NULL;
518
519     addressbook_dbus_open(FALSE, &error);
520     if (error) {
521         g_warning("%s", error->message);
522         g_error_free(error);
523     }
524 #endif
525 }
526
527 static void trayicon_toggle_offline_cb(GtkAction *action, gpointer data)
528 {
529   /* toggle offline mode if menu checkitem has been clicked */
530   if(!updating_menu) {
531     MainWindow *mainwin = mainwindow_get_mainwindow();
532     main_window_toggle_work_offline(mainwin, !prefs_common_get_prefs()->work_offline, TRUE);
533   }
534 }
535
536 #ifdef HAVE_LIBNOTIFY
537 static void trayicon_toggle_notify_cb(GtkAction *action, gpointer data)
538 {
539   if(!updating_menu) {
540     notify_config.trayicon_popup_enabled = !notify_config.trayicon_popup_enabled;
541   }
542 }
543 #endif
544
545 static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
546 {
547   if(prefs_common_get_prefs()->confirm_on_exit) {
548     if(alertpanel(_("Exit"), _("Exit Claws Mail?"),
549                   GTK_STOCK_CANCEL, GTK_STOCK_OK,
550                   NULL) != G_ALERTALTERNATE) {
551       return;
552     }
553     manage_window_focus_in(mainwin->window, NULL, NULL);
554   }
555
556   if (prefs_common_get_prefs()->clean_on_exit) {
557     if (!main_window_empty_trash(mainwin, prefs_common_get_prefs()->ask_on_clean, TRUE))
558       return;
559   }
560
561   app_will_exit(NULL, mainwin);
562 }
563
564 static void trayicon_exit_cb(GtkAction *action, gpointer data)
565 {
566   MainWindow *mainwin = mainwindow_get_mainwindow();
567
568   if(mainwin->lock_count == 0) {
569     app_exit_cb(mainwin, 0, NULL);
570   }
571 }
572
573 #ifdef HAVE_LIBNOTIFY
574 static gboolean notification_trayicon_popup_add_msg(MsgInfo *msginfo,
575                                                     NotificationFolderType nftype)
576 {
577   gchar *summary;
578   gchar *utf8_str;
579   gboolean retval;
580   GdkPixbuf *pixbuf;
581
582   g_return_val_if_fail(msginfo, FALSE);
583
584   if(!popup.notification)
585     return notification_trayicon_popup_create(msginfo,nftype);
586
587   /* Count messages */
588   notification_trayicon_popup_count_msgs(nftype);
589
590   if(popup.msg_path) {
591     g_free(popup.msg_path);
592     popup.msg_path = NULL;
593   }
594
595   summary  = notification_trayicon_popup_assemble_summary();
596   utf8_str = notification_trayicon_popup_assemble_body(msginfo);
597
598   /* make sure we show a logo on many msg arrival */
599   pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
600   if(pixbuf)
601     notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
602
603   retval = notify_notification_update(popup.notification, summary,
604                                       utf8_str, NULL);
605   g_free(summary);
606   g_free(utf8_str);
607   if(!retval) {
608     debug_print("Notification Plugin: Failed to update notification.\n");
609     return FALSE;
610   }
611   /* Show the popup */
612   notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
613   if(!notify_notification_show(popup.notification, &(popup.error))) {
614     debug_print("Notification Plugin: Failed to send updated notification: "
615                 "%s\n", popup.error->message);
616     g_clear_error(&(popup.error));
617     return FALSE;
618   }
619
620   debug_print("Notification Plugin: Popup successfully modified "
621               "with libnotify.\n");
622
623   return TRUE;
624 }
625
626 static gboolean notification_trayicon_popup_create(MsgInfo *msginfo,
627                                                    NotificationFolderType nftype)
628 {
629   gchar *summary = NULL;
630   gchar *utf8_str = NULL;
631   GdkPixbuf *pixbuf;
632   GList *caps = NULL;
633   gboolean support_actions = FALSE;
634
635   /* init libnotify if necessary */
636   if(!notify_is_initted()) {
637     if(!notify_init("claws-mail")) {
638       debug_print("Notification Plugin: Failed to initialize libnotify. "
639                   "No popups will be shown.\n");
640       return FALSE;
641     }
642   }
643
644   /* Count messages */
645   notification_trayicon_popup_count_msgs(nftype);
646
647   summary  = notification_trayicon_popup_assemble_summary();
648   utf8_str = notification_trayicon_popup_assemble_body(msginfo);
649
650 #if NOTIFY_CHECK_VERSION(0, 7, 0)
651   popup.notification = notify_notification_new(summary, utf8_str, NULL);
652 #else
653   popup.notification = notify_notification_new(summary, utf8_str, NULL, NULL);
654   notify_notification_attach_to_status_icon(popup.notification, trayicon);
655 #endif
656
657   g_free(summary);
658   g_free(utf8_str);
659
660   caps = notify_get_server_caps();
661     if(caps != NULL) {
662       GList *c;
663       for(c = caps; c != NULL; c = c->next) {
664         if(strcmp((char*)c->data, "actions") == 0 ) {
665           support_actions = TRUE;
666           break;
667         }
668       }
669
670     g_list_foreach(caps, (GFunc)g_free, NULL);
671     g_list_free(caps);
672   }
673
674   /* Default action */
675   if (support_actions)
676     notify_notification_add_action(popup.notification,
677                                    "default", _("Present main window"),
678                                    (NotifyActionCallback)
679                                    notification_trayicon_popup_default_action_cb,
680                                    GINT_TO_POINTER(nftype),
681                                    notification_trayicon_popup_free_func);
682
683   if(popup.notification == NULL) {
684     debug_print("Notification Plugin: Failed to create a new notification.\n");
685     return FALSE;
686   }
687
688   /* Icon */
689   pixbuf = NULL;
690 #ifndef USE_ALT_ADDRBOOK
691   if(msginfo && msginfo->from) {
692     gchar *icon_path;
693     icon_path = addrindex_get_picture_file(msginfo->from);
694     if(is_file_exist(icon_path)) {
695       GError *error = NULL;
696       gint w, h;
697
698       gdk_pixbuf_get_file_info(icon_path, &w, &h);
699       if((w > 64) || (h > 64))
700         pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
701                                                    64, 64, TRUE, &error);
702       else
703         pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
704
705       if(!pixbuf) {
706         debug_print("Could not load picture file: %s\n",
707                     error ? error->message : "no details");
708         g_error_free(error);
709       }
710     }
711     else
712       debug_print("Picture path does not exist: %s\n",icon_path);
713     g_free(icon_path);
714   }
715 #endif
716   if(!pixbuf)
717     pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
718
719   if(pixbuf) {
720     notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
721     g_object_unref(pixbuf);
722   }
723   else /* This is not fatal */
724     debug_print("Notification plugin: Icon could not be loaded.\n");
725
726   /* timeout */
727   notify_notification_set_timeout(popup.notification, notify_config.trayicon_popup_timeout);
728
729   /* Category */
730   notify_notification_set_category(popup.notification, "email.arrived");
731
732   /* get notified on bubble close */
733   g_signal_connect(G_OBJECT(popup.notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
734
735   /* Show the popup */
736   notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
737   if(!notify_notification_show(popup.notification, &(popup.error))) {
738     debug_print("Notification Plugin: Failed to send notification: %s\n",
739                 popup.error->message);
740     g_clear_error(&(popup.error));
741     g_object_unref(G_OBJECT(popup.notification));
742     popup.notification = NULL;
743     return FALSE;
744   }
745
746   /* Store path to message */
747   if(nftype == F_TYPE_MAIL) {
748     if(msginfo && msginfo->folder) {
749       gchar *ident;
750       ident = folder_item_get_identifier(msginfo->folder);
751       popup.msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
752                                        msginfo->msgnum);
753       g_free(ident);
754     }
755     else
756       popup.msg_path = NULL;
757   }
758
759   debug_print("Notification Plugin: Popup created with libnotify.\n");
760
761   return TRUE;
762 }
763
764 static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
765 {
766   G_LOCK(trayicon_popup);
767
768   g_object_unref(G_OBJECT(popup.notification));
769
770   popup.notification = NULL;
771   g_clear_error(&(popup.error));
772
773   popup.count = 0;
774   popup.num_mail = 0;
775   popup.num_news = 0;
776   popup.num_calendar = 0;
777   popup.num_rss = 0;
778
779   if(popup.msg_path) {
780     g_free(popup.msg_path);
781     popup.msg_path = NULL;
782   }
783
784   G_UNLOCK(trayicon_popup);
785 }
786
787 static void notification_trayicon_popup_free_func(gpointer data)
788 {
789   if(popup.msg_path) {
790     g_free(popup.msg_path);
791     popup.msg_path = NULL;
792   }
793
794   debug_print("Freed notification data\n");
795 }
796
797 static void notification_trayicon_popup_default_action_cb(NotifyNotification
798                                                           *notification,
799                                                           const char *action,
800                                                           void *user_data)
801 {
802   if(strcmp("default", action))
803     return;
804
805   MainWindow *mainwin;
806   mainwin = mainwindow_get_mainwindow();
807   if(mainwin) {
808     NotificationFolderType nftype;
809
810     /* Let mainwindow pop up */
811     notification_show_mainwindow(mainwin);
812     /* If there is only one new mail message, jump to this message */
813     nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
814     if((popup.count == 1) && (nftype == F_TYPE_MAIL)) {
815       gchar *select_str;
816       G_LOCK(trayicon_popup);
817       select_str = g_strdup(popup.msg_path);
818       G_UNLOCK(trayicon_popup);
819       debug_print("Notification plugin: Select message %s\n", select_str);
820       mainwindow_jump_to(select_str, FALSE);
821       g_free(select_str);
822     }
823   }
824 }
825
826 static void notification_trayicon_popup_count_msgs(NotificationFolderType nftype)
827 {
828   switch(nftype) {
829   case F_TYPE_MAIL:
830     popup.num_mail++;
831     break;
832   case F_TYPE_NEWS:
833     popup.num_news++;
834     break;
835   case F_TYPE_CALENDAR:
836     popup.num_calendar++;
837     break;
838   case F_TYPE_RSS:
839     popup.num_rss++;
840     break;
841   default:
842     debug_print("Notification plugin: Unknown folder type\n");
843     return;
844   }
845   popup.count++;
846 }
847
848 /* The returned value has to be freed by the caller */
849 static gchar* notification_trayicon_popup_assemble_summary(void)
850 {
851   gchar *summary = NULL;
852
853   if(popup.count == 1) {
854     if(popup.num_mail)
855       summary = g_strdup(_("New mail message"));
856     else if(popup.num_news)
857       summary = g_strdup(_("New news post"));
858     else if(popup.num_calendar)
859       summary = g_strdup(_("New calendar message"));
860     else
861       summary = g_strdup(_("New article in RSS feed"));
862   } /* One new message */
863   else {
864     summary = g_strdup(_("New messages arrived"));
865   } /* Many new messages */
866
867   return summary;
868 }
869
870 /* The returned value has to be freed by the caller */
871 static gchar* notification_trayicon_popup_assemble_body(MsgInfo *msginfo)
872 {
873   gchar *utf8_str;
874
875   if(popup.count == 1) {
876     if(popup.num_mail || popup.num_news) {
877       gchar *from;
878       gchar *subj;
879       gchar *text;
880           gchar *foldname = NULL;
881
882       from = notification_libnotify_sanitize_str(msginfo->from ?
883                                                  msginfo->from :
884                                                  _("(No From)"));
885       subj = notification_libnotify_sanitize_str(msginfo->subject ?
886                                                  msginfo->subject :
887                                                  _("(No Subject)"));
888         if (notify_config.trayicon_display_folder_name) {
889         foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
890         text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
891         }
892     else
893         text = g_strconcat(from, "\n\n",subj, NULL);
894
895
896       /* Make sure text is valid UTF8 */
897       utf8_str = notification_validate_utf8_str(text);
898       g_free(text);
899
900       if(from) g_free(from);
901       if(subj) g_free(subj);
902           if(foldname) g_free(foldname);
903     }
904     else if(popup.num_calendar) {
905       utf8_str = g_strdup(_("A new calendar message arrived"));
906     }
907     else {
908       utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
909     }
910   } /* One message */
911
912   else {
913     gchar *msg;
914     gchar *tmp;
915     gboolean str_empty = TRUE;
916
917     utf8_str = g_strdup("");
918
919     if(popup.num_mail) {
920       msg = g_strdup_printf(ngettext("%d new mail message arrived",
921                                      "%d new mail messages arrived",
922                             popup.num_mail),
923                             popup.num_mail);
924       tmp = g_strdup_printf("%s%s%s",utf8_str,"",msg);
925       g_free(msg);
926       g_free(utf8_str);
927       utf8_str = tmp;
928       str_empty = FALSE;
929     }
930     if(popup.num_news) {
931       msg = g_strdup_printf(ngettext("%d new news post arrived",
932                                      "%d new news posts arrived",
933                             popup.num_news),
934                             popup.num_news);
935       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
936       g_free(msg);
937       g_free(utf8_str);
938       utf8_str = tmp;
939       str_empty = FALSE;
940     }
941     if(popup.num_calendar) {
942       msg = g_strdup_printf(ngettext("%d new calendar message arrived",
943                                      "%d new calendar messages arrived",
944                             popup.num_calendar),
945                             popup.num_calendar);
946       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
947       g_free(msg);
948       g_free(utf8_str);
949       utf8_str = tmp;
950       str_empty = FALSE;
951     }
952     if(popup.num_rss) {
953       msg = g_strdup_printf(ngettext("%d new article in RSS feeds arrived",
954                                      "%d new articles in RSS feeds arrived",
955                             popup.num_rss),
956                             popup.num_rss);
957       tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
958       g_free(msg);
959       g_free(utf8_str);
960       utf8_str = tmp;
961       str_empty = FALSE;
962     }
963   } /* Many messages */
964
965   return utf8_str;
966 }
967
968 #endif /* HAVE_LIBNOTIFY */
969
970 gboolean notification_trayicon_is_available(void)
971 {
972         gboolean is_available;
973         is_available = FALSE;
974
975         if(trayicon) {
976                 if(gtk_status_icon_is_embedded(trayicon) &&
977                          gtk_status_icon_get_visible(trayicon))
978                         is_available = TRUE;
979         }
980
981         return is_available;
982 }
983
984 #endif /* NOTIFICATION_TRAYICON */