/* Notification plugin for Claws Mail
* Copyright (C) 2005-2008 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 .
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
# include "claws-features.h"
#endif
#include
#include
#ifdef NOTIFICATION_BANNER
#include
#include "gtk/gtkutils.h"
#include "prefs_common.h"
#include "mainwindow.h"
#include "menu.h"
#include "procmsg.h"
#include "messageview.h"
#include "compose.h"
#include "menu.h"
#include "notification_core.h"
#include "notification_prefs.h"
#include "notification_banner.h"
typedef struct {
GtkWidget *table;
} NotificationBannerEntry;
typedef struct {
GtkWidget *window;
GtkWidget *scrolled_win;
GtkWidget *viewport;
NotificationBannerEntry *entries;
guint timeout_id;
gboolean scrolling;
} NotificationBanner;
typedef struct {
gint banner_width;
GtkAdjustment *adj;
} ScrollingData;
static void notification_banner_create(GSList*);
static gboolean scroller(gpointer data);
static GtkWidget* create_entrybox(GSList*);
static gboolean notification_banner_configure(GtkWidget*, GdkEventConfigure*,
gpointer);
static gboolean notification_banner_swap_colors(GtkWidget*,GdkEventCrossing*,gpointer);
static gboolean notification_banner_button_press(GtkWidget*,GdkEventButton*,gpointer);
static void notification_banner_show_popup(GtkWidget*,GdkEventButton*);
static void notification_banner_popup_done(GtkMenuShell*,gpointer);
static void banner_menu_reply_cb(GtkAction *,gpointer);
static NotificationBanner banner;
static ScrollingData sdata;
static GtkWidget *banner_popup;
static GtkUIManager *banner_ui_manager;
static GtkActionGroup *banner_action_group;
static gboolean banner_popup_open = FALSE;
static MsgInfo *current_msginfo = NULL;
/* Corresponding mutexes */
G_LOCK_DEFINE_STATIC(banner);
G_LOCK_DEFINE_STATIC(sdata);
static GtkActionEntry banner_popup_entries[] =
{
{"BannerPopup", NULL, "BannerPopup" },
{"BannerPopup/Reply", NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(banner_menu_reply_cb) },
};
void notification_banner_show(GSList *msg_list)
{
G_LOCK(banner);
if((notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) &&
(g_slist_length(msg_list) ||
(notify_config.banner_show == NOTIFY_BANNER_SHOW_ALWAYS)))
notification_banner_create(msg_list);
else
notification_banner_destroy();
G_UNLOCK(banner);
}
void notification_banner_destroy(void)
{
if(banner.window) {
if(banner.entries) {
g_free(banner.entries);
banner.entries = NULL;
}
gtk_widget_destroy(banner.window);
banner.window = NULL;
G_LOCK(sdata);
sdata.adj = NULL;
sdata.banner_width = 0;
G_UNLOCK(sdata);
if(banner.timeout_id) {
g_source_remove(banner.timeout_id);
banner.timeout_id = 0;
}
}
}
static void notification_banner_create(GSList *msg_list)
{
GtkRequisition requisition, requisition_after;
GtkWidget *viewport;
GtkWidget *hbox;
GtkWidget *entrybox;
GdkColor bg;
gint banner_width;
/* Window */
if(!banner.window) {
banner.window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_banner");
gtk_window_set_decorated(GTK_WINDOW(banner.window), FALSE);
if(notify_config.banner_width > 0)
gtk_widget_set_size_request(banner.window, notify_config.banner_width, -1);
else
gtk_widget_set_size_request(banner.window, gdk_screen_width(), -1);
gtk_window_set_keep_above(GTK_WINDOW(banner.window), TRUE);
gtk_window_set_accept_focus(GTK_WINDOW(banner.window), FALSE);
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(banner.window), TRUE);
gtk_window_move(GTK_WINDOW(banner.window),
notify_config.banner_root_x, notify_config.banner_root_y);
g_signal_connect(banner.window, "configure-event",
G_CALLBACK(notification_banner_configure), NULL);
}
else {
if(banner.entries) {
g_free(banner.entries);
banner.entries = NULL;
}
gtk_widget_destroy(banner.scrolled_win);
}
if(notify_config.banner_sticky)
gtk_window_stick(GTK_WINDOW(banner.window));
else
gtk_window_unstick(GTK_WINDOW(banner.window));
/* Scrolled window */
banner.scrolled_win = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(banner.window), banner.scrolled_win);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(banner.scrolled_win),
GTK_POLICY_NEVER, GTK_POLICY_NEVER);
/* Viewport */
viewport = gtk_viewport_new(NULL,NULL);
banner.viewport = viewport;
gtk_container_add(GTK_CONTAINER(banner.scrolled_win),viewport);
if(notify_config.banner_enable_colors) {
gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
gtk_widget_modify_bg(viewport,GTK_STATE_NORMAL,&bg);
}
/* Hbox */
hbox = gtk_hbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(viewport),hbox);
/* Entrybox */
entrybox = create_entrybox(msg_list);
gtk_box_pack_start(GTK_BOX(hbox), entrybox, FALSE, FALSE, 0);
gtk_widget_show_all(banner.window);
/* Scrolling */
gtk_widget_size_request(hbox, &requisition);
if(notify_config.banner_width > 0)
banner_width = notify_config.banner_width;
else
banner_width = gdk_screen_width();
if(requisition.width > banner_width) {
/* Line is too big for screen! */
/* Double the entrybox into hbox */
GtkWidget *separator, *second_entrybox;
separator = gtk_vseparator_new();
gtk_box_pack_start(GTK_BOX(hbox), separator,
FALSE, FALSE, 0);
second_entrybox = create_entrybox(msg_list);
gtk_box_pack_start(GTK_BOX(hbox), second_entrybox, FALSE, FALSE, 0);
gtk_widget_show_all(banner.window);
gtk_widget_size_request(hbox, &requisition_after);
G_LOCK(sdata);
sdata.banner_width = requisition_after.width - requisition.width;
sdata.adj =
gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW
(banner.scrolled_win));
G_UNLOCK(sdata);
banner.scrolling = TRUE;
if(banner.timeout_id) {
g_source_remove(banner.timeout_id);
banner.timeout_id = 0;
}
banner.timeout_id =
g_timeout_add(notify_config.banner_speed, scroller, NULL);
}
else {
banner.scrolling = FALSE;
if(banner.timeout_id) {
g_source_remove(banner.timeout_id);
banner.timeout_id = 0;
}
G_LOCK(sdata);
sdata.banner_width = 0;
sdata.adj = NULL;
G_UNLOCK(sdata);
}
/* menu */
banner_ui_manager = gtk_ui_manager_new();
banner_action_group = cm_menu_create_action_group_full(banner_ui_manager,"BannerPopup", banner_popup_entries,
G_N_ELEMENTS(banner_popup_entries), (gpointer)NULL);
MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/", "Menus", "Menus", GTK_UI_MANAGER_MENUBAR)
MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus", "BannerPopup", "BannerPopup", GTK_UI_MANAGER_MENU)
MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus/BannerPopup", "Reply", "BannerPopup/Reply", GTK_UI_MANAGER_MENUITEM)
banner_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
gtk_ui_manager_get_widget(banner_ui_manager, "/Menus/BannerPopup")) );
g_signal_connect(banner_popup,"selection-done",
G_CALLBACK(notification_banner_popup_done), NULL);
}
static gboolean notification_banner_configure(GtkWidget *widget,
GdkEventConfigure *event,
gpointer data)
{
gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
&(notify_config.banner_root_x),
&(notify_config.banner_root_y));
return TRUE;
}
static gboolean scroller(gpointer data)
{
// don't scroll during popup open
if(banner_popup_open)
return banner.scrolling;
while(gtk_events_pending())
gtk_main_iteration();
G_LOCK(sdata);
if(sdata.adj && GTK_IS_ADJUSTMENT(sdata.adj)) {
if(sdata.adj->value != sdata.banner_width)
gtk_adjustment_set_value(sdata.adj, sdata.adj->value + 1);
else
gtk_adjustment_set_value(sdata.adj, 0);
gtk_adjustment_value_changed(sdata.adj);
}
G_UNLOCK(sdata);
while(gtk_events_pending())
gtk_main_iteration();
return banner.scrolling;
}
static GtkWidget* create_entrybox(GSList *msg_list)
{
GtkWidget *entrybox;
GdkColor fg;
GdkColor bg;
gint list_length;
list_length = g_slist_length(msg_list);
if(notify_config.banner_enable_colors) {
gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
gtkut_convert_int_to_gdk_color(notify_config.banner_color_fg,&fg);
}
if(banner.entries) {
g_free(banner.entries);
banner.entries = NULL;
}
entrybox = gtk_hbox_new(FALSE, 5);
if(list_length) {
GSList *walk;
gint ii = 0;
banner.entries = g_new(NotificationBannerEntry, list_length);
for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *label3;
GtkWidget *label4;
GtkWidget *label5;
GtkWidget *label6;
GtkWidget *ebox;
CollectedMsg *cmsg = walk->data;
if(ii > 0) {
GtkWidget *separator;
separator = gtk_vseparator_new();
gtk_box_pack_start(GTK_BOX(entrybox), separator, FALSE, FALSE, 0);
}
ebox = gtk_event_box_new();
gtk_box_pack_start(GTK_BOX(entrybox), ebox, FALSE, FALSE, 0);
gtk_widget_set_events(ebox,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK);
if(notify_config.banner_enable_colors)
gtk_widget_modify_bg(ebox,GTK_STATE_NORMAL,&bg);
banner.entries[ii].table = gtk_table_new(3, 2, FALSE);
gtk_container_add(GTK_CONTAINER(ebox),banner.entries[ii].table);
g_signal_connect(ebox, "enter-notify-event",
G_CALLBACK(notification_banner_swap_colors),
banner.entries[ii].table);
g_signal_connect(ebox, "leave-notify-event",
G_CALLBACK(notification_banner_swap_colors),
banner.entries[ii].table);
g_signal_connect(ebox, "button-press-event",
G_CALLBACK(notification_banner_button_press),
cmsg);
label1 = gtk_label_new(prefs_common_translated_header_name("From:"));
gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label1, 0, 1, 0, 1);
label2 = gtk_label_new(prefs_common_translated_header_name("Subject:"));
gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label2, 0, 1, 1, 2);
label3 = gtk_label_new(_("Folder:"));
gtk_misc_set_alignment(GTK_MISC(label3), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label3, 0, 1, 2, 3);
label4 = gtk_label_new(cmsg->from);
gtk_misc_set_alignment(GTK_MISC(label4), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label4, 1, 2, 0, 1);
label5 = gtk_label_new(cmsg->subject);
gtk_misc_set_alignment(GTK_MISC(label5), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label5, 1, 2, 1, 2);
label6 = gtk_label_new(cmsg->folderitem_name);
gtk_misc_set_alignment(GTK_MISC(label6), 0, 0.5);
gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
label6, 1, 2, 2, 3);
gtk_table_set_col_spacings(GTK_TABLE(banner.entries[ii].table), 5);
ii++;
/* Color */
if(notify_config.banner_enable_colors) {
gtk_widget_modify_fg(label1,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(label2,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(label3,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(label4,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(label5,GTK_STATE_NORMAL,&fg);
gtk_widget_modify_fg(label6,GTK_STATE_NORMAL,&fg);
}
}
}
else {
/* We have an empty list -- create an empty dummy element */
GtkWidget *label;
banner.entries = g_new(NotificationBannerEntry, 1);
banner.entries[0].table = gtk_table_new(3, 1, FALSE);
label = gtk_label_new("");
gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
label, 0, 1, 0, 1);
label = gtk_label_new("");
gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
label, 0, 1, 1, 2);
label = gtk_label_new("");
gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
label, 0, 1, 2, 3);
gtk_box_pack_start(GTK_BOX(entrybox), banner.entries[0].table,
FALSE, FALSE, 0);
}
return entrybox;
}
static gboolean notification_banner_swap_colors(GtkWidget *widget,
GdkEventCrossing *event,
gpointer data)
{
GList *children;
GList *walk;
GdkColor *old_bg;
children = gtk_container_get_children(GTK_CONTAINER(data));
old_bg = gdk_color_copy(&(gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL]));
if(children)
gtk_widget_modify_bg(widget,GTK_STATE_NORMAL,
&(gtk_widget_get_style(GTK_WIDGET(children->data))->fg[GTK_STATE_NORMAL]));
for(walk = children; walk; walk = walk->next)
gtk_widget_modify_fg(GTK_WIDGET(walk->data),GTK_STATE_NORMAL,old_bg);
g_list_free(children);
gdk_color_free(old_bg);
return FALSE;
}
static gboolean notification_banner_button_press(GtkWidget *widget,
GdkEventButton *button,
gpointer data)
{
gboolean done;
done = FALSE;
if(button->type == GDK_BUTTON_PRESS) {
CollectedMsg *cmsg = (CollectedMsg*) data;
if(button->button == 1) {
/* jump to that message */
if(cmsg->msginfo) {
gchar *path;
path = procmsg_get_message_file_path(cmsg->msginfo);
mainwindow_jump_to(path, TRUE);
g_free(path);
}
done = TRUE;
}
else if(button->button == 2) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
button->button, button->x_root, button->y_root,
button->time);
}
else if(button->button == 3) {
current_msginfo = cmsg->msginfo;
notification_banner_show_popup(widget, button);
banner_popup_open = TRUE;
done = TRUE;
}
}
return done;
}
static void notification_banner_show_popup(GtkWidget *widget,
GdkEventButton *event)
{
int button, event_time;
if(event) {
button = event->button;
event_time = event->time;
}
else {
button = 0;
event_time = gtk_get_current_event_time();
}
gtk_menu_popup(GTK_MENU(banner_popup), NULL, NULL, NULL, NULL,
button, event_time);
}
static void notification_banner_popup_done(GtkMenuShell *menushell,
gpointer user_data)
{
current_msginfo = NULL;
banner_popup_open = FALSE;
}
static void banner_menu_reply_cb(GtkAction *action, gpointer data)
{
MainWindow *mainwin;
MessageView *messageview;
GSList *msginfo_list = NULL;
if(!(mainwin = mainwindow_get_mainwindow()))
return;
if(!(messageview = (MessageView*)mainwin->messageview))
return;
g_return_if_fail(current_msginfo);
msginfo_list = g_slist_prepend(msginfo_list, current_msginfo);
compose_reply_from_messageview(messageview, msginfo_list,
prefs_common_get_prefs()->reply_with_quote ?
COMPOSE_REPLY_WITH_QUOTE : COMPOSE_REPLY_WITHOUT_QUOTE);
g_slist_free(msginfo_list);
}
#endif /* NOTIFICATION_BANNER */