1 /* Notification plugin for Claws-Mail
2 * Copyright (C) 2005-2007 Holger Berndt
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/>.
20 # include "claws-features.h"
24 #include <glib/gi18n.h>
26 #ifdef NOTIFICATION_POPUP
30 #include "mainwindow.h"
33 #ifndef USE_NEW_ADDRBOOK
34 #include "addrindex.h"
36 #include "common/utils.h"
37 #include "gtk/gtkutils.h"
39 #include "notification_popup.h"
40 #include "notification_prefs.h"
41 #include "notification_foldercheck.h"
42 #include "notification_pixbuf.h"
43 #include "notification_core.h"
46 # include <libnotify/notify.h>
49 #ifndef NOTIFY_CHECK_VERSION
50 # define NOTIFY_CHECK_VERSION(a,b,c) 0
57 NotifyNotification *notification;
59 #else /* !HAVE_LIBNOTIFY */
70 G_LOCK_DEFINE_STATIC(popup);
73 static NotificationPopup popup[F_TYPE_LAST];
75 static void popup_timeout_fun(NotifyNotification*, gpointer data);
77 static gboolean notification_libnotify_create(MsgInfo*, NotificationFolderType);
78 static gboolean notification_libnotify_add_msg(MsgInfo*, NotificationFolderType);
79 static void default_action_cb(NotifyNotification*, const char*,void*);
80 static void notification_libnotify_free_func(gpointer);
83 static NotificationPopup popup;
85 static gboolean popup_timeout_fun(gpointer data);
87 static gboolean notification_popup_add_msg(MsgInfo*);
88 static gboolean notification_popup_create(MsgInfo*);
89 static gboolean notification_popup_button(GtkWidget*, GdkEventButton*, gpointer);
93 void notification_popup_msg(MsgInfo *msginfo)
97 NotificationPopup *ppopup;
101 NotificationFolderType nftype;
103 nftype = F_TYPE_MAIL;
105 if(!msginfo || !notify_config.popup_show)
108 if(notify_config.popup_folder_specific) {
112 gboolean found = FALSE;
114 if(!(msginfo->folder))
117 identifier = folder_item_get_identifier(msginfo->folder);
120 notification_register_folder_specific_list(POPUP_SPECIFIC_FOLDER_ID_STR);
121 list = notification_foldercheck_get_list(id);
122 for(; (list != NULL) && !found; list = g_slist_next(list)) {
123 gchar *list_identifier;
124 FolderItem *list_item = (FolderItem*) list->data;
126 list_identifier = folder_item_get_identifier(list_item);
127 if(!strcmp2(list_identifier, identifier))
130 g_free(list_identifier);
138 ftype = msginfo->folder->folder->klass->type;
141 #ifdef HAVE_LIBNOTIFY
142 /* Check out which type to notify about */
148 nftype = F_TYPE_MAIL;
151 nftype = F_TYPE_NEWS;
154 if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
158 else if(!strcmp(uistr, "vCalendar"))
159 nftype = F_TYPE_CALENDAR;
160 else if(!strcmp(uistr, "RSSyl"))
163 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
169 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
174 ppopup = &(popup[nftype]);
175 retval = notification_libnotify_add_msg(msginfo, nftype);
176 #else /* !HAVE_LIBNOTIFY */
178 retval = notification_popup_add_msg(msginfo);
180 /* Renew timeout only when the above call was successful */
182 if(ppopup->timeout_id)
183 g_source_remove(ppopup->timeout_id);
184 ppopup->timeout_id = g_timeout_add(notify_config.popup_timeout,
186 GINT_TO_POINTER(nftype));
189 #endif /* !HAVE_LIBNOTIFY */
193 #ifndef HAVE_LIBNOTIFY
195 while(gtk_events_pending())
196 gtk_main_iteration();
197 #endif /* !HAVE_LIBNOTIFY */
201 #ifdef HAVE_LIBNOTIFY
202 static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
204 NotificationPopup *ppopup;
205 NotificationFolderType nftype;
207 nftype = GPOINTER_TO_INT(data);
211 ppopup = &(popup[nftype]);
213 g_object_unref(G_OBJECT(ppopup->notification));
214 ppopup->notification = NULL;
215 g_clear_error(&(ppopup->error));
217 if(ppopup->msg_path) {
218 g_free(ppopup->msg_path);
219 ppopup->msg_path = NULL;
223 debug_print("Notification Plugin: Popup closed due to timeout.\n");
227 static gboolean popup_timeout_fun(gpointer data)
229 NotificationPopup *ppopup;
230 NotificationFolderType nftype;
232 nftype = GPOINTER_TO_INT(data);
238 gtk_widget_destroy(ppopup->window);
239 ppopup->window = NULL;
241 ppopup->timeout_id = 0;
243 if(ppopup->msg_path) {
244 g_free(ppopup->msg_path);
245 ppopup->msg_path = NULL;
249 debug_print("Notification Plugin: Popup closed due to timeout.\n");
254 #ifdef HAVE_LIBNOTIFY
255 static void default_action_cb(NotifyNotification *notification,
259 if(strcmp("default", action))
263 mainwin = mainwindow_get_mainwindow();
265 NotificationFolderType nftype;
267 /* Let mainwindow pop up */
268 notification_show_mainwindow(mainwin);
269 /* If there is only one new mail message, jump to this message */
270 nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
271 if(nftype == F_TYPE_MAIL) {
272 if(popup[F_TYPE_MAIL].count == 1) {
275 select_str = g_strdup(popup[F_TYPE_MAIL].msg_path);
277 debug_print("Select message %s\n", select_str);
278 mainwindow_jump_to(select_str, FALSE);
285 static gboolean notification_libnotify_create(MsgInfo *msginfo,
286 NotificationFolderType nftype)
289 NotificationPopup *ppopup;
290 gchar *summary = NULL;
292 gchar *utf8_str = NULL;
295 gchar *foldname = NULL;
297 gboolean support_actions = FALSE;
299 g_return_val_if_fail(msginfo, FALSE);
301 ppopup = &(popup[nftype]);
303 /* init libnotify if necessary */
304 if(!notify_is_initted()) {
305 if(!notify_init("claws-mail")) {
306 debug_print("Notification Plugin: Failed to initialize libnotify. "
307 "No popup will be shown.\n");
314 summary = _("New Mail message");
315 from = notification_libnotify_sanitize_str(msginfo->from ?
316 msginfo->from : _("(No From)"));
317 subj = notification_libnotify_sanitize_str(msginfo->subject ?
318 msginfo->subject : _("(No Subject)"));
319 if (notify_config.popup_display_folder_name) {
320 foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
321 text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
324 text = g_strconcat(from, "\n\n",subj, NULL);
326 /* Make sure text is valid UTF8 */
327 utf8_str = notification_validate_utf8_str(text);
330 if(from) g_free(from);
331 if(subj) g_free(subj);
332 if(foldname) g_free(foldname);
335 summary = _("New News post");
336 utf8_str = g_strdup(_("A new message arrived"));
338 case F_TYPE_CALENDAR:
339 summary = _("New Calendar message");
340 utf8_str = g_strdup(_("A new calendar message arrived"));
343 summary = _("New RSS feed article");
344 utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
347 summary = _("New unknown message");
348 utf8_str = g_strdup(_("Unknown message type arrived"));
352 ppopup->notification = notify_notification_new(summary, utf8_str, NULL
353 #if !NOTIFY_CHECK_VERSION(0, 7, 0)
358 if(ppopup->notification == NULL) {
359 debug_print("Notification Plugin: Failed to create a new "
364 caps = notify_get_server_caps();
367 for(c = caps; c != NULL; c = c->next) {
368 if(strcmp((char*)c->data, "actions") == 0 ) {
369 support_actions = TRUE;
374 g_list_foreach(caps, (GFunc)g_free, NULL);
380 notify_notification_add_action(ppopup->notification,
381 "default", _("Present main window"),
382 (NotifyActionCallback)default_action_cb,
383 GINT_TO_POINTER(nftype),
384 notification_libnotify_free_func);
388 #ifndef USE_NEW_ADDRBOOK
389 if(msginfo && msginfo->from) {
391 icon_path = addrindex_get_picture_file(msginfo->from);
392 if(is_file_exist(icon_path)) {
393 GError *error = NULL;
396 gdk_pixbuf_get_file_info(icon_path, &w, &h);
397 if((w > 64) || (h > 64))
398 pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
399 64, 64, TRUE, &error);
401 pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
404 debug_print("Could not load picture file: %s\n",
405 error ? error->message : "no details");
410 debug_print("Picture path does not exist: %s\n",icon_path);
415 pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
418 notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
419 g_object_unref(pixbuf);
421 else /* This is not fatal */
422 debug_print("Notification plugin: Icon could not be loaded.\n");
425 notify_notification_set_timeout(ppopup->notification, notify_config.popup_timeout);
428 notify_notification_set_category(ppopup->notification, "email.arrived");
430 /* get notified on bubble close */
431 g_signal_connect(G_OBJECT(popup->notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
434 notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
435 if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
436 debug_print("Notification Plugin: Failed to send notification: %s\n",
437 ppopup->error->message);
438 g_clear_error(&(ppopup->error));
439 g_object_unref(G_OBJECT(ppopup->notification));
440 ppopup->notification = NULL;
444 debug_print("Notification Plugin: Popup created with libnotify.\n");
447 /* Store path to message */
448 if(nftype == F_TYPE_MAIL) {
449 if(msginfo && msginfo->folder) {
451 ident = folder_item_get_identifier(msginfo->folder);
452 ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
457 ppopup->msg_path = NULL;
463 static gboolean notification_libnotify_add_msg(MsgInfo *msginfo,
464 NotificationFolderType nftype)
469 NotificationPopup *ppopup;
472 ppopup = &(popup[nftype]);
474 if(!ppopup->notification)
475 return notification_libnotify_create(msginfo,nftype);
479 if(ppopup->msg_path) {
480 g_free(ppopup->msg_path);
481 ppopup->msg_path = NULL;
484 /* make sure we show a logo on many msg arrival */
485 pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
487 notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
491 summary = _("Mail message");
492 text = g_strdup_printf(ngettext("%d new message arrived",
493 "%d new messages arrived",
494 ppopup->count), ppopup->count);
497 summary = _("News message");
498 text = g_strdup_printf(ngettext("%d new message arrived",
499 "%d new messages arrived",
500 ppopup->count), ppopup->count);
502 case F_TYPE_CALENDAR:
503 summary = _("Calendar message");
504 text = g_strdup_printf(ngettext("%d new calendar message arrived",
505 "%d new calendar messages arrived",
506 ppopup->count), ppopup->count);
509 summary = _("RSS news feed");
510 text = g_strdup_printf(ngettext("%d new article in a RSS feed arrived",
511 "%d new articles in a RSS feed arrived",
512 ppopup->count), ppopup->count);
515 /* Should not happen */
516 debug_print("Notification Plugin: Unknown folder type ignored\n");
520 retval = notify_notification_update(ppopup->notification, summary,
524 debug_print("Notification Plugin: Failed to update notification.\n");
529 notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
530 if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
531 debug_print("Notification Plugin: Failed to send updated notification: "
532 "%s\n", ppopup->error->message);
533 g_clear_error(&(ppopup->error));
537 debug_print("Notification Plugin: Popup successfully modified "
538 "with libnotify.\n");
542 void notification_libnotify_free_func(gpointer data)
544 if(popup[F_TYPE_MAIL].msg_path) {
545 g_free(popup[F_TYPE_MAIL].msg_path);
546 popup[F_TYPE_MAIL].msg_path = NULL;
548 debug_print("Freed notification data\n");
551 #else /* !HAVE_LIBNOTIFY */
552 static gboolean notification_popup_add_msg(MsgInfo *msginfo)
555 NotificationPopup *ppopup;
560 return notification_popup_create(msginfo);
564 if(ppopup->msg_path) {
565 g_free(ppopup->msg_path);
566 ppopup->msg_path = NULL;
570 gtk_widget_destroy(ppopup->label2);
572 message = g_strdup_printf(ngettext("%d new message",
574 ppopup->count), ppopup->count);
575 gtk_label_set_text(GTK_LABEL(ppopup->label1), message);
580 static gboolean notification_popup_create(MsgInfo *msginfo)
584 NotificationPopup *ppopup;
586 g_return_val_if_fail(msginfo, FALSE);
591 ppopup->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_popup");
592 gtk_window_set_decorated(GTK_WINDOW(ppopup->window), FALSE);
593 gtk_window_set_keep_above(GTK_WINDOW(ppopup->window), TRUE);
594 gtk_window_set_accept_focus(GTK_WINDOW(ppopup->window), FALSE);
595 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ppopup->window), TRUE);
596 gtk_window_set_skip_pager_hint(GTK_WINDOW(ppopup->window), TRUE);
597 gtk_window_move(GTK_WINDOW(ppopup->window), notify_config.popup_root_x,
598 notify_config.popup_root_y);
599 gtk_window_resize(GTK_WINDOW(ppopup->window), notify_config.popup_width, 1);
600 if(notify_config.popup_sticky)
601 gtk_window_stick(GTK_WINDOW(ppopup->window));
603 gtk_widget_set_events(ppopup->window,
604 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
605 g_signal_connect(ppopup->window, "button_press_event",
606 G_CALLBACK(notification_popup_button), NULL);
609 ppopup->event_box = gtk_event_box_new();
610 gtk_container_add(GTK_CONTAINER(ppopup->window), ppopup->event_box);
613 ppopup->frame = gtk_frame_new(NULL);
614 gtk_frame_set_shadow_type(GTK_FRAME(ppopup->frame), GTK_SHADOW_ETCHED_OUT);
615 gtk_container_add(GTK_CONTAINER(ppopup->event_box), ppopup->frame);
617 /* Vbox with labels */
618 ppopup->vbox = gtk_vbox_new(FALSE, 2);
619 gtk_container_set_border_width(GTK_CONTAINER(ppopup->vbox), 5);
620 ppopup->label1 = gtk_label_new(msginfo->from ?
621 msginfo->from : _("(No From)"));
622 gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label1, FALSE, FALSE, 0);
624 ppopup->label2 = gtk_label_new(msginfo->subject ?
625 msginfo->subject : _("(No Subject)"));
626 gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label2, FALSE, FALSE, 0);
628 gtk_container_add(GTK_CONTAINER(ppopup->frame), ppopup->vbox);
629 gtk_widget_set_size_request(ppopup->vbox, notify_config.popup_width, -1);
632 if(notify_config.popup_enable_colors) {
633 gtkut_convert_int_to_gdk_color(notify_config.popup_color_bg,&bg);
634 gtkut_convert_int_to_gdk_color(notify_config.popup_color_fg,&fg);
635 gtk_widget_modify_bg(ppopup->event_box,GTK_STATE_NORMAL,&bg);
636 gtk_widget_modify_fg(ppopup->label1,GTK_STATE_NORMAL,&fg);
637 gtk_widget_modify_fg(ppopup->label2,GTK_STATE_NORMAL,&fg);
640 gtk_widget_show_all(ppopup->window);
644 if(msginfo->folder && msginfo->folder->name) {
646 ident = folder_item_get_identifier(msginfo->folder);
647 ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
655 static gboolean notification_popup_button(GtkWidget *widget,
656 GdkEventButton *event,
659 if(event->type == GDK_BUTTON_PRESS) {
660 if(event->button == 1) {
662 /* Let mainwindow pop up */
663 mainwin = mainwindow_get_mainwindow();
666 notification_show_mainwindow(mainwin);
667 /* If there is only one new mail message, jump to this message */
668 if(popup.count == 1) {
671 select_str = g_strdup(popup.msg_path);
673 debug_print("Select message %s\n", select_str);
674 mainwindow_jump_to(select_str, FALSE);
681 #endif /* !HAVE_LIBNOTIFY */
683 #endif /* NOTIFICATION_POPUP */