1 /* Notification plugin for Claws Mail
2 * Copyright (C) 2005-2008 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_BANNER
30 #include "gtk/gtkutils.h"
32 #include "prefs_common.h"
33 #include "mainwindow.h"
36 #include "messageview.h"
40 #include "notification_core.h"
41 #include "notification_prefs.h"
42 #include "notification_banner.h"
46 } NotificationBannerEntry;
50 GtkWidget *scrolled_win;
53 NotificationBannerEntry *entries;
64 static void notification_banner_create(GSList*);
65 static gboolean scroller(gpointer data);
66 static GtkWidget* create_entrybox(GSList*);
67 static gboolean notification_banner_configure(GtkWidget*, GdkEventConfigure*,
69 static gboolean notification_banner_swap_colors(GtkWidget*,GdkEventCrossing*,gpointer);
70 static gboolean notification_banner_button_press(GtkWidget*,GdkEventButton*,gpointer);
71 static void notification_banner_show_popup(GtkWidget*,GdkEventButton*);
72 static void notification_banner_popup_done(GtkMenuShell*,gpointer);
74 static void banner_menu_reply_cb(GtkAction *,gpointer);
77 static NotificationBanner banner;
78 static ScrollingData sdata;
80 static GtkWidget *banner_popup;
81 static GtkUIManager *banner_ui_manager;
82 static GtkActionGroup *banner_action_group;
84 static gboolean banner_popup_open = FALSE;
86 static MsgInfo *current_msginfo = NULL;
88 /* Corresponding mutexes */
89 G_LOCK_DEFINE_STATIC(banner);
90 G_LOCK_DEFINE_STATIC(sdata);
92 static GtkActionEntry banner_popup_entries[] =
94 {"BannerPopup", NULL, "BannerPopup" },
95 {"BannerPopup/Reply", NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(banner_menu_reply_cb) },
99 void notification_banner_show(GSList *msg_list)
102 if((notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) &&
103 (g_slist_length(msg_list) ||
104 (notify_config.banner_show == NOTIFY_BANNER_SHOW_ALWAYS)))
105 notification_banner_create(msg_list);
107 notification_banner_destroy();
111 void notification_banner_destroy(void)
115 g_free(banner.entries);
116 banner.entries = NULL;
118 gtk_widget_destroy(banner.window);
119 banner.window = NULL;
122 sdata.banner_width = 0;
124 if(banner.timeout_id) {
125 g_source_remove(banner.timeout_id);
126 banner.timeout_id = 0;
131 static void notification_banner_create(GSList *msg_list)
133 GtkRequisition requisition, requisition_after;
143 banner.window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_banner");
144 gtk_window_set_decorated(GTK_WINDOW(banner.window), FALSE);
145 if(notify_config.banner_width > 0)
146 gtk_widget_set_size_request(banner.window, notify_config.banner_width, -1);
148 gtk_widget_set_size_request(banner.window, gdk_screen_width(), -1);
149 gtk_window_set_keep_above(GTK_WINDOW(banner.window), TRUE);
150 gtk_window_set_accept_focus(GTK_WINDOW(banner.window), FALSE);
151 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(banner.window), TRUE);
152 gtk_window_move(GTK_WINDOW(banner.window),
153 notify_config.banner_root_x, notify_config.banner_root_y);
154 g_signal_connect(banner.window, "configure-event",
155 G_CALLBACK(notification_banner_configure), NULL);
159 g_free(banner.entries);
160 banner.entries = NULL;
162 gtk_widget_destroy(banner.scrolled_win);
164 if(notify_config.banner_sticky)
165 gtk_window_stick(GTK_WINDOW(banner.window));
167 gtk_window_unstick(GTK_WINDOW(banner.window));
169 /* Scrolled window */
170 banner.scrolled_win = gtk_scrolled_window_new(NULL, NULL);
171 gtk_container_add(GTK_CONTAINER(banner.window), banner.scrolled_win);
172 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(banner.scrolled_win),
173 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
176 viewport = gtk_viewport_new(NULL,NULL);
177 banner.viewport = viewport;
178 gtk_container_add(GTK_CONTAINER(banner.scrolled_win),viewport);
179 if(notify_config.banner_enable_colors) {
180 gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
181 gtk_widget_modify_bg(viewport,GTK_STATE_NORMAL,&bg);
185 hbox = gtk_hbox_new(FALSE, 5);
186 gtk_container_add(GTK_CONTAINER(viewport),hbox);
189 entrybox = create_entrybox(msg_list);
190 gtk_box_pack_start(GTK_BOX(hbox), entrybox, FALSE, FALSE, 0);
192 gtk_widget_show_all(banner.window);
195 gtk_widget_size_request(hbox, &requisition);
196 if(notify_config.banner_width > 0)
197 banner_width = notify_config.banner_width;
199 banner_width = gdk_screen_width();
200 if(requisition.width > banner_width) {
201 /* Line is too big for screen! */
202 /* Double the entrybox into hbox */
203 GtkWidget *separator, *second_entrybox;
205 separator = gtk_vseparator_new();
206 gtk_box_pack_start(GTK_BOX(hbox), separator,
208 second_entrybox = create_entrybox(msg_list);
209 gtk_box_pack_start(GTK_BOX(hbox), second_entrybox, FALSE, FALSE, 0);
211 gtk_widget_show_all(banner.window);
212 gtk_widget_size_request(hbox, &requisition_after);
215 sdata.banner_width = requisition_after.width - requisition.width;
217 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW
218 (banner.scrolled_win));
221 banner.scrolling = TRUE;
222 if(banner.timeout_id) {
223 g_source_remove(banner.timeout_id);
224 banner.timeout_id = 0;
227 g_timeout_add(notify_config.banner_speed, scroller, NULL);
230 banner.scrolling = FALSE;
231 if(banner.timeout_id) {
232 g_source_remove(banner.timeout_id);
233 banner.timeout_id = 0;
236 sdata.banner_width = 0;
242 banner_ui_manager = gtk_ui_manager_new();
243 banner_action_group = cm_menu_create_action_group_full(banner_ui_manager,"BannerPopup", banner_popup_entries,
244 G_N_ELEMENTS(banner_popup_entries), (gpointer)NULL);
245 MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/", "Menus", "Menus", GTK_UI_MANAGER_MENUBAR)
246 MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus", "BannerPopup", "BannerPopup", GTK_UI_MANAGER_MENU)
247 MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus/BannerPopup", "Reply", "BannerPopup/Reply", GTK_UI_MANAGER_MENUITEM)
249 banner_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
250 gtk_ui_manager_get_widget(banner_ui_manager, "/Menus/BannerPopup")) );
251 g_signal_connect(banner_popup,"selection-done",
252 G_CALLBACK(notification_banner_popup_done), NULL);
255 static gboolean notification_banner_configure(GtkWidget *widget,
256 GdkEventConfigure *event,
259 gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
260 &(notify_config.banner_root_x),
261 &(notify_config.banner_root_y));
265 static gboolean scroller(gpointer data)
267 // don't scroll during popup open
268 if(banner_popup_open)
269 return banner.scrolling;
271 while(gtk_events_pending())
272 gtk_main_iteration();
274 if(sdata.adj && GTK_IS_ADJUSTMENT(sdata.adj)) {
275 if(sdata.adj->value != sdata.banner_width)
276 gtk_adjustment_set_value(sdata.adj, sdata.adj->value + 1);
278 gtk_adjustment_set_value(sdata.adj, 0);
279 gtk_adjustment_value_changed(sdata.adj);
282 while(gtk_events_pending())
283 gtk_main_iteration();
284 return banner.scrolling;
287 static GtkWidget* create_entrybox(GSList *msg_list)
294 list_length = g_slist_length(msg_list);
296 if(notify_config.banner_enable_colors) {
297 gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
298 gtkut_convert_int_to_gdk_color(notify_config.banner_color_fg,&fg);
302 g_free(banner.entries);
303 banner.entries = NULL;
306 entrybox = gtk_hbox_new(FALSE, 5);
310 banner.entries = g_new(NotificationBannerEntry, list_length);
311 for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
319 CollectedMsg *cmsg = walk->data;
322 GtkWidget *separator;
323 separator = gtk_vseparator_new();
324 gtk_box_pack_start(GTK_BOX(entrybox), separator, FALSE, FALSE, 0);
327 ebox = gtk_event_box_new();
328 gtk_box_pack_start(GTK_BOX(entrybox), ebox, FALSE, FALSE, 0);
329 gtk_widget_set_events(ebox,
330 GDK_POINTER_MOTION_MASK |
331 GDK_BUTTON_PRESS_MASK);
333 if(notify_config.banner_enable_colors)
334 gtk_widget_modify_bg(ebox,GTK_STATE_NORMAL,&bg);
336 banner.entries[ii].table = gtk_table_new(3, 2, FALSE);
337 gtk_container_add(GTK_CONTAINER(ebox),banner.entries[ii].table);
338 g_signal_connect(ebox, "enter-notify-event",
339 G_CALLBACK(notification_banner_swap_colors),
340 banner.entries[ii].table);
341 g_signal_connect(ebox, "leave-notify-event",
342 G_CALLBACK(notification_banner_swap_colors),
343 banner.entries[ii].table);
344 g_signal_connect(ebox, "button-press-event",
345 G_CALLBACK(notification_banner_button_press),
348 label1 = gtk_label_new(prefs_common_translated_header_name("From:"));
349 gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
350 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
352 label2 = gtk_label_new(prefs_common_translated_header_name("Subject:"));
353 gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
354 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
356 label3 = gtk_label_new(_("Folder:"));
357 gtk_misc_set_alignment(GTK_MISC(label3), 0, 0.5);
358 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
361 label4 = gtk_label_new(cmsg->from);
362 gtk_misc_set_alignment(GTK_MISC(label4), 0, 0.5);
363 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
365 label5 = gtk_label_new(cmsg->subject);
366 gtk_misc_set_alignment(GTK_MISC(label5), 0, 0.5);
367 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
369 label6 = gtk_label_new(cmsg->folderitem_name);
370 gtk_misc_set_alignment(GTK_MISC(label6), 0, 0.5);
371 gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
373 gtk_table_set_col_spacings(GTK_TABLE(banner.entries[ii].table), 5);
376 if(notify_config.banner_enable_colors) {
377 gtk_widget_modify_fg(label1,GTK_STATE_NORMAL,&fg);
378 gtk_widget_modify_fg(label2,GTK_STATE_NORMAL,&fg);
379 gtk_widget_modify_fg(label3,GTK_STATE_NORMAL,&fg);
380 gtk_widget_modify_fg(label4,GTK_STATE_NORMAL,&fg);
381 gtk_widget_modify_fg(label5,GTK_STATE_NORMAL,&fg);
382 gtk_widget_modify_fg(label6,GTK_STATE_NORMAL,&fg);
387 /* We have an empty list -- create an empty dummy element */
390 banner.entries = g_new(NotificationBannerEntry, 1);
391 banner.entries[0].table = gtk_table_new(3, 1, FALSE);
392 label = gtk_label_new("");
393 gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
395 label = gtk_label_new("");
396 gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
398 label = gtk_label_new("");
399 gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
401 gtk_box_pack_start(GTK_BOX(entrybox), banner.entries[0].table,
407 static gboolean notification_banner_swap_colors(GtkWidget *widget,
408 GdkEventCrossing *event,
415 children = gtk_container_get_children(GTK_CONTAINER(data));
417 old_bg = gdk_color_copy(&(gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL]));
419 gtk_widget_modify_bg(widget,GTK_STATE_NORMAL,
420 &(gtk_widget_get_style(GTK_WIDGET(children->data))->fg[GTK_STATE_NORMAL]));
422 for(walk = children; walk; walk = walk->next)
423 gtk_widget_modify_fg(GTK_WIDGET(walk->data),GTK_STATE_NORMAL,old_bg);
425 g_list_free(children);
426 gdk_color_free(old_bg);
430 static gboolean notification_banner_button_press(GtkWidget *widget,
431 GdkEventButton *button,
436 if(button->type == GDK_BUTTON_PRESS) {
437 CollectedMsg *cmsg = (CollectedMsg*) data;
438 if(button->button == 1) {
439 /* jump to that message */
442 path = procmsg_get_message_file_path(cmsg->msginfo);
443 mainwindow_jump_to(path, TRUE);
448 else if(button->button == 2) {
449 gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
450 button->button, button->x_root, button->y_root,
453 else if(button->button == 3) {
454 current_msginfo = cmsg->msginfo;
455 notification_banner_show_popup(widget, button);
456 banner_popup_open = TRUE;
463 static void notification_banner_show_popup(GtkWidget *widget,
464 GdkEventButton *event)
466 int button, event_time;
469 button = event->button;
470 event_time = event->time;
474 event_time = gtk_get_current_event_time();
477 gtk_menu_popup(GTK_MENU(banner_popup), NULL, NULL, NULL, NULL,
481 static void notification_banner_popup_done(GtkMenuShell *menushell,
484 current_msginfo = NULL;
485 banner_popup_open = FALSE;
488 static void banner_menu_reply_cb(GtkAction *action, gpointer data)
491 MessageView *messageview;
492 GSList *msginfo_list = NULL;
494 if(!(mainwin = mainwindow_get_mainwindow()))
497 if(!(messageview = (MessageView*)mainwin->messageview))
500 g_return_if_fail(current_msginfo);
502 msginfo_list = g_slist_prepend(msginfo_list, current_msginfo);
503 compose_reply_from_messageview(messageview, msginfo_list,
504 prefs_common_get_prefs()->reply_with_quote ?
505 COMPOSE_REPLY_WITH_QUOTE : COMPOSE_REPLY_WITHOUT_QUOTE);
506 g_slist_free(msginfo_list);
509 #endif /* NOTIFICATION_BANNER */