/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2007 Holger Berndt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_POPUP
#include
#include "mainwindow.h"
#include "procmsg.h"
#include "folder.h"
#include "gtk/gtkutils.h"
#include "gettext.h"
#include "notification_popup.h"
#include "notification_prefs.h"
#include "notification_foldercheck.h"
#include "notification_pixbuf.h"
#include "notification_core.h"
#ifdef HAVE_LIBNOTIFY
# include
#endif
typedef struct {
gint count;
guint timeout_id;
gchar *msg_path;
#ifdef HAVE_LIBNOTIFY
NotifyNotification *notification;
GError *error;
#else /* !HAVE_LIBNOTIFY */
GtkWidget *window;
GtkWidget *frame;
GtkWidget *event_box;
GtkWidget *vbox;
GtkWidget *label1;
GtkWidget *label2;
#endif
} NotificationPopup;
G_LOCK_DEFINE_STATIC(popup);
#ifdef HAVE_LIBNOTIFY
static NotificationPopup popup[F_TYPE_LAST];
#else
static NotificationPopup popup;
#endif
static gboolean popup_timeout_fun(gpointer data);
#ifdef HAVE_LIBNOTIFY
static gboolean notification_libnotify_create(MsgInfo*,
NotificationFolderType);
static gboolean notification_libnotify_add_msg(MsgInfo*,
NotificationFolderType);
static void default_action_cb(NotifyNotification*, const char*,void*);
static void notification_libnotify_free_func(gpointer);
#else /* !HAVE_LIBNOTIFY */
static gboolean notification_popup_add_msg(MsgInfo*);
static gboolean notification_popup_create(MsgInfo*);
static gboolean notification_popup_button(GtkWidget*, GdkEventButton*,
gpointer);
#endif /* !HAVE_LIBNOTIFY */
void notification_popup_msg(MsgInfo *msginfo)
{
gboolean retval;
FolderType ftype;
NotificationPopup *ppopup;
#if HAVE_LIBNOTIFY
gchar *uistr;
#endif
NotificationFolderType nftype;
nftype = F_TYPE_MAIL;
if(!msginfo || !notify_config.popup_show)
return;
if(notify_config.popup_folder_specific) {
guint id;
GSList *list;
gchar *identifier;
gboolean found = FALSE;
if(!(msginfo->folder))
return;
identifier = folder_item_get_identifier(msginfo->folder);
id =
notification_register_folder_specific_list(POPUP_SPECIFIC_FOLDER_ID_STR);
list = notification_foldercheck_get_list(id);
for(; (list != NULL) && !found; list = g_slist_next(list)) {
gchar *list_identifier;
FolderItem *list_item = (FolderItem*) list->data;
list_identifier = folder_item_get_identifier(list_item);
if(!strcmp2(list_identifier, identifier))
found = TRUE;
g_free(list_identifier);
}
g_free(identifier);
if(!found)
return;
}
ftype = msginfo->folder->folder->klass->type;
G_LOCK(popup);
#ifdef HAVE_LIBNOTIFY
/* Check out which type to notify about */
switch(ftype) {
case F_MH:
case F_MBOX:
case F_MAILDIR:
case F_IMAP:
nftype = F_TYPE_MAIL;
break;
case F_NEWS:
nftype = F_TYPE_NEWS;
break;
case F_UNKNOWN:
if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
G_UNLOCK(popup);
return;
}
else if(!strcmp(uistr, "vCalendar"))
nftype = F_TYPE_CALENDAR;
else if(!strcmp(uistr, "RSSyl"))
nftype = F_TYPE_RSS;
else {
debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
G_UNLOCK(popup);
return;
}
break;
default:
debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
G_UNLOCK(popup);
return;
}
ppopup = &(popup[nftype]);
retval = notification_libnotify_add_msg(msginfo, nftype);
#else /* !HAVE_LIBNOTIFY */
ppopup = &popup;
retval = notification_popup_add_msg(msginfo);
#endif /* !HAVE_LIBNOTIFY */
/* Renew timeout only when the above call was successful */
if(retval) {
if(ppopup->timeout_id)
g_source_remove(ppopup->timeout_id);
ppopup->timeout_id = g_timeout_add(notify_config.popup_timeout,
popup_timeout_fun,
GINT_TO_POINTER(nftype));
}
G_UNLOCK(popup);
#ifndef HAVE_LIBNOTIFY
/* GUI update */
while(gtk_events_pending())
gtk_main_iteration();
#endif /* !HAVE_LIBNOTIFY */
}
static gboolean popup_timeout_fun(gpointer data)
{
NotificationPopup *ppopup;
NotificationFolderType nftype;
nftype = GPOINTER_TO_INT(data);
G_LOCK(popup);
#ifdef HAVE_LIBNOTIFY
ppopup = &(popup[nftype]);
if(!notify_notification_close(ppopup->notification, &(ppopup->error))) {
debug_print("Notification Plugin: Failed to close notification: %s.\n",
ppopup->error->message);
/* do I need to g_object_unref()? */
ppopup->notification = NULL;
}
else {
g_object_unref(G_OBJECT(ppopup->notification));
ppopup->notification = NULL;
}
g_clear_error(&(ppopup->error));
#else /* !HAVE_LIBNOTIFY */
ppopup = &popup;
if(ppopup->window) {
gtk_widget_destroy(ppopup->window);
ppopup->window = NULL;
}
#endif
ppopup->timeout_id = 0;
if(ppopup->msg_path) {
g_free(ppopup->msg_path);
ppopup->msg_path = NULL;
}
ppopup->count = 0;
G_UNLOCK(popup);
debug_print("Notification Plugin: Popup closed due to timeout.\n");
return FALSE;
}
#ifdef HAVE_LIBNOTIFY
static void default_action_cb(NotifyNotification *notification,
const char *action,
void *user_data)
{
if(strcmp("default", action))
return;
MainWindow *mainwin;
mainwin = mainwindow_get_mainwindow();
if(mainwin) {
NotificationFolderType nftype;
/* Let mainwindow pop up */
gtk_window_deiconify(GTK_WINDOW(mainwin->window));
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
main_window_show(mainwin);
gtk_window_present(GTK_WINDOW(mainwin->window));
/* If there is only one new mail message, jump to this message */
nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
if(nftype == F_TYPE_MAIL) {
if(popup[F_TYPE_MAIL].count == 1) {
gchar *select_str;
G_LOCK(popup);
select_str = g_strdup(popup[F_TYPE_MAIL].msg_path);
G_UNLOCK(popup);
debug_print("Select message %s\n", select_str);
mainwindow_jump_to(select_str, FALSE);
g_free(select_str);
}
}
}
}
static gboolean notification_libnotify_create(MsgInfo *msginfo,
NotificationFolderType nftype)
{
GdkPixbuf *pixbuf;
NotificationPopup *ppopup;
gchar *summary = NULL;
gchar *text = NULL;
gchar *utf8_str = NULL;
gchar *subj = NULL;
gchar *from = NULL;
g_return_val_if_fail(msginfo, FALSE);
ppopup = &(popup[nftype]);
/* init libnotify if necessary */
if(!notify_is_initted()) {
if(!notify_init("claws-mail")) {
debug_print("Notification Plugin: Failed to initialize libnotify. "
"No popup will be shown.\n");
return FALSE;
}
}
switch(nftype) {
case F_TYPE_MAIL:
summary = _("New Mail message");
from = notification_libnotify_sanitize_str(msginfo->from ?
msginfo->from : _("(No From)"));
subj = notification_libnotify_sanitize_str(msginfo->subject ?
msginfo->subject : _("(No Subject)"));
text = g_strconcat(from,"\n\n",subj,NULL);
/* Make sure text is valid UTF8 */
utf8_str = notification_validate_utf8_str(text);
g_free(text);
if(from) g_free(from);
if(subj) g_free(subj);
break;
case F_TYPE_NEWS:
summary = _("New News post");
utf8_str = g_strdup(_("A new message arrived"));
break;
case F_TYPE_CALENDAR:
summary = _("New Calendar message");
utf8_str = g_strdup(_("A new calendar message arrived"));
break;
case F_TYPE_RSS:
summary = _("New RSS feed article");
utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
break;
default:
summary = _("New unknown message");
utf8_str = g_strdup(_("Unknown message type arrived"));
break;
}
ppopup->notification = notify_notification_new(summary, utf8_str, NULL, NULL);
g_free(utf8_str);
if(ppopup->notification == NULL) {
debug_print("Notification Plugin: Failed to create a new "
"notification.\n");
return FALSE;
}
/* Default action */
notify_notification_add_action(ppopup->notification,
"default", "Present main window",
(NotifyActionCallback)default_action_cb,
GINT_TO_POINTER(nftype),
notification_libnotify_free_func);
/* Icon */
pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
if(pixbuf)
notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
else /* This is not fatal */
debug_print("Notification plugin: Icon could not be loaded.\n");
/* Never time out, close is handled manually. */
notify_notification_set_timeout(ppopup->notification, NOTIFY_EXPIRES_NEVER);
/* Category */
notify_notification_set_category(ppopup->notification, "email.arrived");
/* Show the popup */
if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
debug_print("Notification Plugin: Failed to send notification: %s\n",
ppopup->error->message);
g_clear_error(&(ppopup->error));
g_object_unref(G_OBJECT(ppopup->notification));
ppopup->notification = NULL;
return FALSE;
}
debug_print("Notification Plugin: Popup created with libnotify.\n");
ppopup->count = 1;
/* Store path to message */
if(nftype == F_TYPE_MAIL) {
if(msginfo->folder && msginfo->folder) {
gchar *ident;
ident = folder_item_get_identifier(msginfo->folder);
ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
msginfo->msgnum);
g_free(ident);
}
else
ppopup->msg_path = NULL;
}
return TRUE;
}
static gboolean notification_libnotify_add_msg(MsgInfo *msginfo,
NotificationFolderType nftype)
{
gchar *summary;
gchar *text;
gboolean retval;
NotificationPopup *ppopup;
ppopup = &(popup[nftype]);
if(!ppopup->notification)
return notification_libnotify_create(msginfo,nftype);
ppopup->count++;
if(ppopup->msg_path) {
g_free(ppopup->msg_path);
ppopup->msg_path = NULL;
}
switch(nftype) {
case F_TYPE_MAIL:
summary = _("Mail message");
text = g_strdup_printf(ngettext("%d new message arrived",
"%d new messages arrived",
ppopup->count), ppopup->count);
break;
case F_TYPE_NEWS:
summary = _("News message");
text = g_strdup_printf( ngettext("%d new message arrived",
"%d new messages arrived",
ppopup->count), ppopup->count);
break;
case F_TYPE_CALENDAR:
summary = _("Calendar message");
text = g_strdup_printf( ngettext("%d new calendar message arrived",
"%d new calendar messages arrived",
ppopup->count), ppopup->count);
break;
case F_TYPE_RSS:
summary = _("RSS news feed");
text = g_strdup_printf( ngettext("%d new article in a RSS feed arrived",
"%d new articles in a RSS feed arrived",
ppopup->count), ppopup->count);
break;
default:
/* Should not happen */
debug_print("Notification Plugin: Unknown folder type ignored\n");
return FALSE;
}
retval = notify_notification_update(ppopup->notification, summary,
text, NULL);
g_free(text);
if(!retval) {
debug_print("Notification Plugin: Failed to update notification.\n");
return FALSE;
}
/* Show the popup */
if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
debug_print("Notification Plugin: Failed to send updated notification: "
"%s\n", ppopup->error->message);
g_clear_error(&(ppopup->error));
return FALSE;
}
debug_print("Notification Plugin: Popup successfully modified "
"with libnotify.\n");
return TRUE;
}
void notification_libnotify_free_func(gpointer data)
{
if(popup[F_TYPE_MAIL].msg_path) {
g_free(popup[F_TYPE_MAIL].msg_path);
popup[F_TYPE_MAIL].msg_path = NULL;
}
debug_print("Freed notification data\n");
}
#else /* !HAVE_LIBNOTIFY */
static gboolean notification_popup_add_msg(MsgInfo *msginfo)
{
gchar *message;
NotificationPopup *ppopup;
ppopup = &popup;
if(!ppopup->window)
return notification_popup_create(msginfo);
ppopup->count++;
if(ppopup->msg_path) {
g_free(ppopup->msg_path);
ppopup->msg_path = NULL;
}
if(ppopup->label2)
gtk_widget_destroy(ppopup->label2);
message = g_strdup_printf(ngettext("%d new message",
"%d new messages",
ppopup->count), ppopup->count);
gtk_label_set_text(GTK_LABEL(ppopup->label1), message);
g_free(message);
return TRUE;
}
static gboolean notification_popup_create(MsgInfo *msginfo)
{
GdkColor bg;
GdkColor fg;
NotificationPopup *ppopup;
g_return_val_if_fail(msginfo, FALSE);
ppopup = &popup;
/* Window */
ppopup->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_popup");
gtk_window_set_decorated(GTK_WINDOW(ppopup->window), FALSE);
gtk_window_set_keep_above(GTK_WINDOW(ppopup->window), TRUE);
gtk_window_set_accept_focus(GTK_WINDOW(ppopup->window), FALSE);
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ppopup->window), TRUE);
gtk_window_set_skip_pager_hint(GTK_WINDOW(ppopup->window), TRUE);
gtk_window_move(GTK_WINDOW(ppopup->window), notify_config.popup_root_x,
notify_config.popup_root_y);
gtk_window_resize(GTK_WINDOW(ppopup->window), notify_config.popup_width, 1);
if(notify_config.popup_sticky)
gtk_window_stick(GTK_WINDOW(ppopup->window));
/* Signals */
gtk_widget_set_events(ppopup->window,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
g_signal_connect(ppopup->window, "button_press_event",
G_CALLBACK(notification_popup_button), NULL);
/* Event box */
ppopup->event_box = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(ppopup->window), ppopup->event_box);
/* Frame */
ppopup->frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(ppopup->frame), GTK_SHADOW_ETCHED_OUT);
gtk_container_add(GTK_CONTAINER(ppopup->event_box), ppopup->frame);
/* Vbox with labels */
ppopup->vbox = gtk_vbox_new(FALSE, 2);
gtk_container_set_border_width(GTK_CONTAINER(ppopup->vbox), 5);
ppopup->label1 = gtk_label_new(msginfo->from ?
msginfo->from : _("(No From)"));
gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label1, FALSE, FALSE, 0);
ppopup->label2 = gtk_label_new(msginfo->subject ?
msginfo->subject : _("(No Subject)"));
gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label2, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(ppopup->frame), ppopup->vbox);
gtk_widget_set_size_request(ppopup->vbox, notify_config.popup_width, -1);
/* Color */
if(notify_config.popup_enable_colors) {
gtkut_convert_int_to_gdk_color(notify_config.popup_color_bg,&bg);
gtkut_convert_int_to_gdk_color(notify_config.popup_color_fg,&fg);
gtk_widget_modify_bg(ppopup->event_box,GTK_STATE_NORMAL,&bg);
gtk_widget_modify_fg(ppopup->label1,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(ppopup->label2,GTK_STATE_NORMAL,&fg);
}
gtk_widget_show_all(ppopup->window);
ppopup->count = 1;
if(msginfo->folder && msginfo->folder->name) {
gchar *ident;
ident = folder_item_get_identifier(msginfo->folder);
ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
msginfo->msgnum);
g_free(ident);
}
return TRUE;
}
static gboolean notification_popup_button(GtkWidget *widget,
GdkEventButton *event,
gpointer data)
{
if(event->type == GDK_BUTTON_PRESS) {
if(event->button == 1) {
MainWindow *mainwin;
/* Let mainwindow pop up */
mainwin = mainwindow_get_mainwindow();
if(!mainwin)
return TRUE;
gtk_window_deiconify(GTK_WINDOW(mainwin->window));
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
main_window_show(mainwin);
gtk_window_present(GTK_WINDOW(mainwin->window));
/* If there is only one new mail message, jump to this message */
if(popup.count == 1) {
gchar *select_str;
G_LOCK(popup);
select_str = g_strdup(popup.msg_path);
G_UNLOCK(popup);
debug_print("Select message %s\n", select_str);
mainwindow_jump_to(select_str, FALSE);
g_free(select_str);
}
}
}
return TRUE;
}
#endif /* !HAVE_LIBNOTIFY */
#endif /* NOTIFICATION_POPUP */