Remove inclusion of sys/socket.h from nntp-thread.c
[claws.git] / src / plugins / notification / notification_popup.c
blob507b7c6a7b50d31fb94acc6903e513e60a98b411
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/>.
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 # include "claws-features.h"
21 #endif
23 #include <glib.h>
24 #include <glib/gi18n.h>
26 #ifdef NOTIFICATION_POPUP
28 #include <gtk/gtk.h>
30 #include "mainwindow.h"
31 #include "procmsg.h"
32 #include "folder.h"
33 #ifndef USE_ALT_ADDRBOOK
34 #include "addrindex.h"
35 #endif
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"
45 #ifdef HAVE_LIBNOTIFY
46 # include <libnotify/notify.h>
47 #endif
49 #ifndef NOTIFY_CHECK_VERSION
50 # define NOTIFY_CHECK_VERSION(a,b,c) 0
51 #endif
53 typedef struct {
54 gint count;
55 gchar *msg_path;
56 #ifdef HAVE_LIBNOTIFY
57 NotifyNotification *notification;
58 GError *error;
59 #else /* !HAVE_LIBNOTIFY */
60 guint timeout_id;
61 GtkWidget *window;
62 GtkWidget *frame;
63 GtkWidget *event_box;
64 GtkWidget *vbox;
65 GtkWidget *label1;
66 GtkWidget *label2;
67 #endif
68 } NotificationPopup;
70 G_LOCK_DEFINE_STATIC(popup);
72 #ifdef HAVE_LIBNOTIFY
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);
82 #else
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);
90 #endif
93 void notification_popup_msg(MsgInfo *msginfo)
95 #if HAVE_LIBNOTIFY
96 FolderType ftype;
97 gchar *uistr;
98 #else
99 NotificationPopup *ppopup;
100 gboolean retval;
101 #endif
102 NotificationFolderType nftype;
104 nftype = F_TYPE_MAIL;
106 if(!msginfo || !notify_config.popup_show)
107 return;
109 if(notify_config.popup_folder_specific) {
110 guint id;
111 GSList *list;
112 gchar *identifier;
113 gboolean found = FALSE;
115 if(!(msginfo->folder))
116 return;
118 identifier = folder_item_get_identifier(msginfo->folder);
120 id =
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))
129 found = TRUE;
131 g_free(list_identifier);
133 g_free(identifier);
135 if(!found)
136 return;
140 G_LOCK(popup);
141 #ifdef HAVE_LIBNOTIFY
142 /* Check out which type to notify about */
143 ftype = msginfo->folder->folder->klass->type;
144 switch(ftype) {
145 case F_MH:
146 case F_MBOX:
147 case F_MAILDIR:
148 case F_IMAP:
149 nftype = F_TYPE_MAIL;
150 break;
151 case F_NEWS:
152 nftype = F_TYPE_NEWS;
153 break;
154 case F_UNKNOWN:
155 if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
156 G_UNLOCK(popup);
157 return;
159 else if(!strcmp(uistr, "vCalendar"))
160 nftype = F_TYPE_CALENDAR;
161 else if(!strcmp(uistr, "RSSyl"))
162 nftype = F_TYPE_RSS;
163 else {
164 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
165 G_UNLOCK(popup);
166 return;
168 break;
169 default:
170 debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
171 G_UNLOCK(popup);
172 return;
175 notification_libnotify_add_msg(msginfo, nftype);
176 #else /* !HAVE_LIBNOTIFY */
177 ppopup = &popup;
178 retval = notification_popup_add_msg(msginfo);
180 /* Renew timeout only when the above call was successful */
181 if(retval) {
182 if(ppopup->timeout_id)
183 g_source_remove(ppopup->timeout_id);
184 ppopup->timeout_id = g_timeout_add(notify_config.popup_timeout,
185 popup_timeout_fun,
186 GINT_TO_POINTER(nftype));
189 #endif /* !HAVE_LIBNOTIFY */
191 G_UNLOCK(popup);
193 #ifndef HAVE_LIBNOTIFY
194 /* GUI update */
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);
209 G_LOCK(popup);
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;
221 ppopup->count = 0;
222 G_UNLOCK(popup);
223 debug_print("Notification Plugin: Popup closed due to timeout.\n");
226 #else
227 static gboolean popup_timeout_fun(gpointer data)
229 NotificationPopup *ppopup;
231 G_LOCK(popup);
233 ppopup = &popup;
234 if(ppopup->window) {
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;
244 ppopup->count = 0;
245 G_UNLOCK(popup);
246 debug_print("Notification Plugin: Popup closed due to timeout.\n");
247 return FALSE;
249 #endif
251 #ifdef HAVE_LIBNOTIFY
252 static void default_action_cb(NotifyNotification *notification,
253 const char *action,
254 void *user_data)
256 if(strcmp("default", action))
257 return;
259 MainWindow *mainwin;
260 mainwin = mainwindow_get_mainwindow();
261 if(mainwin) {
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) {
270 gchar *select_str;
271 G_LOCK(popup);
272 select_str = g_strdup(popup[F_TYPE_MAIL].msg_path);
273 G_UNLOCK(popup);
274 debug_print("Select message %s\n", select_str);
275 mainwindow_jump_to(select_str, FALSE);
276 g_free(select_str);
282 static gboolean notification_libnotify_create(MsgInfo *msginfo,
283 NotificationFolderType nftype)
285 GdkPixbuf *pixbuf;
286 NotificationPopup *ppopup;
287 gchar *summary = NULL;
288 gchar *text = NULL;
289 gchar *utf8_str;
290 gchar *subj;
291 gchar *from;
292 gchar *foldname = NULL;
293 GList *caps = 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");
305 return FALSE;
309 switch(nftype) {
310 case F_TYPE_MAIL:
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);
320 else
321 text = g_strconcat(from, "\n\n",subj, NULL);
323 /* Make sure text is valid UTF8 */
324 utf8_str = notification_validate_utf8_str(text);
326 g_free(text);
327 g_free(from);
328 g_free(subj);
329 if(foldname) g_free(foldname);
330 break;
331 case F_TYPE_NEWS:
332 summary = _("New News post");
333 utf8_str = g_strdup(_("A new message arrived"));
334 break;
335 case F_TYPE_CALENDAR:
336 summary = _("New Calendar message");
337 utf8_str = g_strdup(_("A new calendar message arrived"));
338 break;
339 case F_TYPE_RSS:
340 summary = _("New RSS feed article");
341 utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
342 break;
343 default:
344 summary = _("New unknown message");
345 utf8_str = g_strdup(_("Unknown message type arrived"));
346 break;
349 ppopup->notification = notify_notification_new(summary, utf8_str, NULL
350 #if !NOTIFY_CHECK_VERSION(0, 7, 0)
351 , NULL
352 #endif
354 g_free(utf8_str);
355 if(ppopup->notification == NULL) {
356 debug_print("Notification Plugin: Failed to create a new "
357 "notification.\n");
358 return FALSE;
361 caps = notify_get_server_caps();
362 if(caps != NULL) {
363 GList *c;
364 for(c = caps; c != NULL; c = c->next) {
365 if(strcmp((char*)c->data, "actions") == 0 ) {
366 support_actions = TRUE;
367 break;
371 g_list_foreach(caps, (GFunc)g_free, NULL);
372 g_list_free(caps);
375 /* Default action */
376 if (support_actions)
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);
383 /* Icon */
384 pixbuf = NULL;
385 #ifndef USE_ALT_ADDRBOOK
386 if(msginfo && msginfo->from) {
387 gchar *icon_path;
388 icon_path = addrindex_get_picture_file(msginfo->from);
389 if(is_file_exist(icon_path)) {
390 GError *error = NULL;
391 gint w, h;
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);
397 else
398 pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
400 if(!pixbuf) {
401 debug_print("Could not load picture file: %s\n",
402 error ? error->message : "no details");
403 g_error_free(error);
406 else
407 debug_print("Picture path does not exist: %s\n",icon_path);
408 g_free(icon_path);
410 #endif
411 if(!pixbuf)
412 pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
414 if(pixbuf) {
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");
421 /* timeout */
422 notify_notification_set_timeout(ppopup->notification, notify_config.popup_timeout);
424 /* Category */
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);
430 /* Show the popup */
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;
438 return FALSE;
441 debug_print("Notification Plugin: Popup created with libnotify.\n");
442 ppopup->count = 1;
444 /* Store path to message */
445 if(nftype == F_TYPE_MAIL) {
446 if(msginfo && msginfo->folder) {
447 gchar *ident;
448 ident = folder_item_get_identifier(msginfo->folder);
449 ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
450 msginfo->msgnum);
451 g_free(ident);
453 else
454 ppopup->msg_path = NULL;
457 return TRUE;
460 static gboolean notification_libnotify_add_msg(MsgInfo *msginfo,
461 NotificationFolderType nftype)
463 gchar *summary;
464 gchar *text;
465 gboolean retval;
466 NotificationPopup *ppopup;
467 GdkPixbuf *pixbuf;
469 ppopup = &(popup[nftype]);
471 if(!ppopup->notification)
472 return notification_libnotify_create(msginfo,nftype);
474 ppopup->count++;
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);
483 if(pixbuf)
484 notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
486 switch(nftype) {
487 case F_TYPE_MAIL:
488 summary = _("Mail message");
489 text = g_strdup_printf(ngettext("%d new message arrived",
490 "%d new messages arrived",
491 ppopup->count), ppopup->count);
492 break;
493 case F_TYPE_NEWS:
494 summary = _("News message");
495 text = g_strdup_printf(ngettext("%d new message arrived",
496 "%d new messages arrived",
497 ppopup->count), ppopup->count);
498 break;
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);
504 break;
505 case F_TYPE_RSS:
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);
510 break;
511 default:
512 /* Should not happen */
513 debug_print("Notification Plugin: Unknown folder type ignored\n");
514 return FALSE;
517 retval = notify_notification_update(ppopup->notification, summary,
518 text, NULL);
519 g_free(text);
520 if(!retval) {
521 debug_print("Notification Plugin: Failed to update notification.\n");
522 return FALSE;
525 /* Show the popup */
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));
531 return FALSE;
534 debug_print("Notification Plugin: Popup successfully modified "
535 "with libnotify.\n");
536 return TRUE;
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)
551 gchar *message;
552 NotificationPopup *ppopup;
554 ppopup = &popup;
556 if(!ppopup->window)
557 return notification_popup_create(msginfo);
559 ppopup->count++;
561 if(ppopup->msg_path) {
562 g_free(ppopup->msg_path);
563 ppopup->msg_path = NULL;
566 if(ppopup->label2) {
567 gtk_widget_destroy(ppopup->label2);
568 ppopup->label2 = NULL;
571 message = g_strdup_printf(ngettext("%d new message",
572 "%d new messages",
573 ppopup->count), ppopup->count);
574 gtk_label_set_text(GTK_LABEL(ppopup->label1), message);
575 g_free(message);
576 return TRUE;
579 static gboolean notification_popup_create(MsgInfo *msginfo)
581 GdkColor bg;
582 GdkColor fg;
583 NotificationPopup *ppopup;
585 g_return_val_if_fail(msginfo, FALSE);
587 ppopup = &popup;
589 /* Window */
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));
601 /* Signals */
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);
607 /* Event box */
608 ppopup->event_box = gtk_event_box_new();
609 gtk_container_add(GTK_CONTAINER(ppopup->window), ppopup->event_box);
611 /* Frame */
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);
630 /* Color */
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);
641 ppopup->count = 1;
643 if(msginfo->folder && msginfo->folder->name) {
644 gchar *ident;
645 ident = folder_item_get_identifier(msginfo->folder);
646 ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
647 msginfo->msgnum);
648 g_free(ident);
651 return TRUE;
654 static gboolean notification_popup_button(GtkWidget *widget,
655 GdkEventButton *event,
656 gpointer data)
658 if(event->type == GDK_BUTTON_PRESS) {
659 if(event->button == 1) {
660 MainWindow *mainwin;
661 /* Let mainwindow pop up */
662 mainwin = mainwindow_get_mainwindow();
663 if(!mainwin)
664 return TRUE;
665 notification_show_mainwindow(mainwin);
666 /* If there is only one new mail message, jump to this message */
667 if(popup.count == 1) {
668 gchar *select_str;
669 G_LOCK(popup);
670 select_str = g_strdup(popup.msg_path);
671 G_UNLOCK(popup);
672 debug_print("Select message %s\n", select_str);
673 mainwindow_jump_to(select_str, FALSE);
674 g_free(select_str);
678 return TRUE;
680 #endif /* !HAVE_LIBNOTIFY */
682 #endif /* NOTIFICATION_POPUP */