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_ALT_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
)
99 NotificationPopup
*ppopup
;
102 NotificationFolderType nftype
;
104 nftype
= F_TYPE_MAIL
;
106 if(!msginfo
|| !notify_config
.popup_show
)
109 if(notify_config
.popup_folder_specific
) {
113 gboolean found
= FALSE
;
115 if(!(msginfo
->folder
))
118 identifier
= folder_item_get_identifier(msginfo
->folder
);
121 notification_register_folder_specific_list(POPUP_SPECIFIC_FOLDER_ID_STR
);
122 list
= notification_foldercheck_get_list(id
);
123 for(; (list
!= NULL
) && !found
; list
= g_slist_next(list
)) {
124 gchar
*list_identifier
;
125 FolderItem
*list_item
= (FolderItem
*) list
->data
;
127 list_identifier
= folder_item_get_identifier(list_item
);
128 if(!g_strcmp0(list_identifier
, identifier
))
131 g_free(list_identifier
);
141 #ifdef HAVE_LIBNOTIFY
142 /* Check out which type to notify about */
143 ftype
= msginfo
->folder
->folder
->klass
->type
;
149 nftype
= F_TYPE_MAIL
;
152 nftype
= F_TYPE_NEWS
;
155 if((uistr
= msginfo
->folder
->folder
->klass
->uistr
) == NULL
) {
159 else if(!strcmp(uistr
, "vCalendar"))
160 nftype
= F_TYPE_CALENDAR
;
161 else if(!strcmp(uistr
, "RSSyl"))
164 debug_print("Notification Plugin: Unknown folder type %d\n",ftype
);
170 debug_print("Notification Plugin: Unknown folder type %d\n",ftype
);
175 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
;
235 gtk_widget_destroy(ppopup
->window
);
236 ppopup
->window
= NULL
;
238 ppopup
->timeout_id
= 0;
240 if(ppopup
->msg_path
) {
241 g_free(ppopup
->msg_path
);
242 ppopup
->msg_path
= NULL
;
246 debug_print("Notification Plugin: Popup closed due to timeout.\n");
251 #ifdef HAVE_LIBNOTIFY
252 static void default_action_cb(NotifyNotification
*notification
,
256 if(strcmp("default", action
))
260 mainwin
= mainwindow_get_mainwindow();
262 NotificationFolderType nftype
;
264 /* Let mainwindow pop up */
265 notification_show_mainwindow(mainwin
);
266 /* If there is only one new mail message, jump to this message */
267 nftype
= (NotificationFolderType
)GPOINTER_TO_INT(user_data
);
268 if(nftype
== F_TYPE_MAIL
) {
269 if(popup
[F_TYPE_MAIL
].count
== 1) {
272 select_str
= g_strdup(popup
[F_TYPE_MAIL
].msg_path
);
274 debug_print("Select message %s\n", select_str
);
275 mainwindow_jump_to(select_str
, FALSE
);
282 static gboolean
notification_libnotify_create(MsgInfo
*msginfo
,
283 NotificationFolderType nftype
)
286 NotificationPopup
*ppopup
;
287 gchar
*summary
= NULL
;
292 gchar
*foldname
= NULL
;
294 gboolean support_actions
= FALSE
;
296 g_return_val_if_fail(msginfo
, FALSE
);
298 ppopup
= &(popup
[nftype
]);
300 /* init libnotify if necessary */
301 if(!notify_is_initted()) {
302 if(!notify_init("claws-mail")) {
303 debug_print("Notification Plugin: Failed to initialize libnotify. "
304 "No popup will be shown.\n");
311 summary
= _("New Mail message");
312 from
= notification_libnotify_sanitize_str(msginfo
->from
?
313 msginfo
->from
: _("(No From)"));
314 subj
= notification_libnotify_sanitize_str(msginfo
->subject
?
315 msginfo
->subject
: _("(No Subject)"));
316 if (notify_config
.popup_display_folder_name
) {
317 foldname
= notification_libnotify_sanitize_str(msginfo
->folder
->path
);
318 text
= g_strconcat(from
,"\n\n", subj
, "\n\n", foldname
, NULL
);
321 text
= g_strconcat(from
, "\n\n",subj
, NULL
);
323 /* Make sure text is valid UTF8 */
324 utf8_str
= notification_validate_utf8_str(text
);
329 if(foldname
) g_free(foldname
);
332 summary
= _("New News post");
333 utf8_str
= g_strdup(_("A new message arrived"));
335 case F_TYPE_CALENDAR
:
336 summary
= _("New Calendar message");
337 utf8_str
= g_strdup(_("A new calendar message arrived"));
340 summary
= _("New RSS feed article");
341 utf8_str
= g_strdup(_("A new article in a RSS feed arrived"));
344 summary
= _("New unknown message");
345 utf8_str
= g_strdup(_("Unknown message type arrived"));
349 ppopup
->notification
= notify_notification_new(summary
, utf8_str
, NULL
350 #if !NOTIFY_CHECK_VERSION(0, 7, 0)
355 if(ppopup
->notification
== NULL
) {
356 debug_print("Notification Plugin: Failed to create a new "
361 caps
= notify_get_server_caps();
364 for(c
= caps
; c
!= NULL
; c
= c
->next
) {
365 if(strcmp((char*)c
->data
, "actions") == 0 ) {
366 support_actions
= TRUE
;
371 g_list_foreach(caps
, (GFunc
)g_free
, NULL
);
377 notify_notification_add_action(ppopup
->notification
,
378 "default", _("Present main window"),
379 (NotifyActionCallback
)default_action_cb
,
380 GINT_TO_POINTER(nftype
),
381 notification_libnotify_free_func
);
385 #ifndef USE_ALT_ADDRBOOK
386 if(msginfo
&& msginfo
->from
) {
388 icon_path
= addrindex_get_picture_file(msginfo
->from
);
389 if(is_file_exist(icon_path
)) {
390 GError
*error
= NULL
;
393 gdk_pixbuf_get_file_info(icon_path
, &w
, &h
);
394 if((w
> 64) || (h
> 64))
395 pixbuf
= gdk_pixbuf_new_from_file_at_scale(icon_path
,
396 64, 64, TRUE
, &error
);
398 pixbuf
= gdk_pixbuf_new_from_file(icon_path
, &error
);
401 debug_print("Could not load picture file: %s\n",
402 error
? error
->message
: "no details");
407 debug_print("Picture path does not exist: %s\n",icon_path
);
412 pixbuf
= g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64
));
415 notify_notification_set_icon_from_pixbuf(ppopup
->notification
, pixbuf
);
416 g_object_unref(pixbuf
);
418 else /* This is not fatal */
419 debug_print("Notification plugin: Icon could not be loaded.\n");
422 notify_notification_set_timeout(ppopup
->notification
, notify_config
.popup_timeout
);
425 notify_notification_set_category(ppopup
->notification
, "email.arrived");
427 /* get notified on bubble close */
428 g_signal_connect(G_OBJECT(popup
->notification
), "closed", G_CALLBACK(popup_timeout_fun
), NULL
);
431 notify_notification_set_hint_string(ppopup
->notification
, "desktop-entry", "claws-mail");
432 if(!notify_notification_show(ppopup
->notification
, &(ppopup
->error
))) {
433 debug_print("Notification Plugin: Failed to send notification: %s\n",
434 ppopup
->error
->message
);
435 g_clear_error(&(ppopup
->error
));
436 g_object_unref(G_OBJECT(ppopup
->notification
));
437 ppopup
->notification
= NULL
;
441 debug_print("Notification Plugin: Popup created with libnotify.\n");
444 /* Store path to message */
445 if(nftype
== F_TYPE_MAIL
) {
446 if(msginfo
&& msginfo
->folder
) {
448 ident
= folder_item_get_identifier(msginfo
->folder
);
449 ppopup
->msg_path
= g_strdup_printf("%s%s%u", ident
,G_DIR_SEPARATOR_S
,
454 ppopup
->msg_path
= NULL
;
460 static gboolean
notification_libnotify_add_msg(MsgInfo
*msginfo
,
461 NotificationFolderType nftype
)
466 NotificationPopup
*ppopup
;
469 ppopup
= &(popup
[nftype
]);
471 if(!ppopup
->notification
)
472 return notification_libnotify_create(msginfo
,nftype
);
476 if(ppopup
->msg_path
) {
477 g_free(ppopup
->msg_path
);
478 ppopup
->msg_path
= NULL
;
481 /* make sure we show a logo on many msg arrival */
482 pixbuf
= notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64
);
484 notify_notification_set_icon_from_pixbuf(ppopup
->notification
, pixbuf
);
488 summary
= _("Mail message");
489 text
= g_strdup_printf(ngettext("%d new message arrived",
490 "%d new messages arrived",
491 ppopup
->count
), ppopup
->count
);
494 summary
= _("News message");
495 text
= g_strdup_printf(ngettext("%d new message arrived",
496 "%d new messages arrived",
497 ppopup
->count
), ppopup
->count
);
499 case F_TYPE_CALENDAR
:
500 summary
= _("Calendar message");
501 text
= g_strdup_printf(ngettext("%d new calendar message arrived",
502 "%d new calendar messages arrived",
503 ppopup
->count
), ppopup
->count
);
506 summary
= _("RSS news feed");
507 text
= g_strdup_printf(ngettext("%d new article in a RSS feed arrived",
508 "%d new articles in a RSS feed arrived",
509 ppopup
->count
), ppopup
->count
);
512 /* Should not happen */
513 debug_print("Notification Plugin: Unknown folder type ignored\n");
517 retval
= notify_notification_update(ppopup
->notification
, summary
,
521 debug_print("Notification Plugin: Failed to update notification.\n");
526 notify_notification_set_hint_string(ppopup
->notification
, "desktop-entry", "claws-mail");
527 if(!notify_notification_show(ppopup
->notification
, &(ppopup
->error
))) {
528 debug_print("Notification Plugin: Failed to send updated notification: "
529 "%s\n", ppopup
->error
->message
);
530 g_clear_error(&(ppopup
->error
));
534 debug_print("Notification Plugin: Popup successfully modified "
535 "with libnotify.\n");
539 void notification_libnotify_free_func(gpointer data
)
541 if(popup
[F_TYPE_MAIL
].msg_path
) {
542 g_free(popup
[F_TYPE_MAIL
].msg_path
);
543 popup
[F_TYPE_MAIL
].msg_path
= NULL
;
545 debug_print("Freed notification data\n");
548 #else /* !HAVE_LIBNOTIFY */
549 static gboolean
notification_popup_add_msg(MsgInfo
*msginfo
)
552 NotificationPopup
*ppopup
;
557 return notification_popup_create(msginfo
);
561 if(ppopup
->msg_path
) {
562 g_free(ppopup
->msg_path
);
563 ppopup
->msg_path
= NULL
;
567 gtk_widget_destroy(ppopup
->label2
);
568 ppopup
->label2
= NULL
;
571 message
= g_strdup_printf(ngettext("%d new message",
573 ppopup
->count
), ppopup
->count
);
574 gtk_label_set_text(GTK_LABEL(ppopup
->label1
), message
);
579 static gboolean
notification_popup_create(MsgInfo
*msginfo
)
583 NotificationPopup
*ppopup
;
585 g_return_val_if_fail(msginfo
, FALSE
);
590 ppopup
->window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "notification_popup");
591 gtk_window_set_decorated(GTK_WINDOW(ppopup
->window
), FALSE
);
592 gtk_window_set_keep_above(GTK_WINDOW(ppopup
->window
), TRUE
);
593 gtk_window_set_accept_focus(GTK_WINDOW(ppopup
->window
), FALSE
);
594 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ppopup
->window
), TRUE
);
595 gtk_window_set_skip_pager_hint(GTK_WINDOW(ppopup
->window
), TRUE
);
596 gtk_window_move(GTK_WINDOW(ppopup
->window
), notify_config
.popup_root_x
,
597 notify_config
.popup_root_y
);
598 gtk_window_resize(GTK_WINDOW(ppopup
->window
), notify_config
.popup_width
, 1);
599 if(notify_config
.popup_sticky
)
600 gtk_window_stick(GTK_WINDOW(ppopup
->window
));
602 gtk_widget_set_events(ppopup
->window
,
603 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
604 g_signal_connect(ppopup
->window
, "button_press_event",
605 G_CALLBACK(notification_popup_button
), NULL
);
608 ppopup
->event_box
= gtk_event_box_new();
609 gtk_container_add(GTK_CONTAINER(ppopup
->window
), ppopup
->event_box
);
612 ppopup
->frame
= gtk_frame_new(NULL
);
613 gtk_frame_set_shadow_type(GTK_FRAME(ppopup
->frame
), GTK_SHADOW_ETCHED_OUT
);
614 gtk_container_add(GTK_CONTAINER(ppopup
->event_box
), ppopup
->frame
);
616 /* Vbox with labels */
617 ppopup
->vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 2);
618 gtk_container_set_border_width(GTK_CONTAINER(ppopup
->vbox
), 5);
619 ppopup
->label1
= gtk_label_new(msginfo
->from
?
620 msginfo
->from
: _("(No From)"));
621 gtk_box_pack_start(GTK_BOX(ppopup
->vbox
), ppopup
->label1
, FALSE
, FALSE
, 0);
623 ppopup
->label2
= gtk_label_new(msginfo
->subject
?
624 msginfo
->subject
: _("(No Subject)"));
625 gtk_box_pack_start(GTK_BOX(ppopup
->vbox
), ppopup
->label2
, FALSE
, FALSE
, 0);
627 gtk_container_add(GTK_CONTAINER(ppopup
->frame
), ppopup
->vbox
);
628 gtk_widget_set_size_request(ppopup
->vbox
, notify_config
.popup_width
, -1);
631 if(notify_config
.popup_enable_colors
) {
632 GTKUT_GDKRGBA_TO_GDKCOLOR(notify_config
.popup_color_bg
,bg
);
633 GTKUT_GDKRGBA_TO_GDKCOLOR(notify_config
.popup_color_fg
,fg
);
634 gtk_widget_modify_bg(ppopup
->event_box
,GTK_STATE_NORMAL
,&bg
);
635 gtk_widget_modify_fg(ppopup
->label1
,GTK_STATE_NORMAL
,&fg
);
636 gtk_widget_modify_fg(ppopup
->label2
,GTK_STATE_NORMAL
,&fg
);
639 gtk_widget_show_all(ppopup
->window
);
643 if(msginfo
->folder
&& msginfo
->folder
->name
) {
645 ident
= folder_item_get_identifier(msginfo
->folder
);
646 ppopup
->msg_path
= g_strdup_printf("%s%s%u", ident
,G_DIR_SEPARATOR_S
,
654 static gboolean
notification_popup_button(GtkWidget
*widget
,
655 GdkEventButton
*event
,
658 if(event
->type
== GDK_BUTTON_PRESS
) {
659 if(event
->button
== 1) {
661 /* Let mainwindow pop up */
662 mainwin
= mainwindow_get_mainwindow();
665 notification_show_mainwindow(mainwin
);
666 /* If there is only one new mail message, jump to this message */
667 if(popup
.count
== 1) {
670 select_str
= g_strdup(popup
.msg_path
);
672 debug_print("Select message %s\n", select_str
);
673 mainwindow_jump_to(select_str
, FALSE
);
680 #endif /* !HAVE_LIBNOTIFY */
682 #endif /* NOTIFICATION_POPUP */