[Commits] Makefile.am NONE 1.1.2.1 claws.def NONE 1.1.2.1 notification_banner.c NONE 1.1.2.1 notification_banner.h NONE 1.1.2.1 notification_command.c NONE 1.1.2.1 notification_command.h NONE 1.1.2.1 notification_core.c NONE 1.1.2.1 notification_core.h NONE 1.1.2.1 notification_foldercheck.c NONE 1.1.2.1 notification_foldercheck.h NONE 1.1.2.1 notification_hotkeys.c NONE 1.1.2.1 notification_hotkeys.h NONE 1.1.2.1 notification_indicator.c NONE 1.1.2.1 notification_indicator.h NONE 1.1.2.1 notification_lcdproc.c NONE 1.1.2.1 notification_lcdproc.h NONE 1.1.2.1 notification_pixbuf.c NONE 1.1.2.1 notification_pixbuf.h NONE 1.1.2.1 notification_plugin.c NONE 1.1.2.1 notification_plugin.h NONE 1.1.2.1 notification_popup.c NONE 1.1.2.1 notification_popup.h NONE 1.1.2.1 notification_prefs.c NONE 1.1.2.1 notification_prefs.h NONE 1.1.2.1 notification_trayicon.c NONE 1.1.2.1 notification_trayicon.h NONE 1.1.2.1 placeholder.txt 1.1.2.1 NONE plugin.def NONE 1.1.2.1 raw_claws_mail_logo_64x64.h NONE 1.1.2.1 version.rc NONE 1.1.2.1
colin at claws-mail.org
colin at claws-mail.org
Sun Feb 17 22:22:01 CET 2013
Update of /home/claws-mail/claws/src/plugins/notification
In directory srv:/tmp/cvs-serv7104/src/plugins/notification
Added Files:
Tag: gtk2
Makefile.am claws.def notification_banner.c
notification_banner.h notification_command.c
notification_command.h notification_core.c notification_core.h
notification_foldercheck.c notification_foldercheck.h
notification_hotkeys.c notification_hotkeys.h
notification_indicator.c notification_indicator.h
notification_lcdproc.c notification_lcdproc.h
notification_pixbuf.c notification_pixbuf.h
notification_plugin.c notification_plugin.h
notification_popup.c notification_popup.h notification_prefs.c
notification_prefs.h notification_trayicon.c
notification_trayicon.h plugin.def raw_claws_mail_logo_64x64.h
version.rc
Removed Files:
Tag: gtk2
placeholder.txt
Log Message:
2013-02-17 [colin] 3.9.0cvs75
* src/plugins/Makefile.am
* src/plugins/archive/Makefile.am
* src/plugins/clamd/Makefile.am
* src/plugins/clamd/clamav_plugin.c
* src/plugins/clamd/clamav_plugin.h
* src/plugins/clamd/clamav_plugin_gtk.c
* src/plugins/clamd/placeholder.txt
* src/plugins/clamd/libclamd/Makefile.am
* src/plugins/clamd/libclamd/clamd-plugin.c
* src/plugins/clamd/libclamd/clamd-plugin.h
* src/plugins/fetchinfo/Makefile.am
* src/plugins/fetchinfo/fetchinfo_plugin.c
* src/plugins/fetchinfo/fetchinfo_plugin.h
* src/plugins/fetchinfo/fetchinfo_plugin_gtk.c
* src/plugins/fetchinfo/placeholder.txt
* src/plugins/gdata/Makefile.am
* src/plugins/gdata/cm_gdata_contacts.c
* src/plugins/gdata/cm_gdata_contacts.h
* src/plugins/gdata/cm_gdata_prefs.c
* src/plugins/gdata/cm_gdata_prefs.h
* src/plugins/gdata/gdata_plugin.c
* src/plugins/gdata/gdata_plugin.h
* src/plugins/gdata/placeholder.txt
* src/plugins/geolocation/placeholder.txt
* src/plugins/gtkhtml2_viewer/placeholder.txt
* src/plugins/mailmbox/Makefile.am
* src/plugins/mailmbox/carray.c
* src/plugins/mailmbox/carray.h
* src/plugins/mailmbox/chash.c
* src/plugins/mailmbox/chash.h
* src/plugins/mailmbox/clist.c
* src/plugins/mailmbox/clist.h
* src/plugins/mailmbox/mailimf.c
* src/plugins/mailmbox/mailimf.h
* src/plugins/mailmbox/mailimf_types.c
* src/plugins/mailmbox/mailimf_types.h
* src/plugins/mailmbox/mailimf_types_helper.c
* src/plugins/mailmbox/mailimf_types_helper.h
* src/plugins/mailmbox/mailimf_write.c
* src/plugins/mailmbox/mailimf_write.h
* src/plugins/mailmbox/maillock.c
* src/plugins/mailmbox/maillock.h
* src/plugins/mailmbox/mailmbox.c
* src/plugins/mailmbox/mailmbox.h
* src/plugins/mailmbox/mailmbox_folder.c
* src/plugins/mailmbox/mailmbox_folder.h
* src/plugins/mailmbox/mailmbox_gtk.deps
* src/plugins/mailmbox/mailmbox_parse.c
* src/plugins/mailmbox/mailmbox_parse.h
* src/plugins/mailmbox/mailmbox_types.c
* src/plugins/mailmbox/mailmbox_types.h
* src/plugins/mailmbox/mmapstring.c
* src/plugins/mailmbox/mmapstring.h
* src/plugins/mailmbox/placeholder.txt
* src/plugins/mailmbox/plugin.c
* src/plugins/mailmbox/plugin_gtk.c
* src/plugins/mailmbox/plugin_gtk.h
* src/plugins/newmail/Makefile.am
* src/plugins/newmail/newmail.c
* src/plugins/newmail/placeholder.txt
* src/plugins/notification/Makefile.am
* src/plugins/notification/claws.def
* src/plugins/notification/notification_banner.c
* src/plugins/notification/notification_banner.h
* src/plugins/notification/notification_command.c
* src/plugins/notification/notification_command.h
* src/plugins/notification/notification_core.c
* src/plugins/notification/notification_core.h
* src/plugins/notification/notification_foldercheck.c
* src/plugins/notification/notification_foldercheck.h
* src/plugins/notification/notification_hotkeys.c
* src/plugins/notification/notification_hotkeys.h
* src/plugins/notification/notification_indicator.c
* src/plugins/notification/notification_indicator.h
* src/plugins/notification/notification_lcdproc.c
* src/plugins/notification/notification_lcdproc.h
* src/plugins/notification/notification_pixbuf.c
* src/plugins/notification/notification_pixbuf.h
* src/plugins/notification/notification_plugin.c
* src/plugins/notification/notification_plugin.h
* src/plugins/notification/notification_popup.c
* src/plugins/notification/notification_popup.h
* src/plugins/notification/notification_prefs.c
* src/plugins/notification/notification_prefs.h
* src/plugins/notification/notification_trayicon.c
* src/plugins/notification/notification_trayicon.h
* src/plugins/notification/placeholder.txt
* src/plugins/notification/plugin.def
* src/plugins/notification/raw_claws_mail_logo_64x64.h
* src/plugins/notification/version.rc
* src/plugins/pdf_viewer/Makefile.am
* src/plugins/pdf_viewer/doc_index.xpm
* src/plugins/pdf_viewer/doc_index_close.xpm
* src/plugins/pdf_viewer/doc_info.xpm
* src/plugins/pdf_viewer/first_arrow.xpm
* src/plugins/pdf_viewer/last_arrow.xpm
* src/plugins/pdf_viewer/left_arrow.xpm
* src/plugins/pdf_viewer/placeholder.txt
* src/plugins/pdf_viewer/poppler_viewer.c
* src/plugins/pdf_viewer/poppler_viewer.h
* src/plugins/pdf_viewer/right_arrow.xpm
* src/plugins/pdf_viewer/rotate_left.xpm
* src/plugins/pdf_viewer/rotate_right.xpm
* src/plugins/pdf_viewer/zoom_fit.xpm
* src/plugins/pdf_viewer/zoom_in.xpm
* src/plugins/pdf_viewer/zoom_out.xpm
* src/plugins/pdf_viewer/zoom_width.xpm
* src/plugins/perl/Makefile.am
* src/plugins/perl/perl_gtk.c
* src/plugins/perl/perl_gtk.h
* src/plugins/perl/perl_plugin.c
* src/plugins/perl/perl_plugin.h
* src/plugins/perl/placeholder.txt
* src/plugins/python/Makefile.am
* src/plugins/python/clawsmailmodule.c
* src/plugins/python/clawsmailmodule.h
* src/plugins/python/composewindowtype.c
* src/plugins/python/composewindowtype.h
* src/plugins/python/foldertype.c
* src/plugins/python/foldertype.h
* src/plugins/python/messageinfotype.c
* src/plugins/python/messageinfotype.h
* src/plugins/python/nodetype.c
* src/plugins/python/nodetype.h
* src/plugins/python/placeholder.txt
* src/plugins/python/python-hooks.c
* src/plugins/python/python-hooks.h
* src/plugins/python/python-shell.c
* src/plugins/python/python-shell.h
* src/plugins/python/python_plugin.c
* src/plugins/vcalendar/Makefile.in
Add some plugins (clamd, fetchinfo, gdata, mailmbox, newmail,
notification, pdf_viewer, perl, python). Notification not yet
enabled because it has too much autoconf switches for my taste.
--- NEW FILE: notification_foldercheck.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_FOLDERCHECK_H
#define NOTIFICATION_FOLDERCHECK_H NOTIFICATION_FOLDERCHECK_H
#include <gtk/gtk.h>
#include "pluginconfig.h"
void notification_foldercheck_sel_folders_cb(GtkButton*, gpointer);
guint notification_register_folder_specific_list(gchar*);
GSList* notification_foldercheck_get_list(guint);
void notification_free_folder_specific_array(void);
void notification_foldercheck_write_array(void);
gboolean notification_foldercheck_read_array(void);
#endif /* NOTIFICATION_FOLDERCHECK_H */
--- NEW FILE: notification_banner.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_BANNER
#include <gtk/gtk.h>
#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 */
--- NEW FILE: notification_banner.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_BANNER_H
#define NOTIFICATION_BANNER_H NOTIFICATION_BANNER_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_BANNER
#define BANNER_SPECIFIC_FOLDER_ID_STR "banner"
void notification_banner_show(GSList*);
void notification_banner_destroy(void);
#endif /* NOTIFICATION_BANNER */
#endif /* NOTIFICATION_BANNER_H */
--- placeholder.txt DELETED ---
--- NEW FILE: notification_foldercheck.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
/* This code is based on foldersel.c in Claws Mail.
* Some functions are only slightly modified, almost 1:1 copies from there. */
[...963 lines suppressed...]
FolderItem *item = hookdata->item;
/* If that folder is in anywhere in the array, cut it out. */
for(ii = 0; ii < specific_folder_array_size; ii++) {
entry = foldercheck_get_entry_from_id(ii);
entry->list = g_slist_remove(entry->list, item);
} /* for all entries in the array */
} /* A FolderItem was deleted */
return FALSE;
}
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
if(event && (event->keyval == GDK_Escape)) {
foldercheck_cancel(NULL, data);
return TRUE;
}
return FALSE;
}
--- NEW FILE: notification_prefs.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_PREFS_H
#define NOTIFICATION_PREFS_H NOTIFICATION_PREFS_H
#include "pluginconfig.h"
#include <glib.h>
#include "prefs_gtk.h"
#include "notification_banner.h"
#ifdef NOTIFICATION_BANNER
typedef enum {
NOTIFY_BANNER_SHOW_NEVER = 0,
NOTIFY_BANNER_SHOW_ALWAYS,
NOTIFY_BANNER_SHOW_NONEMPTY
} NotifyBannerShow;
#endif
typedef struct {
gboolean include_mail;
gboolean include_news;
gboolean include_rss;
gboolean include_calendar;
gboolean urgency_hint_new;
gboolean urgency_hint_unread;
#ifdef HAVE_LIBCANBERRA_GTK
gboolean canberra_play_sounds;
#endif
#ifdef NOTIFICATION_BANNER
NotifyBannerShow banner_show;
gint banner_speed;
gboolean banner_include_unread;
gint banner_max_msgs;
gboolean banner_sticky;
gint banner_root_x;
gint banner_root_y;
gboolean banner_folder_specific;
gboolean banner_enable_colors;
gulong banner_color_bg;
gulong banner_color_fg;
gint banner_width;
#endif
#ifdef NOTIFICATION_POPUP
gboolean popup_show;
gint popup_timeout;
gboolean popup_folder_specific;
#ifndef HAVE_LIBNOTIFY
gboolean popup_sticky;
gint popup_root_x;
gint popup_root_y;
gint popup_width;
gboolean popup_enable_colors;
gulong popup_color_bg;
gulong popup_color_fg;
#else /* HAVE_LIBNOTIFY */
gboolean popup_display_folder_name;
#endif /* HAVE_LIBNOTIFY */
#endif /* Popup */
#ifdef NOTIFICATION_COMMAND
gboolean command_enabled;
gint command_timeout;
gboolean command_folder_specific;
gchar* command_line;
#endif
#ifdef NOTIFICATION_LCDPROC
gboolean lcdproc_enabled;
gchar* lcdproc_hostname;
guint lcdproc_port;
#endif
#ifdef NOTIFICATION_TRAYICON
gboolean trayicon_enabled;
gboolean trayicon_hide_at_startup;
gboolean trayicon_close_to_tray;
gboolean trayicon_hide_when_iconified;
gboolean trayicon_folder_specific;
#ifdef HAVE_LIBNOTIFY
gboolean trayicon_display_folder_name;
gboolean trayicon_popup_enabled;
gint trayicon_popup_timeout;
#endif /* HAVE_LIBNOTIFY */
#endif /* Trayicon */
#ifdef NOTIFICATION_INDICATOR
gboolean indicator_enabled;
gboolean indicator_hide_minimized;
#endif /* NOTIFICATION_INDICATOR */
#ifdef NOTIFICATION_HOTKEYS
gboolean hotkeys_enabled;
gchar* hotkeys_toggle_mainwindow;
#endif /* Hotkeys */
} NotifyPrefs;
extern NotifyPrefs notify_config;
extern PrefParam notify_param[];
void notify_gtk_init(void);
void notify_gtk_done(void);
void notify_save_config(void);
#endif /* NOTIFICATION_PREFS_H */
--- NEW FILE: notification_prefs.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
[...1913 lines suppressed...]
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hotkeys_page.hotkeys_enabled));
gtk_widget_set_sensitive(hotkeys_page.hotkeys_cont_enable, active);
}
#endif /* hotkeys */
/* This feels so wrong... */
static gint conv_color_to_int(GdkColor *color)
{
gint result;
guint red, green, blue;
red = (guint) ((gdouble)(color->red) /65535.*255.);
green = (guint) ((gdouble)(color->green) /65535.*255.);
blue = (guint) ((gdouble)(color->blue) /65535.*255.);
result = (gint) (blue | (green<<8)| (red<<16));
return result;
}
--- NEW FILE: Makefile.am ---
if BUILD_HOTKEYS
hotkey_lib = libcmnpgtkhotkey.la
hotkey_lib_path = $(top_builddir)/src/plugins/notification/gtkhotkey/libcmnpgtkhotkey.la
else
hotkey_lib =
hotkey_lib_path =
endif
EXTRA_DIST = claws.def plugin.def version.rc
CFLAGS += "-Wall"
if OS_WIN32
LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RC) \
`echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
sed -e 's/-I/--include-dir /g;s/-D/--define /g'`
%.lo : %.rc
$(LTRCCOMPILE) -i $< -o $@
plugin_res = version.lo
plugin_res_ldflag = -Wl,.libs/version.o
export_symbols = -export-symbols $(srcdir)/plugin.def
plugin_deps = libclaws.a $(plugin_res) plugin.def $(hotkey_lib)
libclaws.a: claws.def
$(DLLTOOL) --output-lib $@ --def $<
plugin_ldadd = -L. -lclaws
else
plugin_res =
plugin_res_ldflag =
export_symbols =
plugin_deps =
plugin_ldadd =
endif
if PLATFORM_WIN32
no_undefined = -no-undefined
else
no_undefined =
endif
if CYGWIN
cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
else
cygwin_export_lib =
endif
plugindir = $(pkglibdir)/plugins
plugin_LTLIBRARIES = notification_plugin.la
notification_plugin_la_SOURCES = \
gettext.h \
notification_plugin.c notification_plugin.h \
notification_core.c notification_core.h \
notification_popup.c notification_popup.h \
notification_banner.c notification_banner.h \
notification_command.c notification_command.h \
notification_lcdproc.c notification_lcdproc.h \
notification_trayicon.c notification_trayicon.h \
notification_indicator.c notification_indicator.h \
notification_hotkeys.c notification_hotkeys.h \
notification_prefs.c notification_prefs.h \
notification_foldercheck.c notification_foldercheck.h \
notification_pixbuf.c notification_pixbuf.h \
raw_claws_mail_logo_64x64.h
notification_plugin_la_LDFLAGS = \
$(plugin_res_ldflag) $(no_undefined) $(export_symbols) \
-avoid-version -module
notification_plugin_la_DEPENDENCIES = $(plugin_deps) \
$(hotkey_lib_path)
INCLUDES = \
-I$(top_srcdir)/src/plugins/notification/gtkhotkey \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/gtk
notification_plugin_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
$(GTK_LIBS) \
$(libnotify_LIBS) \
$(libindicate_LIBS) \
$(libcanberra_gtk_LIBS) \
$(hotkey_lib_path)
notification_plugin_la_CPPFLAGS = \
$(NOTIFY_CFLAGS) \
$(CLAWS_MAIL_CFLAGS) \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
-DLOCALEDIR=\""$(localedir)"\" \
$(libnotify_CFLAGS) \
$(libindicate_CFLAGS) \
$(libcanberra_gtk_CFLAGS)
clean-local:
rm -f libclaws.a
--- NEW FILE: notification_plugin.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#include "common/claws.h"
#include "common/version.h"
#include "common/utils.h"
#include "common/defs.h"
#include "folder.h"
#include "common/hooks.h"
#include "procmsg.h"
#include "mainwindow.h"
#include "main.h"
#include "gtk/gtkutils.h"
#include "notification_plugin.h"
#include "notification_core.h"
#include "notification_prefs.h"
#include "notification_banner.h"
#include "notification_lcdproc.h"
#include "notification_trayicon.h"
#include "notification_indicator.h"
#include "notification_hotkeys.h"
#include "notification_foldercheck.h"
#include "notification_pixbuf.h"
#include "plugin.h"
#if HAVE_LIBNOTIFY
# include <libnotify/notify.h>
#endif
static gboolean my_folder_item_update_hook(gpointer, gpointer);
static gboolean my_folder_update_hook(gpointer, gpointer);
static gboolean my_msginfo_update_hook(gpointer, gpointer);
static gboolean my_offline_switch_hook(gpointer, gpointer);
static gboolean my_main_window_close_hook(gpointer, gpointer);
static gboolean my_main_window_got_iconified_hook(gpointer, gpointer);
static gboolean my_account_list_changed_hook(gpointer, gpointer);
static gboolean my_update_theme_hook(gpointer, gpointer);
#ifdef NOTIFICATION_TRAYICON
static gboolean trayicon_startup_idle(gpointer);
#endif
static guint hook_f_item;
static guint hook_f;
static guint hook_m_info;
static guint hook_offline;
static guint hook_mw_close;
static guint hook_got_iconified;
static guint hook_account;
static guint hook_theme_changed;
#ifdef NOTIFICATION_BANNER
static GSList* banner_collected_msgs;
#endif
void notification_update_urgency_hint(void)
{
MainWindow *mainwin;
mainwin = mainwindow_get_mainwindow();
if(mainwin) {
NotificationMsgCount count;
gboolean active;
active = FALSE;
if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread) {
notification_core_get_msg_count(NULL, &count);
if(notify_config.urgency_hint_new)
active = (active || (count.new_msgs > 0));
if(notify_config.urgency_hint_unread)
active = (active || (count.unread_msgs > 0));
}
gtk_window_set_urgency_hint(GTK_WINDOW(mainwin->window), active);
}
}
static gboolean my_account_list_changed_hook(gpointer source,
gpointer data)
{
gboolean retVal = FALSE;
#ifdef NOTIFICATION_TRAYICON
notification_update_msg_counts(NULL);
retVal = notification_trayicon_account_list_changed(source, data);
#endif
return retVal;
}
static gboolean my_update_theme_hook(gpointer source, gpointer data)
{
notification_pixbuf_free_all();
#ifdef NOTIFICATION_TRAYICON
notification_update_trayicon();
#endif
return FALSE;
}
static gboolean my_main_window_got_iconified_hook(gpointer source,
gpointer data)
{
gboolean retVal = FALSE;
#ifdef NOTIFICATION_TRAYICON
notification_update_msg_counts(NULL);
retVal = notification_trayicon_main_window_got_iconified(source, data);
#endif
return retVal;
}
static gboolean my_main_window_close_hook(gpointer source, gpointer data)
{
gboolean retVal = FALSE;
#ifdef NOTIFICATION_TRAYICON
notification_update_msg_counts(NULL);
retVal = notification_trayicon_main_window_close(source, data);
#endif
return retVal;
}
static gboolean my_offline_switch_hook(gpointer source, gpointer data)
{
#ifdef NOTIFICATION_TRAYICON
notification_update_msg_counts(NULL);
#endif
return FALSE;
}
static gboolean my_folder_item_update_hook(gpointer source, gpointer data)
{
FolderItemUpdateData *update_data = source;
FolderType ftype;
gchar *uistr;
g_return_val_if_fail(source != NULL, FALSE);
#if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON) || defined(NOTIFICATION_INDICATOR)
notification_update_msg_counts(NULL);
#else
if(notify_config.urgency_hint)
notification_update_msg_counts(NULL);
#endif
/* Check if the folder types is to be notified about */
ftype = update_data->item->folder->klass->type;
uistr = update_data->item->folder->klass->uistr;
if(!notify_include_folder_type(ftype, uistr))
return FALSE;
if(update_data->update_flags & F_ITEM_UPDATE_MSGCNT) {
#ifdef NOTIFICATION_BANNER
notification_update_banner();
#endif
#if defined(NOTIFICATION_POPUP) || defined(NOTIFICATION_COMMAND)
notification_new_unnotified_msgs(update_data);
#endif
}
return FALSE;
}
static gboolean my_folder_update_hook(gpointer source, gpointer data)
{
FolderUpdateData *hookdata;
g_return_val_if_fail(source != NULL, FALSE);
hookdata = source;
#if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON)
if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
notification_update_msg_counts(hookdata->item);
else
notification_update_msg_counts(NULL);
#endif
return FALSE;
}
static gboolean my_msginfo_update_hook(gpointer source, gpointer data)
{
return notification_notified_hash_msginfo_update((MsgInfoUpdate*)source);
}
gint plugin_init(gchar **error)
{
gchar *rcpath;
/* Version check */
/* No be able to test against new-contacts */
if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
VERSION_NUMERIC, _("Notification"), error))
return -1;
/* Check if threading is enabled */
if(!g_thread_supported()) {
*error = g_strdup(_("The Notification plugin needs threading support."));
return -1;
}
hook_f_item = hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
my_folder_item_update_hook, NULL);
if(hook_f_item == (guint) -1) {
*error = g_strdup(_("Failed to register folder item update hook in the "
"Notification plugin"));
return -1;
}
hook_f = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
my_folder_update_hook, NULL);
if(hook_f == (guint) -1) {
*error = g_strdup(_("Failed to register folder update hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
return -1;
}
hook_m_info = hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
my_msginfo_update_hook, NULL);
if(hook_m_info == (guint) -1) {
*error = g_strdup(_("Failed to register msginfo update hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
return -1;
}
hook_offline = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST,
my_offline_switch_hook, NULL);
if(hook_offline == (guint) -1) {
*error = g_strdup(_("Failed to register offline switch hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
return -1;
}
hook_mw_close = hooks_register_hook(MAIN_WINDOW_CLOSE,
my_main_window_close_hook, NULL);
if(hook_mw_close == (guint) -1) {
*error = g_strdup(_("Failed to register main window close hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
return -1;
}
hook_got_iconified = hooks_register_hook(MAIN_WINDOW_GOT_ICONIFIED,
my_main_window_got_iconified_hook,
NULL);
if(hook_got_iconified == (guint) -1) {
*error = g_strdup(_("Failed to register got iconified hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
return -1;
}
hook_account = hooks_register_hook(ACCOUNT_LIST_CHANGED_HOOKLIST,
my_account_list_changed_hook, NULL);
if (hook_account == (guint) -1) {
*error = g_strdup(_("Failed to register account list changed hook in the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
return -1;
}
hook_theme_changed = hooks_register_hook(THEME_CHANGED_HOOKLIST, my_update_theme_hook, NULL);
if(hook_theme_changed == (guint)-1) {
*error = g_strdup(_("Failed to register theme change hook int the "
"Notification plugin"));
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
return -1;
}
/* Configuration */
prefs_set_default(notify_param);
rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
prefs_read_config(notify_param, "NotificationPlugin", rcpath, NULL);
g_free(rcpath);
/* Folder specific stuff */
notification_foldercheck_read_array();
notification_notified_hash_startup_init();
notify_gtk_init();
#ifdef NOTIFICATION_BANNER
notification_update_banner();
#endif
#ifdef NOTIFICATION_LCDPROC
notification_lcdproc_connect();
#endif
#ifdef NOTIFICATION_TRAYICON
if(notify_config.trayicon_enabled &&
notify_config.trayicon_hide_at_startup && claws_is_starting()) {
MainWindow *mainwin = mainwindow_get_mainwindow();
g_idle_add(trayicon_startup_idle,NULL);
if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
main_window_hide(mainwin);
main_set_show_at_startup(FALSE);
}
#endif
my_account_list_changed_hook(NULL,NULL);
if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread)
notification_update_msg_counts(NULL);
#ifdef NOTIFICATION_HOTKEYS
notification_hotkeys_update_bindings();
#endif
debug_print("Notification plugin loaded\n");
return 0;
}
gboolean plugin_done(void)
{
hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
hooks_unregister_hook(THEME_CHANGED_HOOKLIST, hook_theme_changed);
notify_save_config();
notify_gtk_done();
/* foldercheck cleanup */
notification_foldercheck_write_array();
notification_free_folder_specific_array();
#ifdef NOTIFICATION_BANNER
notification_collected_msgs_free(banner_collected_msgs);
banner_collected_msgs = NULL;
notification_banner_destroy();
#endif
#ifdef NOTIFICATION_LCDPROC
notification_lcdproc_disconnect();
#endif
#ifdef NOTIFICATION_TRAYICON
notification_trayicon_destroy();
#endif
#ifdef NOTIFICATION_INDICATOR
notification_indicator_destroy();
#endif
notification_core_free();
#ifdef HAVE_LIBNOTIFY
if(notify_is_initted())
notify_uninit();
#endif
#ifdef NOTIFICATION_HOTKEYS
notification_hotkeys_unbind_all();
#endif
notification_pixbuf_free_all();
debug_print("Notification plugin unloaded\n");
/* Returning FALSE here means that g_module_close() will not be called on the plugin.
* This is necessary, as needed libraries are not designed to be unloaded. */
return FALSE;
}
const gchar *plugin_name(void)
{
return _("Notification");
}
const gchar *plugin_desc(void)
{
return _("This plugin provides various ways "
"to notify the user of new and unread email.\n"
"The plugin is extensively configurable in the "
"plugins section of the preferences dialog.\n\n"
"Feedback to <berndth at gmx.de> is welcome.");
}
const gchar *plugin_type(void)
{
return "GTK2";
}
const gchar *plugin_licence(void)
{
return "GPL3+";
}
const gchar *plugin_version(void)
{
return VERSION;
}
struct PluginFeature *plugin_provides(void)
{
static struct PluginFeature features[] =
{ {PLUGIN_NOTIFIER, N_("Various tools")},
{PLUGIN_NOTHING, NULL}};
return features;
}
#ifdef NOTIFICATION_BANNER
void notification_update_banner(void)
{
notification_collected_msgs_free(banner_collected_msgs);
banner_collected_msgs = NULL;
if(notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) {
guint id;
GSList *folder_list = NULL;
if(notify_config.banner_folder_specific) {
id = notification_register_folder_specific_list
(BANNER_SPECIFIC_FOLDER_ID_STR);
folder_list = notification_foldercheck_get_list(id);
}
if(!(notify_config.banner_folder_specific && (folder_list == NULL)))
banner_collected_msgs =
notification_collect_msgs(notify_config.banner_include_unread,
notify_config.banner_folder_specific ?
folder_list : NULL, notify_config.banner_max_msgs);
}
notification_banner_show(banner_collected_msgs);
}
#endif
#ifdef NOTIFICATION_TRAYICON
static gboolean trayicon_startup_idle(gpointer data)
{
/* if the trayicon is not available,
simulate click on it to show mainwindow */
if(!notification_trayicon_is_available())
notification_trayicon_on_activate(NULL,data);
return FALSE;
}
#endif
--- NEW FILE: notification_indicator.c ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_INDICATOR
#include "notification_indicator.h"
#include "notification_prefs.h"
#include "notification_core.h"
#include "folder.h"
#include "common/utils.h"
#include <libindicate/server.h>
#include <libindicate/indicator.h>
#include <libindicate/indicator-messages.h>
static IndicateServer *server = NULL;
static GHashTable *indicators = NULL;
static gulong mainwin_state_changed_signal_id = 0;
void notification_indicator_destroy(void)
{
if(indicators) {
g_hash_table_destroy(indicators);
indicators = NULL;
}
if(server) {
indicate_server_hide(server);
g_object_unref(server);
server = NULL;
}
if(mainwin_state_changed_signal_id != 0) {
MainWindow *mainwin;
if((mainwin = mainwindow_get_mainwindow()) != NULL)
g_signal_handler_disconnect(mainwin->window, mainwin_state_changed_signal_id);
mainwin_state_changed_signal_id = 0;
}
}
static void show_claws_mail(IndicateIndicator *indicator, guint dummy, gpointer data)
{
MainWindow *mainwin;
if((mainwin = mainwindow_get_mainwindow()) == NULL)
return;
notification_show_mainwindow(mainwin);
if(data) {
Folder *folder = data;
FolderItem *item = folder->inbox;
gchar *path = folder_item_get_identifier(item);
mainwindow_jump_to(path, FALSE);
g_free(path);
}
}
static void set_indicator_unread_count(IndicateIndicator *indicator, gint new, gint unread)
{
gchar *count_str;
count_str = g_strdup_printf("%d / %d", new, unread);
indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_COUNT, count_str);
g_free(count_str);
}
static void create_indicators(void)
{
IndicateIndicator *indicator;
GList *cur_mb;
indicators = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
for(cur_mb = folder_get_list(); cur_mb; cur_mb = cur_mb->next) {
gchar *name;
Folder *folder = cur_mb->data;
if(!folder->name) {
debug_print("Notification plugin: Warning: Ignoring unnamed mailbox in indicator applet\n");
continue;
}
name = g_strdup(folder->name);
indicator = indicate_indicator_new();
indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_NAME, name);
set_indicator_unread_count(indicator, 0, 0);
g_object_set_data(G_OBJECT(indicator), "new_msgs", GINT_TO_POINTER(0));
g_object_set_data(G_OBJECT(indicator), "unread_msgs", GINT_TO_POINTER(0));
g_signal_connect(indicator, "user-display", G_CALLBACK (show_claws_mail), folder);
indicate_indicator_show(indicator);
g_hash_table_insert(indicators, name, indicator);
}
}
static gboolean mainwin_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
{
if(notify_config.indicator_hide_minimized) {
MainWindow *mainwin;
if((mainwin = mainwindow_get_mainwindow()) == NULL)
return FALSE;
if((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
}
else if((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
}
}
return FALSE;
}
void notification_update_indicator(void)
{
GHashTableIter iter;
gpointer key, value;
if(!mainwin_state_changed_signal_id) {
MainWindow *mainwin;
if((mainwin = mainwindow_get_mainwindow()) != NULL)
mainwin_state_changed_signal_id = g_signal_connect(G_OBJECT(mainwin->window), "window-state-event", G_CALLBACK(mainwin_state_event), NULL);
}
if(!notify_config.indicator_enabled)
return;
if(!server) {
server = indicate_server_ref_default();
indicate_server_set_type (server, "message.mail");
indicate_server_set_desktop_file(server, get_desktop_file());
g_signal_connect(server, "server-display", G_CALLBACK(show_claws_mail), NULL);
indicate_server_show(server);
}
if(!indicators)
create_indicators();
/* check accounts for new/unread counts */
g_hash_table_iter_init(&iter, indicators);
while(g_hash_table_iter_next(&iter, &key, &value)) {
NotificationMsgCount count;
gchar *foldername = key;
IndicateIndicator *indicator = value;
notification_core_get_msg_count_of_foldername(foldername, &count);
set_indicator_unread_count(indicator, count.new_msgs, count.unread_msgs);
indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION,
(count.new_msgs > 0) ? "true" : "false");
g_object_set_data(G_OBJECT(indicator), "new_msgs", GINT_TO_POINTER(count.new_msgs));
g_object_set_data(G_OBJECT(indicator), "unread_msgs", GINT_TO_POINTER(count.unread_msgs));
}
}
#endif /* NOTIFICATION_INDICATOR */
--- NEW FILE: plugin.def ---
EXPORTS
plugin_desc
plugin_done
plugin_init
plugin_licence
plugin_name
plugin_type
plugin_provides
plugin_version
--- NEW FILE: notification_plugin.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_PLUGIN_H
#define NOTIFICATION_PLUGIN_H NOTIFICATION_PLUGIN_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_BANNER
void notification_update_banner(void);
#endif
void notification_update_urgency_hint(void);
#endif /* NOTIFICATION_PLUGIN_H */
--- NEW FILE: notification_indicator.h ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_INDICATOR_H
#define NOTIFICATION_INDICATOR_H NOTIFICATION_INDICATOR_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_INDICATOR
#include <glib.h>
void notification_update_indicator(void);
void notification_indicator_destroy(void);
#endif /* NOTIFICATION_INDICATOR */
#endif /* NOTIFICATION_INDICATOR_H */
--- NEW FILE: notification_trayicon.c ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2007 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
/* This module is of course inspired by the trayicon plugin which is
* shipped with Claws-Mail, copyrighted by the Claws-Mail Team. */
#include "pluginconfig.h"
#ifdef NOTIFICATION_TRAYICON
#include "notification_trayicon.h"
#include "notification_prefs.h"
#include "notification_core.h"
#include "notification_hotkeys.h"
#include "notification_pixbuf.h"
#include "notification_foldercheck.h"
#include "main.h"
#include "account.h"
#include "mainwindow.h"
#include "prefs_common.h"
#include "alertpanel.h"
#include "gtk/menu.h"
#ifndef USE_NEW_ADDRBOOK
#include "addressbook.h"
#include "addrindex.h"
#else
#include "addressbook-dbus.h"
#endif
#include "gtk/manage_window.h"
#include "common/utils.h"
#include "gtk/gtkutils.h"
static GdkPixbuf* notification_trayicon_create(void);
static void notification_trayicon_on_popup_menu(GtkStatusIcon*,guint,
guint,gpointer);
static gboolean notification_trayicon_on_size_changed(GtkStatusIcon*,
gint, gpointer);
static void trayicon_get_all_cb(GtkAction*, gpointer);
static void trayicon_compose_cb(GtkAction*, gpointer);
static void trayicon_compose_acc_cb(GtkMenuItem*, gpointer);
static void trayicon_addressbook_cb(GtkAction*, gpointer);
static void trayicon_exit_cb(GtkAction*,gpointer);
static void trayicon_toggle_offline_cb(GtkAction*,gpointer);
#ifdef HAVE_LIBNOTIFY
static void trayicon_toggle_notify_cb(GtkAction*,gpointer);
#endif
#ifdef HAVE_LIBNOTIFY
#include <libnotify/notify.h>
#ifndef NOTIFY_CHECK_VERSION
# define NOTIFY_CHECK_VERSION(a,b,c) 0
#endif
typedef struct {
gint count;
gint num_mail;
gint num_news;
gint num_calendar;
gint num_rss;
gchar *msg_path;
NotifyNotification *notification;
GError *error;
} NotificationTrayiconPopup;
static NotificationTrayiconPopup popup;
static gboolean notification_trayicon_popup_add_msg(MsgInfo*,
NotificationFolderType);
static gboolean notification_trayicon_popup_create(MsgInfo*,
NotificationFolderType);
static void popup_timeout_fun(NotifyNotification*, gpointer);
static void notification_trayicon_popup_free_func(gpointer);
static void notification_trayicon_popup_default_action_cb(NotifyNotification*,
const char*,void*);
static gchar* notification_trayicon_popup_assemble_summary(void);
static gchar* notification_trayicon_popup_assemble_body(MsgInfo*);
static void notification_trayicon_popup_count_msgs(NotificationFolderType);
G_LOCK_DEFINE_STATIC(trayicon_popup);
#endif
static GtkStatusIcon *trayicon;
static gboolean updating_menu = FALSE;
static GtkWidget *traymenu_popup;
static GtkActionEntry trayicon_popup_menu_entries[] = {
{"SysTrayiconPopup", NULL, "SysTrayiconPopup" },
{"SysTrayiconPopup/GetMail", NULL, N_("_Get Mail"), NULL, NULL, G_CALLBACK(trayicon_get_all_cb) },
{"SysTrayiconPopup/---", NULL, "---" },
{"SysTrayiconPopup/Email", NULL, N_("_Email"), NULL, NULL, G_CALLBACK(trayicon_compose_cb) },
{"SysTrayiconPopup/EmailAcc", NULL, N_("E_mail from account"), NULL, NULL, NULL },
{"SysTrayiconPopup/OpenAB", NULL, N_("Open A_ddressbook"), NULL, NULL, G_CALLBACK(trayicon_addressbook_cb) },
{"SysTrayiconPopup/Exit", NULL, N_("E_xit Claws Mail"), NULL, NULL, G_CALLBACK(trayicon_exit_cb) },
};
static GtkToggleActionEntry trayicon_popup_toggle_menu_entries[] =
{
{"SysTrayiconPopup/ToggleOffline", NULL, N_("_Work Offline"), NULL, NULL, G_CALLBACK(trayicon_toggle_offline_cb) },
#ifdef HAVE_LIBNOTIFY
{"SysTrayiconPopup/ShowBubbles", NULL, N_("Show Trayicon Notifications"), NULL, NULL, G_CALLBACK(trayicon_toggle_notify_cb) },
#endif
};
void notification_trayicon_msg(MsgInfo *msginfo)
{
#ifndef HAVE_LIBNOTIFY
return;
#else
FolderType ftype;
NotificationFolderType nftype;
gchar *uistr;
nftype = F_TYPE_MAIL;
if(!msginfo || !notify_config.trayicon_enabled ||
!notify_config.trayicon_popup_enabled ||
!MSG_IS_NEW(msginfo->flags))
return;
if(notify_config.trayicon_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
(TRAYICON_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;
} /* folder specific */
ftype = msginfo->folder->folder->klass->type;
G_LOCK(trayicon_popup);
/* 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(trayicon_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(trayicon_popup);
return;
}
break;
default:
debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
G_UNLOCK(trayicon_popup);
return;
}
notification_trayicon_popup_add_msg(msginfo, nftype);
G_UNLOCK(trayicon_popup);
#endif /* HAVE_LIBNOTIFY */
}
void notification_trayicon_destroy(void)
{
if(trayicon) {
gtk_status_icon_set_visible(trayicon, FALSE);
g_object_unref(trayicon);
trayicon = NULL;
}
}
void notification_update_trayicon()
{
gchar *buf;
static GdkPixbuf *old_icon = NULL;
GdkPixbuf *new_icon;
gint offset;
NotificationMsgCount count;
GSList *list;
if(!notify_config.trayicon_enabled)
return;
if(notify_config.trayicon_folder_specific) {
guint id;
id =
notification_register_folder_specific_list
(TRAYICON_SPECIFIC_FOLDER_ID_STR);
list = notification_foldercheck_get_list(id);
}
else
list = NULL;
notification_core_get_msg_count(list, &count);
if(!trayicon) {
#ifdef NOTIFICATION_HOTKEYS
notification_hotkeys_update_bindings();
#endif
old_icon = notification_trayicon_create();
if(!trayicon) {
debug_print("Notification plugin: Could not create trayicon\n");
return;
}
}
/* Tooltip */
buf = g_strdup_printf(_("New %d, Unread: %d, Total: %d"),
count.new_msgs, count.unread_msgs,
count.total_msgs);
#if GTK_CHECK_VERSION(2,16,0)
gtk_status_icon_set_tooltip_text(trayicon, buf);
#else
gtk_status_icon_set_tooltip(trayicon, buf);
#endif
g_free(buf);
/* Pixmap */
(prefs_common_get_prefs()->work_offline) ? (offset = 1) : (offset = 0);
if((count.new_msgs > 0) && (count.unreadmarked_msgs > 0))
new_icon =
notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMARKEDMAIL+offset);
else if(count.new_msgs > 0)
new_icon =
notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMAIL+offset);
else if(count.unreadmarked_msgs > 0)
new_icon =
notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMARKEDMAIL+offset);
else if(count.unread_msgs > 0)
new_icon =
notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMAIL+offset);
else
new_icon =
notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL+offset);
if(new_icon != old_icon) {
gtk_status_icon_set_from_pixbuf(trayicon, new_icon);
old_icon = new_icon;
}
}
gboolean notification_trayicon_main_window_close(gpointer source, gpointer data)
{
if(!notify_config.trayicon_enabled)
return FALSE;
if(source) {
gboolean *close_allowed = (gboolean*)source;
if(notify_config.trayicon_close_to_tray) {
MainWindow *mainwin = mainwindow_get_mainwindow();
*close_allowed = FALSE;
if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
main_window_hide(mainwin);
}
}
return FALSE;
}
gboolean notification_trayicon_main_window_got_iconified(gpointer source,
gpointer data)
{
MainWindow *mainwin = mainwindow_get_mainwindow();
if(!notify_config.trayicon_enabled)
return FALSE;
if(notify_config.trayicon_hide_when_iconified &&
mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))
&& !gtk_window_get_skip_taskbar_hint(GTK_WINDOW(mainwin->window))) {
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
}
return FALSE;
}
gboolean notification_trayicon_account_list_changed(gpointer source,
gpointer data)
{
GList *cur_ac;
GtkWidget *menu, *submenu;
GtkWidget *menuitem;
PrefsAccount *ac_prefs;
GList *account_list = account_get_list();
if(!notify_config.trayicon_enabled)
return FALSE;
menu = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup/EmailAcc");
gtk_widget_show(menu);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), NULL);
submenu = gtk_menu_new();
for(cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) {
ac_prefs = (PrefsAccount *)cur_ac->data;
menuitem = gtk_menu_item_new_with_label
(ac_prefs->account_name ? ac_prefs->account_name
: _("Untitled"));
gtk_widget_show(menuitem);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(trayicon_compose_acc_cb),
ac_prefs);
}
gtk_widget_show(submenu);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu);
return FALSE;
}
static GdkPixbuf* notification_trayicon_create(void)
{
GdkPixbuf *trayicon_nomail;
GtkActionGroup *action_group;
trayicon_nomail = notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL);
notification_trayicon_destroy();
trayicon = gtk_status_icon_new_from_pixbuf(trayicon_nomail);
g_signal_connect(G_OBJECT(trayicon), "activate",
G_CALLBACK(notification_trayicon_on_activate), NULL);
g_signal_connect(G_OBJECT(trayicon), "popup-menu",
G_CALLBACK(notification_trayicon_on_popup_menu), NULL);
g_signal_connect(G_OBJECT(trayicon), "size-changed",
G_CALLBACK(notification_trayicon_on_size_changed), NULL);
/* Popup-Menu */
action_group = cm_menu_create_action_group("SysTrayiconPopup", trayicon_popup_menu_entries,
G_N_ELEMENTS(trayicon_popup_menu_entries), NULL);
gtk_action_group_add_toggle_actions(action_group, trayicon_popup_toggle_menu_entries,
G_N_ELEMENTS(trayicon_popup_toggle_menu_entries), NULL);
MENUITEM_ADDUI("/Menus", "SysTrayiconPopup", "SysTrayiconPopup", GTK_UI_MANAGER_MENU)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "GetMail", "SysTrayiconPopup/GetMail", GTK_UI_MANAGER_MENUITEM)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator1", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Email", "SysTrayiconPopup/Email", GTK_UI_MANAGER_MENUITEM)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "EmailAcc", "SysTrayiconPopup/EmailAcc", GTK_UI_MANAGER_MENU)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator2", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "OpenAB", "SysTrayiconPopup/OpenAB", GTK_UI_MANAGER_MENUITEM)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator3", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ToggleOffline", "SysTrayiconPopup/ToggleOffline", GTK_UI_MANAGER_MENUITEM)
#ifdef HAVE_LIBNOTIFY
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ShowBubbles", "SysTrayiconPopup/ShowBubbles", GTK_UI_MANAGER_MENUITEM)
#endif
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator4", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Exit", "SysTrayiconPopup/Exit", GTK_UI_MANAGER_MENUITEM)
traymenu_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup")));
return trayicon_nomail;
}
void notification_trayicon_on_activate(GtkStatusIcon *status_icon, gpointer user_data)
{
notification_toggle_hide_show_window();
}
static void notification_trayicon_on_popup_menu(GtkStatusIcon *status_icon,
guint button, guint activate_time,
gpointer user_data)
{
MainWindow *mainwin = mainwindow_get_mainwindow();
if(!mainwin)
return;
/* tell callbacks to skip any event */
updating_menu = TRUE;
/* initialize checkitems according to current states */
cm_toggle_menu_set_active("SysTrayiconPopup/ToggleOffline", prefs_common_get_prefs()->work_offline);
#ifdef HAVE_LIBNOTIFY
cm_toggle_menu_set_active("SysTrayiconPopup/ShowBubbles", notify_config.trayicon_popup_enabled);
#endif
cm_menu_set_sensitive("SysTrayiconPopup/GetMail", mainwin->lock_count == 0);
updating_menu = FALSE;
#ifndef G_OS_WIN32
gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
button, activate_time);
#else
/* http://bugzilla.gnome.org/show_bug.cgi?id=552642 */
gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
0, activate_time);
#endif
}
static gboolean notification_trayicon_on_size_changed(GtkStatusIcon *icon,
gint size,
gpointer user_data)
{
notification_update_msg_counts(NULL);
return FALSE;
}
/* popup menu callbacks */
static void trayicon_get_all_cb(GtkAction *action, gpointer data)
{
MainWindow *mainwin = mainwindow_get_mainwindow();
inc_all_account_mail_cb(mainwin, 0, NULL);
}
static void trayicon_compose_cb(GtkAction *action, gpointer data)
{
MainWindow *mainwin = mainwindow_get_mainwindow();
compose_mail_cb(mainwin, 0, NULL);
}
static void trayicon_compose_acc_cb(GtkMenuItem *menuitem, gpointer data)
{
compose_new((PrefsAccount *)data, NULL, NULL);
}
static void trayicon_addressbook_cb(GtkAction *action, gpointer data)
{
#ifndef USE_NEW_ADDRBOOK
addressbook_open(NULL);
#else
GError* error = NULL;
addressbook_dbus_open(FALSE, &error);
if (error) {
g_warning("%s", error->message);
g_error_free(error);
}
#endif
}
static void trayicon_toggle_offline_cb(GtkAction *action, gpointer data)
{
/* toggle offline mode if menu checkitem has been clicked */
if(!updating_menu) {
MainWindow *mainwin = mainwindow_get_mainwindow();
main_window_toggle_work_offline(mainwin, !prefs_common_get_prefs()->work_offline, TRUE);
}
}
#ifdef HAVE_LIBNOTIFY
static void trayicon_toggle_notify_cb(GtkAction *action, gpointer data)
{
if(!updating_menu) {
notify_config.trayicon_popup_enabled = !notify_config.trayicon_popup_enabled;
}
}
#endif
static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
{
if(prefs_common_get_prefs()->confirm_on_exit) {
if(alertpanel(_("Exit"), _("Exit Claws Mail?"),
GTK_STOCK_CANCEL, GTK_STOCK_OK,
NULL) != G_ALERTALTERNATE) {
return;
}
manage_window_focus_in(mainwin->window, NULL, NULL);
}
if (prefs_common_get_prefs()->clean_on_exit) {
if (!main_window_empty_trash(mainwin, prefs_common_get_prefs()->ask_on_clean, TRUE))
return;
}
app_will_exit(NULL, mainwin);
}
static void trayicon_exit_cb(GtkAction *action, gpointer data)
{
MainWindow *mainwin = mainwindow_get_mainwindow();
if(mainwin->lock_count == 0) {
app_exit_cb(mainwin, 0, NULL);
}
}
#ifdef HAVE_LIBNOTIFY
static gboolean notification_trayicon_popup_add_msg(MsgInfo *msginfo,
NotificationFolderType nftype)
{
gchar *summary;
gchar *utf8_str;
gboolean retval;
GdkPixbuf *pixbuf;
g_return_val_if_fail(msginfo, FALSE);
if(!popup.notification)
return notification_trayicon_popup_create(msginfo,nftype);
/* Count messages */
notification_trayicon_popup_count_msgs(nftype);
if(popup.msg_path) {
g_free(popup.msg_path);
popup.msg_path = NULL;
}
summary = notification_trayicon_popup_assemble_summary();
utf8_str = notification_trayicon_popup_assemble_body(msginfo);
/* make sure we show a logo on many msg arrival */
pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
if(pixbuf)
notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
retval = notify_notification_update(popup.notification, summary,
utf8_str, NULL);
g_free(summary);
g_free(utf8_str);
if(!retval) {
debug_print("Notification Plugin: Failed to update notification.\n");
return FALSE;
}
/* Show the popup */
notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
if(!notify_notification_show(popup.notification, &(popup.error))) {
debug_print("Notification Plugin: Failed to send updated notification: "
"%s\n", popup.error->message);
g_clear_error(&(popup.error));
return FALSE;
}
debug_print("Notification Plugin: Popup successfully modified "
"with libnotify.\n");
return TRUE;
}
static gboolean notification_trayicon_popup_create(MsgInfo *msginfo,
NotificationFolderType nftype)
{
gchar *summary = NULL;
gchar *utf8_str = NULL;
GdkPixbuf *pixbuf;
GList *caps = NULL;
gboolean support_actions = FALSE;
/* init libnotify if necessary */
if(!notify_is_initted()) {
if(!notify_init("claws-mail")) {
debug_print("Notification Plugin: Failed to initialize libnotify. "
"No popups will be shown.\n");
return FALSE;
}
}
/* Count messages */
notification_trayicon_popup_count_msgs(nftype);
summary = notification_trayicon_popup_assemble_summary();
utf8_str = notification_trayicon_popup_assemble_body(msginfo);
#if NOTIFY_CHECK_VERSION(0, 7, 0)
popup.notification = notify_notification_new(summary, utf8_str, NULL);
#else
popup.notification = notify_notification_new(summary, utf8_str, NULL, NULL);
notify_notification_attach_to_status_icon(popup.notification, trayicon);
#endif
g_free(summary);
g_free(utf8_str);
caps = notify_get_server_caps();
if(caps != NULL) {
GList *c;
for(c = caps; c != NULL; c = c->next) {
if(strcmp((char*)c->data, "actions") == 0 ) {
support_actions = TRUE;
break;
}
}
g_list_foreach(caps, (GFunc)g_free, NULL);
g_list_free(caps);
}
/* Default action */
if (support_actions)
notify_notification_add_action(popup.notification,
"default", "Present main window",
(NotifyActionCallback)
notification_trayicon_popup_default_action_cb,
GINT_TO_POINTER(nftype),
notification_trayicon_popup_free_func);
if(popup.notification == NULL) {
debug_print("Notification Plugin: Failed to create a new notification.\n");
return FALSE;
}
/* Icon */
pixbuf = NULL;
#ifndef USE_NEW_ADDRBOOK
if(msginfo && msginfo->from) {
gchar *icon_path;
icon_path = addrindex_get_picture_file(msginfo->from);
if(is_file_exist(icon_path)) {
GError *error = NULL;
gint w, h;
gdk_pixbuf_get_file_info(icon_path, &w, &h);
if((w > 64) || (h > 64))
pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
64, 64, TRUE, &error);
else
pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
if(!pixbuf) {
debug_print("Could not load picture file: %s\n",
error ? error->message : "no details");
g_error_free(error);
}
}
else
debug_print("Picture path does not exist: %s\n",icon_path);
g_free(icon_path);
}
#endif
if(!pixbuf)
pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
if(pixbuf) {
notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
g_object_unref(pixbuf);
}
else /* This is not fatal */
debug_print("Notification plugin: Icon could not be loaded.\n");
/* timeout */
notify_notification_set_timeout(popup.notification, notify_config.trayicon_popup_timeout);
/* Category */
notify_notification_set_category(popup.notification, "email.arrived");
/* get notified on bubble close */
g_signal_connect(G_OBJECT(popup.notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
/* Show the popup */
notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
if(!notify_notification_show(popup.notification, &(popup.error))) {
debug_print("Notification Plugin: Failed to send notification: %s\n",
popup.error->message);
g_clear_error(&(popup.error));
g_object_unref(G_OBJECT(popup.notification));
popup.notification = NULL;
return FALSE;
}
/* Store path to message */
if(nftype == F_TYPE_MAIL) {
if(msginfo->folder && msginfo->folder) {
gchar *ident;
ident = folder_item_get_identifier(msginfo->folder);
popup.msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
msginfo->msgnum);
g_free(ident);
}
else
popup.msg_path = NULL;
}
debug_print("Notification Plugin: Popup created with libnotify.\n");
return TRUE;
}
static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
{
G_LOCK(trayicon_popup);
g_object_unref(G_OBJECT(popup.notification));
popup.notification = NULL;
g_clear_error(&(popup.error));
popup.count = 0;
popup.num_mail = 0;
popup.num_news = 0;
popup.num_calendar = 0;
popup.num_rss = 0;
if(popup.msg_path) {
g_free(popup.msg_path);
popup.msg_path = NULL;
}
G_UNLOCK(trayicon_popup);
}
static void notification_trayicon_popup_free_func(gpointer data)
{
if(popup.msg_path) {
g_free(popup.msg_path);
popup.msg_path = NULL;
}
debug_print("Freed notification data\n");
}
static void notification_trayicon_popup_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 */
notification_show_mainwindow(mainwin);
/* If there is only one new mail message, jump to this message */
nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
if((popup.count == 1) && (nftype == F_TYPE_MAIL)) {
gchar *select_str;
G_LOCK(trayicon_popup);
select_str = g_strdup(popup.msg_path);
G_UNLOCK(trayicon_popup);
debug_print("Notification plugin: Select message %s\n", select_str);
mainwindow_jump_to(select_str, FALSE);
g_free(select_str);
}
}
}
static void notification_trayicon_popup_count_msgs(NotificationFolderType nftype)
{
switch(nftype) {
case F_TYPE_MAIL:
popup.num_mail++;
break;
case F_TYPE_NEWS:
popup.num_news++;
break;
case F_TYPE_CALENDAR:
popup.num_calendar++;
break;
case F_TYPE_RSS:
popup.num_rss++;
break;
default:
debug_print("Notification plugin: Unknown folder type\n");
return;
}
popup.count++;
}
/* The returned value has to be freed by the caller */
static gchar* notification_trayicon_popup_assemble_summary(void)
{
gchar *summary = NULL;
if(popup.count == 1) {
if(popup.num_mail)
summary = g_strdup(_("New mail message"));
else if(popup.num_news)
summary = g_strdup(_("New news post"));
else if(popup.num_calendar)
summary = g_strdup(_("New calendar message"));
else
summary = g_strdup(_("New article in RSS feed"));
} /* One new message */
else {
summary = g_strdup(_("New messages arrived"));
} /* Many new messages */
return summary;
}
/* The returned value has to be freed by the caller */
static gchar* notification_trayicon_popup_assemble_body(MsgInfo *msginfo)
{
gchar *utf8_str;
if(popup.count == 1) {
if(popup.num_mail || popup.num_news) {
gchar *from;
gchar *subj;
gchar *text;
gchar *foldname = NULL;
from = notification_libnotify_sanitize_str(msginfo->from ?
msginfo->from :
_("(No From)"));
subj = notification_libnotify_sanitize_str(msginfo->subject ?
msginfo->subject :
_("(No Subject)"));
if (notify_config.trayicon_display_folder_name) {
foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
}
else
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);
if(foldname) g_free(foldname);
}
else if(popup.num_calendar) {
utf8_str = g_strdup(_("A new calendar message arrived"));
}
else {
utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
}
} /* One message */
else {
gchar *msg;
gchar *tmp;
gboolean str_empty = TRUE;
utf8_str = g_strdup("");
if(popup.num_mail) {
msg = g_strdup_printf(ngettext("%d new mail message arrived",
"%d new mail messages arrived",
popup.num_mail),
popup.num_mail);
tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
g_free(msg);
g_free(utf8_str);
utf8_str = tmp;
str_empty = FALSE;
}
if(popup.num_news) {
msg = g_strdup_printf(ngettext("%d new news post arrived",
"%d new news posts arrived",
popup.num_news),
popup.num_news);
tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
g_free(msg);
g_free(utf8_str);
utf8_str = tmp;
str_empty = FALSE;
}
if(popup.num_calendar) {
msg = g_strdup_printf(ngettext("%d new calendar message arrived",
"%d new calendar messages arrived",
popup.num_calendar),
popup.num_calendar);
tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
g_free(msg);
g_free(utf8_str);
utf8_str = tmp;
str_empty = FALSE;
}
if(popup.num_rss) {
msg = g_strdup_printf(ngettext("%d new article in RSS feeds arrived",
"%d new articles in RSS feeds arrived",
popup.num_rss),
popup.num_rss);
tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
g_free(msg);
g_free(utf8_str);
utf8_str = tmp;
str_empty = FALSE;
}
} /* Many messages */
return utf8_str;
}
#endif /* HAVE_LIBNOTIFY */
gboolean notification_trayicon_is_available(void)
{
gboolean is_available;
is_available = FALSE;
if(trayicon) {
if(gtk_status_icon_is_embedded(trayicon) &&
gtk_status_icon_get_visible(trayicon))
is_available = TRUE;
}
return is_available;
}
#endif /* NOTIFICATION_TRAYICON */
--- NEW FILE: raw_claws_mail_logo_64x64.h ---
/* GdkPixbuf RGBA C-Source image dump */
#ifdef __SUNPRO_C
#pragma align 4 (raw_claws_mail_logo_64x64)
#endif
#ifdef __GNUC__
static const guint8 raw_claws_mail_logo_64x64[] __attribute__ ((__aligned__ (4))) =
#else
static const guint8 raw_claws_mail_logo_64x64[] =
#endif
{ ""
/* Pixbuf magic (0x47646b50) */
"GdkP"
/* length: header (24) + pixel_data (16384) */
"\0\0@\30"
/* pixdata_type (0x1010002) */
"\1\1\0\2"
/* rowstride (256) */
"\0\0\1\0"
/* width (64) */
"\0\0\0@"
/* height (64) */
"\0\0\0@"
/* pixel_data: */
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\10\0\0\0\10\0\0\0\7\0"
"\0\0\6\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0"
"\0\0\1\0\0\0\3\0\0\0\6\0\0\0\15\0\0\0\27\0\0\0(\0\0\0=\15\30\35W\"@M"
"w-Sc\2073]n\2226`q\2250Ve\214)IW}\26&-_\4\6\7B\0\0\0+\0\0\0\31\0\0\0"
"\16\0\0\0\6\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\1\0\0\0\2\0\0\0\4\0\0\0\14\0\0\0\31\0\0\0""0\24&.c8g{\244T\215\245"
"\321s\257\310\362\215\304\333\375\243\321\344\377\261\327\350\377\271"
"\333\352\377\273\335\352\377\265\331\350\377\252\323\346\377\227\311"
"\337\376\200\267\316\366b\230\260\335Gw\213\263&@Kv\4\4\4""7\0\0\0\31"
"\0\0\0\12\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\14\0\0"
"\0\40\0\5\13P\25""7P\242Y\225\255\333\211\302\333\375\265\330\350\377"
"\321\343\355\377\343\354\362\377\346\354\361\377\353\362\365\377\360"
"\367\372\377\360\367\372\377\360\367\372\377\360\367\372\377\360\367"
"\372\377\360\366\372\377\353\364\370\377\336\356\364\377\304\340\355"
"\377\235\315\340\376o\246\275\351Fr\204\254\20\30\34O\0\0\0\37\0\0\0"
"\12\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\1\0\0\0\3\0\0\0\10\0\0\0\26\0\2\4C\22""4N\250:g\212\361\236"
"\304\327\377\311\334\346\377\331\341\350\377\336\344\352\377\336\344"
"\352\377\336\344\352\377\337\346\353\377\346\356\363\377\345\354\361"
"\377\344\353\357\377\344\353\357\377\346\357\363\377\352\363\370\377"
"\353\364\370\377\351\363\367\377\346\355\363\377\352\363\366\377\352"
"\364\370\377\334\354\364\377\265\332\350\377~\264\314\365Jv\212\263\14"
"\21\24H\0\0\0\30\0\0\0\7\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\1\0\0\0\4\0\0\0\15\0\0\0-\22)6zCz\227\337\220\271\320\377\316"
"\340\352\377\325\336\346\377\324\334\344\377\324\334\344\377\324\334"
"\344\377\324\334\344\377\325\334\344\377\325\335\344\377\326\335\345"
"\377\326\335\345\377\326\336\345\377\327\336\346\377\327\336\346\377"
"\330\340\346\377\331\342\350\377\330\337\346\377\330\337\346\377\330"
"\340\347\377\335\345\354\377\340\352\360\377\344\357\365\377\334\355"
"\364\377\260\326\346\377r\251\300\3555Tb\214\0\0\0*\0\0\0\13\0\0\0\3"
"\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\5\0\0\0\21\0\0\0""3\40G_\257i\234"
"\272\376\242\270\313\377\306\321\334\377\312\324\336\377\312\324\336"
"\377\312\324\336\377\312\324\336\377\312\324\336\377\313\325\336\377"
"\314\325\337\377\314\326\337\377\315\326\337\377\315\327\340\377\316"
"\327\340\377\316\327\340\377\317\330\341\377\317\330\341\377\317\330"
"\341\377\320\331\341\377\320\331\342\377\320\331\342\377\320\331\342"
"\377\320\331\342\377\320\331\342\377\326\342\351\377\334\352\361\377"
"\313\342\354\377\220\303\330\375Q\200\223\276\6\11\12@\0\0\0\22\0\0\0"
"\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\2\0\0\0\6\0\0\0\27\0\5\12N\13/O\303Sz\234\376\262\301\320"
"\377\277\314\330\377\277\314\330\377\277\314\330\377\277\314\330\377"
"\277\314\330\377\277\314\330\377\300\315\330\377\301\315\331\377\302"
"\316\331\377\303\317\332\377\303\317\332\377\276\312\324\377\266\302"
"\314\377\302\316\327\377\200\207\216\377\277\313\325\377\307\321\334"
"\377\307\321\335\377\307\322\335\377\310\322\335\377\306\320\333\377"
"\310\322\335\377\310\322\335\377\310\322\335\377\310\322\334\377\310"
"\322\334\377\300\315\331\377\227\273\320\377T\204\235\334\25\37$`\0\0"
"\0\36\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\2\0\0\0\6\0\0\0\27\0\7\16W\17""8`\344j\210\244\377\255\275\315"
"\377\265\304\322\377\265\304\322\377\265\304\322\377\265\304\322\377"
"\265\304\322\377\265\304\322\377\266\305\322\377\267\305\323\377\270"
"\306\324\377\271\307\324\377\270\306\323\377\226\240\253\377\201\213"
"\224\377py\200\377\267\304\320\377\40\"#\377\216\225\233\377\304\317"
"\331\377\275\310\325\377\275\310\324\377\237\251\264\377\203\213\224"
"\377\257\272\306\377\300\314\330\377\300\314\327\377\300\314\327\377"
"\300\314\330\377\300\313\327\377\274\311\325\377\207\236\263\377T\200"
"\236\372\32-=\206\0\0\0\33\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\2\0\0\0\6\0\0\0\26\0\3\10O\13""2Z\334n\213\247\377\251\272"
"\313\377\252\274\314\377\252\274\314\377\252\274\314\377\252\274\314"
"\377\252\273\314\377\252\273\314\377\253\275\314\377\254\275\315\377"
"\255\276\316\377\256\277\316\377\260\300\317\377\252\271\307\377^fn\377"
"(,/\377ox\202\377\240\256\272\377!$&\377\26\27\30\377\216\223\230\377"
"\235\247\257\377dkq\377\32\34\36\377r{\203\377lt{\377\237\253\266\377"
"\271\307\324\377\270\305\323\377\272\310\326\377\270\305\322\377\267"
"\305\322\377\267\304\322\377\253\304\325\377[\212\246\362\14\26\36d\0"
"\0\0\26\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\24\0\4\10"
"K\12""1X\333_~\236\377\236\260\304\377\240\263\306\377\240\263\306\377"
"\240\263\306\377\240\263\306\377\240\263\306\377\240\263\306\377\240"
"\263\306\377\242\264\307\377\243\265\310\377\244\266\310\377\243\265"
"\305\377\233\253\273\377\240\260\301\377x\204\220\377\24\26\30\377|\210"
"\225\377\203\215\231\377\34\36\40\377\0\0\0\377\36\37\37\377ORU\377\3"
"\3\3\377\16\17\21\377\26\30\31\377_in\377\265\304\323\377\277\323\337"
"\377\267\310\326\377\304\331\344\377\261\277\316\377\262\301\317\377"
"\300\325\341\377\311\343\355\377\260\325\344\377Q~\232\351\6\11\14J\0"
"\0\0\17\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\15\0\0\0""1\4$D\273Tv\227"
"\377\223\250\276\377\226\253\300\377\226\253\300\377\226\253\300\377"
"\226\253\300\377\226\253\300\377\226\253\300\377\226\253\300\377\227"
"\254\300\377\231\255\301\377\232\256\302\377\234\257\303\377\233\257"
"\302\377\212\232\253\377q\177\214\3777>D\377\37\"%\377`ir\377mu}\377"
"\27\31\33\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\21\23"
"\24\377\207\231\241\377\276\327\342\377\311\344\357\377\311\344\357\377"
"\260\303\321\377\252\272\312\377\260\302\321\377\310\342\355\377\306"
"\341\355\377\305\337\353\377\241\314\340\377>f~\311\5\5\5""3\0\0\0\12"
"\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\36\0\17\37z&Qz\374\204\234\265\377\214"
"\242\271\377\214\242\271\377\214\242\271\377\214\242\271\377\214\242"
"\272\377\214\242\272\377\214\242\272\377\214\242\272\377\216\244\273"
"\377\220\245\274\377\221\246\275\377\223\250\276\377\210\235\260\377"
"9BJ\377\2\2\2\377\0\0\0\377\20\22\24\377\40#&\377#&)\377\6\7\5\377)9"
"\6\377*;\6\377\25\35\3\377\6\11\0\377\0\0\0\377(.1\377s\203\213\377\211"
"\234\245\377\215\241\251\377\304\341\355\377\263\312\331\377\244\265"
"\307\377\246\271\312\377\302\340\354\377\300\337\353\377\300\336\353"
"\377\275\334\352\377\205\267\320\376\35""3D\220\0\0\0\34\0\0\0\5\0\0"
"\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0"
"\0\5\0\0\0\22\0\0\1C\12""2Z\336c\202\241\377\201\232\263\377\202\232"
"\263\377\202\232\263\377\202\232\263\377\202\232\264\377\202\232\264"
"\377\202\232\264\377\202\232\264\377\202\233\265\377\203\234\265\377"
"}\230\261\377o\215\251\377[|\235\377Gj\216\3775Y}\377\34""3J\377\2\4"
"\6\377\0\0\0\377\0\0\0\377\1\2\0\377d\207\21\377\202\262\26\377o\226"
"\23\377`\202\21\377F`\12\377\24\35\2\377\0\0\0\377\12\14\15\377.46\377"
"z\214\224\377\255\311\325\377\267\324\340\377\236\261\303\377\256\307"
"\327\377\276\336\353\377\274\334\351\377\274\334\351\377\273\334\351"
"\377\256\324\344\377V\206\242\353\7\10\11E\0\0\0\16\0\0\0\3\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\12\0\0\0#\0"
"\27-\2224]\205\377t\220\254\377w\222\255\377w\222\255\377w\222\255\377"
"w\222\255\377w\222\255\377w\222\256\377v\222\256\377n\213\250\377Vx\233"
"\3777`\210\377\37Mz\377\25Ft\377\23Dr\377\27Ft\377\32Ep\377!Kw\377\37"
"7O\377\6\12\16\377\0\0\0\377;P\16\377\207\267\34\377q\233\24\377k\222"
"\23\377m\225\24\377k\223\23\377Hc\12\377\23\33\1\377\10\13\6\377h~\207"
"\377\246\310\327\377\243\304\322\377\270\330\345\377\271\331\347\377"
"\273\334\352\377\272\334\351\377\271\333\351\377\270\331\351\377\266"
"\331\347\377\264\327\346\377\221\305\334\377>bp\236\0\0\0\37\0\0\0\6"
"\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\21"
"\0\0\0<\10""0W\333Xz\232\377l\212\246\377l\212\247\377l\212\247\377l"
"\212\247\377l\212\247\377k\211\247\377[}\236\3775]\204\377\22:_\377\30"
";L\377/PA\3776P=\377OhC\377RfA\377o\210C\377f{C\377Xk;\377Uh0\377GQ3"
"\377La\36\377o\2114\377t\2210\377l\215\"\377h\206&\377h\211\34\377c\204"
"\31\377Eb\11\3771F\6\377\27!\2\377\25\32\34\377L]d\377 at S\\\377\222\302"
"\327\377\237\317\343\377\257\326\347\377\265\331\350\377\264\331\350"
"\377\263\327\347\377\261\327\346\377\260\325\345\377\246\320\342\377"
"k\243\271\350\4\5\5B\0\0\0\15\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\2\0\0\0\11\0\0\0\34\0\15\32t\40Lx\376`\200\240\377b\202\241"
"\377b\202\241\377b\202\241\377a\201\240\377Qt\230\377'R{\377\35>F\377"
"6I/\377y\222C\377\200\231G\377\201\234A\377\200\2378\377}\233;\377{\232"
"7\377o\2106\377f}3\377e\201%\377\\t&\377e\200+\377s\224'\377y\2320\377"
"h\210\40\377b\177\33\377c\204\32\377h\210#\377f\210\33\377M_)\377FZ\33"
"\377$/\24\377k\212\230\3775IR\377#29\377\177\264\313\377\207\303\334"
"\377\212\303\333\377\230\312\337\377\251\323\344\377\256\325\346\377"
"\255\324\345\377\254\323\343\377\251\321\343\377\211\301\331\3778Xe\220"
"\0\0\0\32\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\15"
"\0\0\0+\2#C\270:b\210\377Xz\232\377Xz\232\377Wy\232\377Nr\226\377*V\200"
"\377\7""6c\377\20\34(\377q\204G\377\224\271=\377\205\2516\377y\241\""
"\377y\235%\377t\223/\377k\211'\377O_(\377Ma\40\377IY(\377IZ#\377e}-\377"
"f\204\"\377k\216\34\377k\213%\377_~\31\377Vn\36\377b\203\32\377l\222"
"\26\377y\234*\377[o.\377d~*\377c~*\377XmT\377t\244\270\377\211\300\330"
"\377\213\304\335\377\210\303\334\377\206\302\334\377\204\300\331\377"
"\206\301\332\377\225\310\336\377\245\320\342\377\246\321\342\377\245"
"\317\341\377\230\311\335\377[\216\244\324\0\0\0""0\0\0\0\12\0\0\0\2\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\23\0\0\0\77\15""8_\342Dj\216"
"\377Nr\224\377Lp\224\3775^\206\377\16\77p\377\0""1`\377QRJ\377\306\264"
"\223\3771*%\377Sh#\377j\217\27\377e\206\31\377f\210\30\377Pe\40\377F"
"U&\377>L#\377N[/\377g~3\377\214\257<\377f\201'\377i\213\32\377g\203("
"\377a|$\377Ob&\377EX\34\377[w\32\377l\221\26\377\205\2520\377o\205>\377"
"e\200,\377z\236'\377|\2326\377Nb1\377i\222\250\377\215\306\337\377\212"
"\304\335\377\210\303\334\377\204\300\332\377\202\277\331\377\177\276"
"\327\377\205\300\330\377\226\310\335\377\237\314\337\377\234\313\336"
"\377w\263\315\372\22\30\33X\0\0\0\20\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\11\0\0\0\34\1\10\17d\24Dq\374Ag\214\377>d\212\377\36Kx\377"
"\3""6i\377\0""4g\377)9A\377\362\352\326\377\325\275\214\377pbH\377\77"
"H/\377Vi*\377Na#\377CP%\377\40'\21\377!,\40\377\27#\35\377`u1\377\227"
"\265N\377}\230@\377o\2121\377e\201'\377f\205\37\377K^!\377>L\34\377C"
"T\36\377[|\22\377k\213&\377\210\250<\377u\2179\377e\203\"\377e\201&\377"
"n\215,\377v\221:\377\\p>\377w\247\271\377\214\305\335\377\210\303\334"
"\377\206\301\333\377\202\277\331\377\177\276\327\377}\274\326\377|\273"
"\325\377\210\300\330\377\225\310\334\377\203\276\326\3777Vc\221\0\0\0"
"\32\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0%\0\34""6\237\40"
"Ly\377/Y\202\377\16\77o\377\0""4g\377\0""4h\377\0""1a\377\220\205l\377"
"\353\341\310\377\316\265\200\377H at 1\377+3\36\377=K\36\3778D\33\377)4"
"#\377\12(E\377\25*4\377i\200:\377\224\270@\377{\227;\377`s2\377Ym.\377"
"Pd\40\377I[!\377\77G,\377,5\30\377BT\36\377Xr\36\377s\223*\377\216\256"
"D\377at6\377Qd'\377Zv\32\377Ti$\377x\220B\377\242\303S\377cxC\377p\235"
"\261\377\210\303\334\377\206\301\333\377\202\277\331\377\200\276\330"
"\377}\275\326\377z\272\325\377w\270\323\377~\272\324\377\202\276\326"
"\377O\177\222\300\0\0\0&\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20"
"\0\0\0/\1$H\302\30Ft\377\7""9j\377\0""4g\377\0""4g\377\0""5h\377\0/\\"
"\377\277\260\220\377\335\312\243\377d\\G\377\0\31""2\377\26\35\34\377"
"#+\22\377*0\35\377\14!.\377#6/\377\203\242>\377}\230@\377w\2253\377p"
"\220(\377`y#\377Pi\35\377FV#\377*;\32\377\0\1\0\377\32\40\37\377<L\32"
"\377Tr\22\377s\2204\377\200\231E\377Yo'\377%,\32\377IY&\377Un\37\377"
"Zs\"\377\220\263\77\377\216\256D\377YqI\377v\252\300\377\205\301\333"
"\377\202\277\331\377\177\276\330\377}\274\326\377z\272\325\377w\270\323"
"\377t\266\322\377s\266\322\377[\223\253\337\0\0\0""2\0\0\0\13\0\0\0\2"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\23\0\0\0""7\0(P\327\2""5f\377\0""4f\377\0"
"4g\377\0""4g\377\0""5h\377\0""1a\377\220\204d\377\326\300\220\377\22"
"%4\377\0&L\377\0#G\377\7\"=\377\6\33""1\377,;&\377x\222<\377~\2348\377"
"l\212,\377Ys\34\377]q1\3773@\27\3775>\40\377)3\34\377\16\26\34\377\31"
"!%\377F]Y\377J]\37\377Wk*\377|\2351\377\211\245G\377r\2138\377\".3\377"
".67\377:F#\377Nk\15\377`{#\377x\231-\377\241\310J\377Vh;\377m\236\256"
"\377\201\277\331\377\177\276\330\377}\274\326\377z\272\325\377v\270\322"
"\377t\266\322\377p\264\321\377d\242\274\361\3\3\3C\0\0\0\16\0\0\0\3\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0>\0-V\343\0""4f\377\0""4f\377\0""4g"
"\377\0""4h\377\0""5h\377\0""3e\3776==\377\330\276\211\377\37-7\377\0"
"&L\377\0#F\377\25,4\377Xj6\377}\2363\377f\205!\377_|\34\377]x!\377Rf"
"$\37719!\377\26/D\377\31""8U\377\30""1J\377$D`\377U\200\221\377\77S8"
"\377I[!\377Ti'\377f\207\36\377\234\301H\377\232\271M\377Ool\377d\213"
"\235\3773E:\377=H%\377Ql\24\377h\205%\377s\221.\377\206\2515\377Pd5\377"
"q\246\275\377~\275\330\377{\274\326\377z\271\325\377v\270\322\377t\266"
"\322\377p\264\321\377j\255\311\373\11\14\15R\0\0\0\21\0\0\0\4\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\30\0\0\0C\22Go\353\2""7i\377\0""4f\377\0""4g\377\0"
"4h\377\0""5h\377\0""1`\377\0!B\377e^J\377\211z\\\377\3\33.\3776D*\377"
"x\222>\377t\2158\377o\221\40\377g\206$\377a{(\377J_\37\377IT0\377\32"
"-3\377\31:Z\377\33;[\377\36=\\\377&Ge\377Z\214\240\377AU\77\377L`!\377"
"^z\35\377g\210\35\377\214\253C\377{\222C\377Vxy\377e\217\240\377Y\177"
"\216\377&,$\3773B\17\377Wo#\377_x#\377k\206*\377\227\274@\377UiB\377"
"f\230\251\377{\273\325\377x\271\325\377u\267\322\377r\265\321\377p\264"
"\321\377k\257\314\375\25\35\"a\0\0\0\23\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\31\0\0\0D\34S{\356\30S\202\377\0""4f\377\0""4g\377\0""4g\377\0""5"
"h\377\0/]\377\0&K\377\0\"C\377CM,\377gy:\377\214\254A\377y\233-\377c"
"\200\"\377_u-\377Zn*\377K^\36\377'2\35\377\22'/\377\23""5U\377\27""8"
"X\377\32:Z\377\35=\\\377Dt\223\377u\265\316\377=UE\377Re#\377`x'\377"
"e\205\36\377\202\241;\377\211\246D\377Ruz\377c\216\240\377c\216\240\377"
"Lkx\377484\377La\37\377_y'\377g\207#\377}\2338\377u\215<\377La<\377v"
"\265\316\377w\270\324\377t\267\322\377r\265\321\377n\263\320\377l\261"
"\316\376\31'-g\0\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\0\0\0E\6"
"6c\356P\237\304\377)k\226\377\2""6i\377\0""4g\377\0""5h\377\0""1`\377"
"\0$E\3770G)\377w\2253\377\200\237=\377f\203%\377Yq\"\377Nb#\377M`!\377"
"Yp(\377.:!\377\15+I\377\21""3V\377\24""6W\377\27""8X\377\32<\\\377Eu"
"\223\377z\301\337\377w\272\325\377:M#\377H\\\36\377e\205\33\377p\224"
"\40\377\233\302C\377]l8\377a\215\232\377f\225\250\377_\213\235\377`\213"
"\236\377A_f\377FT*\377Tj$\377b\177$\377p\223\"\377}\2372\377z\223A\377"
"_\221\242\377v\267\324\377s\266\321\377p\264\321\377m\263\320\377i\260"
"\315\376\26#'d\0\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\0\0\0B\1"
".Z\350\24O~\377a\265\330\377D\217\270\377\2""7j\377\0""4h\377\0""4f\377"
"\15';\377Xi2\377;E$\3771<\35\377Mb\35\377J_\34\377ES'\377FT(\377CU\33"
"\377.=%\377\14""0S\377\17""2T\377\31 at a\377D~\244\377h\254\316\377x\301"
"\340\377z\303\340\377Eie\377M^)\377Vk(\377c\201\40\377j\217\32\377u\216"
";\377z\222\77\377^tx\377\217\236\245\377lop\377[u\200\377>RV\377ET#\377"
"Na#\377Vp\33\377g\206\"\377r\221.\377m\202=\377V\177\210\377t\267\323"
"\377r\266\321\377n\263\320\377l\261\320\377h\254\311\374\14\23\25Z\0"
"\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0:\7""0X\330*l\230\377"
"a\265\330\377a\265\331\3771v\240\377\0""4h\377\0""5h\377\2$G\377J@*\377"
"p\\8\377TE)\377\40\34\37\377 at H0\3779F\36\3774A\27\377=J\37\377\33,(\377"
"\34Ff\377<o\211\377e\254\313\377t\300\340\377u\300\340\377v\301\337\377"
"^\230\253\377EU&\377IZ%\377]z\34\377k\221\23\377v\231$\377\204\237H\377"
"Ra0\377\215\215\214\377\262\262\262\377\276\276\275\377kx}\3778MQ\377"
"4@\26\377=M\35\377JZ'\377[v\36\377r\2172\377\212\247D\377\\\204\204\377"
"s\266\323\377p\265\321\377m\263\320\377k\261\317\377d\246\303\370\3\4"
"\4N\0\0\0\22\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0""2\20""8X\300"
"H\224\273\377`\264\330\377a\265\330\377Z\254\321\377\2""6i\377\0""5h"
"\377\1""5e\377\221\204a\377\376\370\356\377\312\260z\377yg>\377\14\15"
"\21\3779D\35\3772<\33\377;E%\377\12#7\3772f\205\377c\253\312\377p\277"
"\337\377q\274\335\377p\271\330\377u\256\306\377K^T\377=K#\377FT'\377"
"Zv\36\377Zn0\377ay/\377w\212F\377bw0\377\263\263\262\377\263\263\262"
"\377\375\375\373\377\234\237\240\3777NV\3779F\35\377;K\27\377AN#\377"
"m\212)\377\201\2453\377\203\235J\377U~\177\377p\265\321\377n\263\320"
"\377l\262\317\377i\260\316\377Y\226\260\350\0\0\0>\0\0\0\20\0\0\0\4\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\23\0\0\0+\27;S\245D\216\265\377`\264\327\377"
"a\265\330\377a\265\331\377\32X\206\377\0""4g\377%`\211\377\214\177Z\377"
"\370\363\347\377\306\256z\377\301\250u\377\26\24\24\3774;%\3775@\37\377"
"\20\37\35\377\5-S\377\14>m\377]\225\262\377\201\251\271\377\230\254\265"
"\377\257\263\265\377\300\300\300\377T]@\377IZ(\377[n+\377c}%\377b{)\377"
"}\232<\377\217\252L\377T\\A\377\235\235\234\377\371\371\367\377\376\376"
"\374\377\332\332\330\377Ry\213\377!*\37\377 at N$\377K]$\377\40$\23\377"
"&'\37\377OZ4\377Z\221\243\377n\264\321\377m\262\317\377j\261\317\377"
"h\257\316\377N\205\234\320\0\0\0""2\0\0\0\16\0\0\0\3\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\20\0\0\0$\26*4wQ\236\303\377_\262\325\377_\262\325\377`\263"
"\326\377<\204\254\377\0""4f\377\37Sw\377ohI\377\364\354\335\377\305\255"
"{\377\311\261\177\377\21\40(\377\20\35,\377\25$.\3778I[\377it\177\377"
"\256\261\263\377\302\302\302\377\310\310\310\377\314\314\314\377\317"
"\317\317\377\322\322\322\377[\\W\377563\377\24\25\33\377\26\23\23\377"
"4(\32\377\15\16\13\377h~7\377VXP\377\343\343\342\377\376\376\374\377"
"\376\376\374\377\372\372\370\377Smy\377Aet\377+<6\377G>+\377\260\242"
"\213\377\346\331\276\377\221~S\377V\215\244\377l\261\316\377j\260\316"
"\377h\256\314\377e\255\313\377;ev\251\0\0\0'\0\0\0\14\0\0\0\3\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\15\0\0\0\36\1\2\2N1o\223\361\\\255\317\377\\\255\317"
"\377]\256\320\377\\\256\317\377\34X\204\377\22Ck\377CSM\377\335\315\250"
"\377\327\306\241\377\314\265\200\377SVR\377\200\202\204\377\214\214\214"
"\377\217\217\217\377\264\264\264\377\317\317\317\377\322\322\322\377"
"\326\326\326\377\331\331\331\377\334\334\334\377\337\337\337\377xxy\377"
",-0\377>4\"\377\270\235d\377\350\327\265\377hW5\377##$\377\232\232\231"
"\377\366\366\364\377\376\376\374\377\376\376\374\377\375\375\373\377"
"fv{\377Q\200\225\377\77dt\377\235\210]\377\357\346\322\377\332\307\240"
"\377\230\203T\377W\216\246\377h\254\310\377f\253\310\377d\252\306\377"
"a\250\305\377\32-6v\0\0\0\35\0\0\0\11\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\12\0\0\0\30\0\0\0""8(Xs\306Z\250\310\377Z\251\312\377Y\247\310\377"
"X\245\305\377f\240\272\377k\212\233\377\\in\377\213wL\377\360\346\322"
"\377\313\264\200\377\202tV\377\202\202\202\377\226\226\226\377\233\233"
"\233\377\322\322\322\377\334\334\334\377\332\332\332\377\340\340\340"
"\377\217\215\207\377\334\334\334\377\352\352\352\377\264\264\264\377"
"a[P\377\263\227\\\377\317\271\213\377\360\350\323\377m\\8\377www\377"
"\274\274\272\377\362\362\360\377\376\376\374\377\374\374\372\377\371"
"\370\366\377\200\203\205\377Jv\210\377NSI\377\317\271\211\377\363\355"
"\341\377\310\257x\377a\\C\377\\\232\265\377c\247\303\377`\246\302\377"
"_\244\301\377R\216\247\352\0\0\1F\0\0\0\26\0\0\0\7\0\0\0\1\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\7\0\0\0\23\0\0\0(\23*7\207P\230\267\377t\244\270\377"
"\242\264\274\377\275\300\302\377\307\307\306\377\273\273\273\377\222"
"\222\222\377ca[\377\257\234v\377\335\312\243\377\316\266\203\377~rV\377"
"mic\377\207\206\204\377\227\224\217\377\215\204p\377i^F\377\310\310\310"
"\377\203pE\377\253\253\251\377\362\362\362\377usp\377\226}F\377\303\252"
"t\377\354\341\313\377\244\217f\377\\[X\377\261\261\260\377\237\237\235"
"\377rdE\377\223\214\177\377\271\271\267\377\324\324\322\377\205\204\204"
"\377STI\377\305\260\200\377\355\343\316\377\317\270\211\377\234\205S"
"\3777Wc\377_\241\274\377_\241\275\377\\\240\274\377Z\236\273\3778cu\264"
"\0\0\0-\0\0\0\20\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\17"
"\0\0\0\37\0\0\0L7l\204\351\242\265\275\377\330\330\327\377\300\300\300"
"\377\320\320\320\377\315\315\315\377\235\235\235\377\234\234\234\377"
"nmj\377\207wW\377\314\264\200\377\324\273\207\377\324\274\207\377\323"
"\272\207\377\323\271\201\377\220{N\377\255\253\247\377\340\340\340\377"
"\230~H\377zkJ\377\203|n\377\227\200M\377\304\253u\377\337\316\254\377"
"\264\243\202\377ROH\377\251\251\251\377\270\267\266\377\364\363\361\377"
"\211\202u\377\250\213P\377\272\241k\377\253\230l\377\300\253{\377\323"
"\273\210\377\330\305\233\377\310\256x\377\213uF\3775LR\377Du\211\377"
"Z\233\267\377Z\234\267\377X\232\266\377S\223\257\372\20\35\"i\0\0\0\37"
"\0\0\0\15\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\12\0\0\0\30"
"\0\0\0""2\36""9E\251v\226\244\377\375\375\373\377\354\354\352\377\267"
"\267\266\377\265\265\265\377\250\250\250\377\247\247\247\377\252\252"
"\252\377\235\235\235\377\220\216\211\377\225\214x\377\227\213r\377\230"
"\214u\377\225\220\205\377\326\326\326\377\376\376\376\377\365\365\365"
"\377~mF\377\300\243f\377\307\256w\377\314\265\201\377\324\277\217\377"
"\241\217g\377QOI\377\240\240\240\377\264\264\263\377\330\327\326\377"
"\367\366\364\377\360\357\356\377\243\240\234\377\203uW\377\220zO\377"
"\237\211Y\377\231\204V\377wlL\377EMC\3771S`\377<fx\377>l\177\377O\213"
"\245\377O\213\244\377N\212\243\3775_q\313\0\0\0I\0\0\0+\0\0\0!\0\0\0"
"\23\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\22\0\0\0$\0\2\2"
"_Lp\200\357\356\356\354\377\376\376\374\377\376\376\374\377\334\334\332"
"\377\216\216\215\377~~~\377\241\241\241\377\264\264\264\377\310\310\310"
"\377\373\373\373\377\374\374\374\377\375\375\375\377\376\376\376\377"
"\376\376\376\377\374\374\374\377\373\373\373\377\252\251\247\377\201"
"rS\377\234\206X\377\234\207Y\377ujS\377VVU\377\234\234\232\377\263\263"
"\261\377\304\303\302\377\363\361\357\377\354\354\352\377\356\354\353"
"\377\356\354\352\377\350\346\345\377\331\327\325\377\316\313\312\377"
"\202\202\201\377t{\177\377u}\200\377t|\177\377r{\177\377\205\217\223"
"\377\236\252\256\377\234\250\255\377\234\247\253\376\236\237\237\344"
"\245\245\244\327\244\244\244\325\243\243\242\323LLLr\0\0\0\22\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\32\0\0\0:\24$*\241\320\324"
"\325\377\376\376\374\377\376\376\374\377\376\376\374\377\374\374\372"
"\377\252\252\251\377oon\377www\377\230\230\230\377\304\304\304\377\337"
"\337\337\377\344\344\344\377\333\333\333\377\310\310\310\377\310\310"
"\310\377\353\353\353\377\271\271\271\377\362\362\362\377\344\344\344"
"\377\264\264\264\377kkj\377\233\233\232\377\257\257\255\377\266\266\264"
"\377\352\351\347\377\362\360\356\377\311\310\307\377\263\262\260\377"
"\312\310\306\377\331\327\325\377\333\330\327\377\312\307\305\377\206"
"\204\203\377\233\233\232\377\266\266\264\377\266\266\264\377\273\273"
"\271\377\364\364\362\377\376\376\374\377\376\376\374\377\361\341\330"
"\377\362\335\323\377\362\337\325\377\363\341\331\377\374\372\367\377"
"kkk\246\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\23"
"\0\0\0'\0\0\0c\233\244\250\361\376\376\374\377\376\376\374\377\376\376"
"\374\377\376\376\374\377\374\374\372\377\322\322\322\377\225\225\224"
"\377vvu\377ssr\377\214\214\214\377\262\262\262\377\266\266\266\377\277"
"\277\277\377\361\361\361\377\345\345\345\377\266\266\266\377\311\311"
"\311\377\305\305\305\377jjj\377\231\231\230\377\252\252\250\377\263\263"
"\262\377\340\337\336\377\360\356\354\377\356\353\352\377\345\343\341"
"\377\265\262\261\377\244\242\240\377\242\240\237\377\241\236\236\377"
"\237\234\233\377\230\225\223\377vvu\377\266\266\264\377\274\274\272\377"
"\354\354\352\377\376\376\374\377\376\376\374\377\376\376\374\377\343"
"yj\377\340F9\377\3325$\377\315Q=\377\365\363\356\377fff\250\0\0\0\37"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\14\0\0\0\35\0\0\0"
"Gmmm\267\375\375\373\377\376\376\374\377\376\376\374\377\376\376\374"
"\377\376\376\374\377\376\376\374\377\362\362\360\377\320\320\316\377"
"\245\245\243\377\216\216\216\377\177\177\177\377\234\234\234\377\352"
"\352\352\377\376\376\376\377\361\361\361\377\267\267\267\377\266\266"
"\266\377nnn\377\223\223\222\377\240\240\237\377\260\257\256\377\336\335"
"\333\377\356\354\352\377\354\352\350\377\350\346\344\377\346\344\342"
"\377\340\334\333\377\302\277\275\377\250\246\245\377\236\233\232\377"
"\233\230\227\377\233\230\226\377oon\377\327\327\325\377\367\367\365\377"
"\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\337"
"\236\227\377\312\277\272\377\343\240\231\377\325TD\377\365\362\352\377"
"ccc\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\10"
"\0\0\0\25\0\0\0""8@@@}\353\353\351\377\376\376\374\377\376\376\374\377"
"\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376"
"\376\374\377\376\376\374\377\357\357\356\377\326\326\325\377\313\313"
"\312\377\241\241\240\377\245\245\245\377\342\342\342\377\340\340\340"
"\377}}|\377\215\215\214\377\240\237\236\377\274\272\271\377\334\333\332"
"\377\344\342\340\377\344\342\340\377\346\343\342\377\344\342\340\377"
"\342\337\335\377\340\334\332\377\334\331\330\377\331\325\323\377\322"
"\316\315\377\316\312\310\377\315\310\307\377\211\206\205\377\373\373"
"\371\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
"\377\371\371\367\377\312\204x\377\322\310\313\377\323\313\313\377\343"
"ob\377\366\362\352\377```\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\1\0\0\0\5\0\0\0\17\0\0\0*\5\5\5V\316\316\315\366\376\376"
"\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
"\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377"
"\374\374\372\377\350\350\346\377\332\332\331\377\330\330\330\377\251"
"\251\250\377\220\220\220\377\265\265\263\377\326\325\324\377\340\336"
"\335\377\344\343\341\377\342\340\336\377\340\335\334\377\334\332\331"
"\377\332\330\326\377\333\330\326\377\334\330\326\377\334\327\326\377"
"\330\324\322\377\326\322\320\377\324\316\315\377\320\314\312\377\316"
"\310\307\377\233\227\226\377\332\332\330\377\376\376\374\377\376\376"
"\374\377\376\376\374\377\376\376\374\377\356\356\354\377\314pc\377\315"
"ZP\377\321{v\377\341RB\377\365\361\351\377\\\\\\\250\0\0\0\40\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\37\0\0\0"
"L\250\250\250\316\376\376\374\377\376\376\374\377\376\376\374\377\376"
"\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376"
"\374\377\376\376\374\377\370\370\366\377\365\365\363\377\361\361\357"
"\377\346\345\344\377\337\336\334\377\336\336\334\377\336\335\334\377"
"\342\340\337\377\344\341\337\377\340\336\334\377\336\334\332\377\334"
"\331\330\377\331\326\324\377\326\323\322\377\324\320\317\377\322\316"
"\314\377\320\314\313\377\322\316\314\377\322\314\313\377\320\312\310"
"\377\314\307\306\377\312\304\302\377\277\271\270\377\237\237\236\377"
"\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\375"
"\375\373\377\306\233\220\377\317\202u\377\332\226\210\377\344\263\243"
"\377\362\357\353\377YXX\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\30\0\0\0Bppp\245\375\375\373\377"
"\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376"
"\376\374\377\376\376\374\377\376\376\374\377\373\373\371\377\364\364"
"\362\377\362\361\357\377\356\356\354\377\354\353\351\377\350\347\345"
"\377\342\340\336\377\340\336\335\377\342\337\336\377\336\334\333\377"
"\334\332\330\377\332\330\326\377\330\324\323\377\326\322\320\377\322"
"\317\315\377\320\314\312\377\316\311\307\377\312\306\304\377\310\303"
"\302\377\307\302\301\377\310\303\302\377\310\302\300\377\306\277\276"
"\377\303\274\273\377\207\205\204\377\375\375\373\377\376\376\374\377"
"\376\376\374\377\376\375\373\377\375\374\372\377\366\365\364\377\371"
"\370\366\377\366\366\364\377\367\366\364\377\365\364\362\377UUS\250\0"
"\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0"
"\0\0\23\0\0\0""8<<<z\350\350\346\377\376\376\374\377\376\376\374\377"
"\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\375"
"\375\373\377\363\363\361\377\360\357\355\377\355\354\352\377\352\351"
"\347\377\350\346\344\377\346\343\342\377\342\340\337\377\340\336\334"
"\377\336\333\332\377\333\330\327\377\330\326\324\377\326\323\321\377"
"\324\320\316\377\321\315\313\377\316\312\310\377\314\307\306\377\312"
"\304\302\377\306\301\300\377\304\277\276\377\302\274\273\377\277\272"
"\270\377\276\270\266\377\276\270\266\377\276\270\266\377xut\377\372\372"
"\370\377\374\374\372\377\373\373\371\377\371\371\367\377\370\367\365"
"\377\366\366\364\377\365\364\362\377\363\362\360\377\362\360\357\377"
"\360\357\355\377PPP\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0*\6\6\6V\310\310\307\367\376\376"
"\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
"\377\374\374\372\377\364\364\362\377\356\356\354\377\354\352\350\377"
"\350\350\346\377\346\344\343\377\344\342\340\377\341\337\335\377\336"
"\334\332\377\334\332\330\377\332\327\325\377\330\324\322\377\324\321"
"\320\377\322\316\314\377\320\313\312\377\314\310\306\377\312\306\304"
"\377\310\302\301\377\305\300\276\377\302\276\274\377\300\273\271\377"
"\276\270\266\377\274\264\263\377\271\262\260\377\241\233\231\377wsr\377"
"jhg\377\362\362\360\377\370\367\365\377\366\365\363\377\365\364\362\377"
"\363\362\360\377\362\360\356\377\360\357\355\377\357\355\353\377\355"
"\353\351\377\354\351\350\377LLL\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\40\0\0\0L\240\240"
"\237\321\376\376\374\377\376\376\374\377\376\376\374\377\375\375\373"
"\377\372\372\370\377\366\365\364\377\354\354\352\377\352\350\347\377"
"\350\346\344\377\344\343\341\377\342\340\336\377\340\335\334\377\334"
"\332\331\377\332\330\326\377\330\325\324\377\326\322\320\377\324\317"
"\316\377\320\314\312\377\316\312\310\377\314\306\305\377\310\304\302"
"\377\306\300\300\377\304\276\275\377\300\274\272\377\277\271\270\377"
"\247\241\240\377~zy\377vtt\377xwv\377\220\220\217\377\267\267\267\377"
"\271\270\266\377\345\343\342\377\363\362\360\377\362\360\356\377\360"
"\356\355\377\357\355\353\377\355\353\351\377\353\351\347\377\352\350"
"\346\377\350\346\344\377\347\344\342\377GGG\250\0\0\0\40\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\30\0"
"\0\0Cfff\247\376\376\374\377\376\376\374\377\374\374\372\377\372\371"
"\367\377\366\366\364\377\354\353\352\377\350\347\345\377\346\344\342"
"\377\344\341\337\377\340\336\334\377\336\334\332\377\334\331\330\377"
"\331\326\324\377\326\323\322\377\324\320\317\377\322\316\314\377\317"
"\313\311\377\314\310\306\377\312\304\303\377\310\302\300\377\304\277"
"\276\377\255\250\247\377\204\200\177\377ron\377_^]\377eed\377\214\213"
"\213\377\232\231\230\377\247\246\245\377\274\273\271\377\302\302\300"
"\377\350\346\344\377\360\356\355\377\356\355\353\377\355\353\351\377"
"\353\351\347\377\352\347\346\377\350\346\344\377\347\344\342\377\345"
"\342\341\377\344\341\337\377\342\337\335\377DCC\250\0\0\0\40\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\23"
"\0\0\0""8555}\345\345\343\377\372\372\370\377\370\367\365\377\365\364"
"\362\377\356\355\353\377\346\345\343\377\344\342\340\377\342\337\336"
"\377\336\334\333\377\334\332\330\377\332\330\326\377\330\324\323\377"
"\326\322\320\377\322\317\315\377\320\314\312\377\316\311\307\377\312"
"\306\304\377\263\257\255\377\213\210\207\377\177~}\377\202\201\200\377"
"\230\230\230\377\277\277\275\377\313\313\311\377\302\301\300\377\272"
"\271\270\377\300\277\275\377\320\317\315\377\332\331\327\377\333\332"
"\330\377\353\351\347\377\355\353\351\377\353\351\347\377\352\347\345"
"\377\350\346\344\377\347\344\342\377\345\342\341\377\344\340\337\377"
"\342\337\335\377\340\335\333\377\337\333\332\377\335\332\330\377@\77"
"\77\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0+\7\7\7W\275\275\274\370\366\365\364\377"
"\364\362\360\377\357\356\354\377\346\344\342\377\342\340\337\377\340"
"\336\334\377\336\333\332\377\333\330\327\377\330\326\324\377\326\323"
"\321\377\324\320\316\377\322\315\314\377\271\266\264\377\224\221\220"
"\377\212\210\207\377\205\204\203\377\220\220\217\377\233\233\231\377"
"\225\225\225\377\230\230\226\377\251\250\247\377\302\301\277\377\327"
"\325\324\377\335\335\333\377\326\325\323\377\316\314\312\377\312\311"
"\310\377\310\306\304\377\334\332\330\377\352\347\345\377\350\345\344"
"\377\347\344\342\377\345\342\340\377\343\340\337\377\342\337\335\377"
"\340\335\333\377\337\333\331\377\335\332\330\377\334\330\326\377\332"
"\326\325\377\331\324\323\377<;;\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\40\0\0\0"
"M\223\223\223\323\362\360\356\377\357\356\354\377\346\344\342\377\342"
"\337\335\377\336\334\332\377\334\332\330\377\332\327\325\377\330\324"
"\322\377\277\274\273\377\231\227\226\376\221\217\216\377\215\214\213"
"\377\240\240\237\377\303\303\301\377\316\315\314\377\325\325\324\377"
"\337\336\334\377\350\347\346\377\357\356\354\377\361\360\356\377\362"
"\360\356\377\361\357\356\377\357\356\354\377\356\354\352\377\354\352"
"\351\377\353\351\347\377\351\347\345\377\350\345\343\377\346\344\342"
"\377\345\342\340\377\343\340\337\377\342\336\335\377\340\335\333\377"
"\337\333\331\377\335\331\330\377\333\330\326\377\332\326\324\377\330"
"\324\323\377\327\323\321\377\325\321\317\377\324\317\315\377866\250\0"
"\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\2\0\0\0\11\0\0\0\31\0\0\0DYYY\253\356\354\352\377\350\345\344\377"
"\340\335\334\377\334\332\331\377\305\303\302\377\237\235\235\372zyy\327"
"HGG\254\35\35\35\213www\337\317\317\315\377\327\326\324\377\340\340\336"
"\377\352\351\347\377\360\357\355\377\362\361\360\377\363\362\360\377"
"\362\361\357\377\361\357\355\377\357\356\354\377\356\354\352\377\354"
"\352\351\377\353\351\347\377\351\347\345\377\350\345\343\377\346\343"
"\342\377\345\342\340\377\343\340\336\377\341\336\335\377\340\335\333"
"\377\336\333\331\377\335\331\327\377\333\330\326\377\332\326\324\377"
"\330\324\323\377\327\322\321\377\325\321\317\377\324\317\315\377\322"
"\315\314\377\321\313\312\377\317\312\310\377322\250\0\0\0\40\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6"
"\0\0\0\23\0\0\0""8+++~\274\272\271\377\247\245\244\372\202\200\200\326"
"NNN\254%%%\201\6\6\6X\0\0\0M\0\0\0F\0\0\0K\222\222\221\330\361\361\357"
"\377\364\363\361\377\364\363\362\377\364\363\361\377\362\361\357\377"
"\361\357\355\377\357\355\354\377\356\354\352\377\354\352\350\377\353"
"\350\347\377\351\347\345\377\350\345\343\377\346\343\341\377\345\342"
"\340\377\343\340\336\377\341\336\335\377\340\334\333\377\336\333\331"
"\377\335\331\327\377\333\327\326\377\332\326\324\377\330\324\322\377"
"\327\322\321\377\325\321\317\377\324\317\315\377\322\315\313\377\321"
"\313\312\377\317\312\310\377\316\310\307\377\314\306\305\377\312\305"
"\303\377/..\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0)\0\0\0P\"\"\"v\7\7\7"
"X\0\0\0M\0\0\0D\0\0\0""9\0\0\0-\0\0\0$\0\0\0#\0\0\0""5\227\227\226\326"
"\364\363\361\377\362\361\357\377\361\357\355\377\357\355\354\377\356"
"\354\352\377\354\352\350\377\353\350\347\377\351\347\345\377\347\345"
"\343\377\346\343\341\377\344\341\340\377\343\340\336\377\341\336\334"
"\377\340\334\333\377\336\333\331\377\335\331\327\377\333\327\325\377"
"\332\326\324\377\330\324\322\377\327\322\321\377\325\320\317\377\323"
"\317\315\377\322\315\313\377\320\313\312\377\317\312\310\377\315\310"
"\306\377\314\306\305\377\312\305\303\377\311\303\301\377\307\301\277"
"\377\306\277\276\377+**\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\12\0\0\0\30\0\0\0"
"0\0\0\0""5\0\0\0,\0\0\0\"\0\0\0\34\0\0\0\26\0\0\0\22\0\0\0\21\0\0\0\27"
"\0\0\0""1\223\222\222\326\357\355\353\377\355\353\352\377\354\352\350"
"\377\352\350\346\377\351\347\345\377\347\345\343\377\346\343\341\377"
"\344\341\340\377\343\340\336\377\341\336\334\377\340\334\333\377\336"
"\332\331\377\335\331\327\377\333\327\325\377\331\325\324\377\330\324"
"\322\377\326\322\320\377\325\320\317\377\323\317\315\377\322\315\313"
"\377\320\313\311\377\317\311\310\377\315\310\306\377\314\306\305\377"
"\312\304\303\377\311\303\301\377\307\301\277\377\306\277\276\377\304"
"\276\274\377\302\274\272\377\301\272\271\377'&&\250\0\0\0\40\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"
"\0\0\0\5\0\0\0\14\0\0\0\21\0\0\0\23\0\0\0\21\0\0\0\16\0\0\0\13\0\0\0"
"\10\0\0\0\6\0\0\0\10\0\0\0\22\0\0\0/\216\215\214\325\352\350\346\377"
"\351\346\345\377\347\345\343\377\346\343\341\377\344\341\337\377\342"
"\337\336\377\341\336\334\377\337\334\332\377\336\332\331\377\334\331"
"\327\377\333\327\325\377\331\325\324\377\330\324\322\377\326\322\320"
"\377\325\320\317\377\323\316\315\377\322\315\313\377\320\313\311\377"
"\317\311\310\377\315\310\306\377\314\306\304\377\312\304\303\377\311"
"\303\301\377\307\301\277\377\306\277\276\377\304\275\274\377\302\274"
"\272\377\301\272\271\377\277\270\267\377\276\267\265\377\274\265\263"
"\377!\40\40\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\6\0\0\0\7\0\0"
"\0\5\0\0\0\4\0\0\0\3\0\0\0\2\0\0\0\2\0\0\0\5\0\0\0\17\0\0\0'777|VUU\252"
"TTT\252RRR\252QPP\252ONN\252MLL\252LKK\252III\252HHG\252FEE\252DCC\252"
"CBB\252@@@\252\77>=\252=<<\252<::\252999\252776\252654\252433\252311"
"\252100\252.--\252-,+\252+**\252*((\252(''\252%%$\252$#\"\252\"!!\252"
"\37\36\36\251\1\1\1_\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1"
"\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\32\0"
"\0\0&\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,"
"\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0"
",\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0"
"\0,\0\0\0,\0\0\0*\0\0\0\"\0\0\0\23\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0"
"\17\0\0\0\26\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0"
"\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30"
"\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0"
"\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0"
"\0\0\30\0\0\0\27\0\0\0\22\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0"};
--- NEW FILE: notification_trayicon.h ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2007 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_TRAYICON_H
#define NOTIFICATION_TRAYICON_H NOTIFICATION_TRAYICON_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_TRAYICON
#include <glib.h>
#include "procmsg.h"
#define TRAYICON_SPECIFIC_FOLDER_ID_STR "trayicon"
void notification_trayicon_msg(MsgInfo*);
void notification_trayicon_destroy(void);
/* creates it, if necessary */
void notification_update_trayicon(void);
gboolean notification_trayicon_main_window_close(gpointer, gpointer);
gboolean notification_trayicon_main_window_got_iconified(gpointer, gpointer);
gboolean notification_trayicon_account_list_changed(gpointer, gpointer);
gboolean notification_trayicon_is_available(void);
void notification_trayicon_on_activate(GtkStatusIcon*,gpointer);
#endif /* NOTIFICATION_TRAYICON */
#endif /* include guard */
--- NEW FILE: notification_command.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_COMMAND
#include <string.h>
#include <glib.h>
#include "common/utils.h"
#include "folder.h"
#include "notification_command.h"
#include "notification_prefs.h"
#include "notification_foldercheck.h"
typedef struct {
gboolean blocked;
guint timeout_id;
} NotificationCommand;
static gboolean command_timeout_fun(gpointer data);
static NotificationCommand command;
G_LOCK_DEFINE_STATIC(command);
void notification_command_msg(MsgInfo *msginfo)
{
gchar *ret_str, *buf;
gsize by_read = 0, by_written = 0;
FolderType ftype;
if(!msginfo || !notify_config.command_enabled || !MSG_IS_NEW(msginfo->flags))
return;
if(!notify_config.command_enabled || !MSG_IS_NEW(msginfo->flags))
return;
if(notify_config.command_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(COMMAND_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;
} /* folder specific */
ftype = msginfo->folder->folder->klass->type;
buf = g_strdup(notify_config.command_line);
G_LOCK(command);
if(!command.blocked) {
/* execute command */
command.blocked = TRUE;
ret_str = g_locale_from_utf8(buf,strlen(buf),&by_read,&by_written,NULL);
if(ret_str && by_written) {
g_free(buf);
buf = ret_str;
}
execute_command_line(buf, TRUE);
g_free(buf);
}
/* block further execution for some time,
no matter if it was blocked or not */
if(command.timeout_id)
g_source_remove(command.timeout_id);
command.timeout_id = g_timeout_add(notify_config.command_timeout,
command_timeout_fun, NULL);
G_UNLOCK(command);
}
static gboolean command_timeout_fun(gpointer data)
{
G_LOCK(command);
command.timeout_id = 0;
command.blocked = FALSE;
G_UNLOCK(command);
return FALSE;
}
#endif /* NOTIFICATION_COMMAND */
--- NEW FILE: notification_core.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#include "folder.h"
#include "folderview.h"
#include "codeconv.h"
#include "gtk/gtkutils.h"
#include "notification_core.h"
#include "notification_plugin.h"
#include "notification_prefs.h"
#include "notification_banner.h"
#include "notification_popup.h"
#include "notification_command.h"
#include "notification_lcdproc.h"
#include "notification_trayicon.h"
#include "notification_indicator.h"
#ifdef HAVE_LIBCANBERRA_GTK
# include <canberra-gtk.h>
#endif
typedef struct {
GSList *collected_msgs;
GSList *folder_items;
gboolean unread_also;
gint max_msgs;
gint num_msgs;
} TraverseCollect;
static gboolean notification_traverse_collect(GNode*, gpointer);
static void notification_new_unnotified_do_msg(MsgInfo*);
static gboolean notification_traverse_hash_startup(GNode*, gpointer);
static GHashTable *msg_count_hash;
static NotificationMsgCount msg_count;
#ifdef HAVE_LIBCANBERRA_GTK
static gboolean canberra_new_email_is_playing = FALSE;
#endif
static void msg_count_hash_update_func(FolderItem*, gpointer);
static void msg_count_update_from_hash(gpointer, gpointer, gpointer);
static void msg_count_clear(NotificationMsgCount*);
static void msg_count_add(NotificationMsgCount*,NotificationMsgCount*);
static void msg_count_copy(NotificationMsgCount*,NotificationMsgCount*);
void notification_core_global_includes_changed(void)
{
#ifdef NOTIFICATION_BANNER
notification_update_banner();
#endif
if(msg_count_hash) {
g_hash_table_destroy(msg_count_hash);
msg_count_hash = NULL;
}
notification_update_msg_counts(NULL);
}
/* Hide/show main window */
void notification_toggle_hide_show_window(void)
{
MainWindow *mainwin;
if((mainwin = mainwindow_get_mainwindow()) == NULL)
return;
if(gtk_widget_get_visible(GTK_WIDGET(mainwin->window))) {
if((gdk_window_get_state(GTK_WIDGET(mainwin->window)->window)&GDK_WINDOW_STATE_ICONIFIED)
|| mainwindow_is_obscured()) {
notification_show_mainwindow(mainwin);
}
else {
main_window_hide(mainwin);
}
}
else {
notification_show_mainwindow(mainwin);
}
}
void notification_update_msg_counts(FolderItem *removed_item)
{
if(!msg_count_hash)
msg_count_hash = g_hash_table_new_full(g_str_hash,g_str_equal,
g_free,g_free);
folder_func_to_all_folders(msg_count_hash_update_func, msg_count_hash);
if(removed_item) {
gchar *identifier;
identifier = folder_item_get_identifier(removed_item);
if(identifier) {
g_hash_table_remove(msg_count_hash, identifier);
g_free(identifier);
}
}
msg_count_clear(&msg_count);
g_hash_table_foreach(msg_count_hash, msg_count_update_from_hash, NULL);
#ifdef NOTIFICATION_LCDPROC
notification_update_lcdproc();
#endif
#ifdef NOTIFICATION_TRAYICON
notification_update_trayicon();
#endif
#ifdef NOTIFICATION_INDICATOR
notification_update_indicator();
#endif
notification_update_urgency_hint();
}
static void msg_count_clear(NotificationMsgCount *count)
{
count->new_msgs = 0;
count->unread_msgs = 0;
count->unreadmarked_msgs = 0;
count->marked_msgs = 0;
count->total_msgs = 0;
}
/* c1 += c2 */
static void msg_count_add(NotificationMsgCount *c1,NotificationMsgCount *c2)
{
c1->new_msgs += c2->new_msgs;
c1->unread_msgs += c2->unread_msgs;
c1->unreadmarked_msgs += c2->unreadmarked_msgs;
c1->marked_msgs += c2->marked_msgs;
c1->total_msgs += c2->total_msgs;
}
/* c1 = c2 */
static void msg_count_copy(NotificationMsgCount *c1,NotificationMsgCount *c2)
{
c1->new_msgs = c2->new_msgs;
c1->unread_msgs = c2->unread_msgs;
c1->unreadmarked_msgs = c2->unreadmarked_msgs;
c1->marked_msgs = c2->marked_msgs;
c1->total_msgs = c2->total_msgs;
}
gboolean get_flat_gslist_from_nodes_traverse_func(GNode *node, gpointer data)
{
if(node->data) {
GSList **list = data;
*list = g_slist_prepend(*list, node->data);
}
return FALSE;
}
GSList* get_flat_gslist_from_nodes(GNode *node)
{
GSList *retval = NULL;
g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, get_flat_gslist_from_nodes_traverse_func, &retval);
return retval;
}
void notification_core_get_msg_count_of_foldername(gchar *foldername, NotificationMsgCount *count)
{
GList *list;
Folder *walk_folder;
Folder *folder = NULL;
for(list = folder_get_list(); list != NULL; list = list->next) {
walk_folder = list->data;
if(strcmp2(foldername, walk_folder->name) == 0) {
folder = walk_folder;
break;
}
}
if(!folder) {
debug_print("Notification plugin: Error: Could not find folder %s\n", foldername);
return;
}
msg_count_clear(count);
notification_core_get_msg_count(get_flat_gslist_from_nodes(folder->node), count);
}
void notification_core_get_msg_count(GSList *folder_list,
NotificationMsgCount *count)
{
GSList *walk;
if(!folder_list)
msg_count_copy(count,&msg_count);
else {
msg_count_clear(count);
for(walk = folder_list; walk; walk = walk->next) {
gchar *identifier;
NotificationMsgCount *item_count;
FolderItem *item = (FolderItem*) walk->data;
identifier = folder_item_get_identifier(item);
if(identifier) {
item_count = g_hash_table_lookup(msg_count_hash,identifier);
g_free(identifier);
if(item_count)
msg_count_add(count, item_count);
}
}
}
}
static void msg_count_hash_update_func(FolderItem *item, gpointer data)
{
gchar *identifier;
NotificationMsgCount *count;
GHashTable *hash = data;
if(!notify_include_folder_type(item->folder->klass->type,
item->folder->klass->uistr))
return;
identifier = folder_item_get_identifier(item);
if(!identifier)
return;
count = g_hash_table_lookup(hash, identifier);
if(!count) {
count = g_new0(NotificationMsgCount,1);
g_hash_table_insert(hash, identifier, count);
}
else
g_free(identifier);
count->new_msgs = item->new_msgs;
count->unread_msgs = item->unread_msgs;
count->unreadmarked_msgs = item->unreadmarked_msgs;
count->marked_msgs = item->marked_msgs;
count->total_msgs = item->total_msgs;
}
static void msg_count_update_from_hash(gpointer key, gpointer value,
gpointer data)
{
NotificationMsgCount *count = value;
msg_count_add(&msg_count,count);
}
/* Replacement for the post-filtering hook:
Pseudocode by Colin:
hook on FOLDER_ITEM_UPDATE_HOOKLIST
if hook flags & F_ITEM_UPDATE_MSGCOUNT
scan mails (folder_item_get_msg_list)
if MSG_IS_NEW(msginfo->flags) and not in hashtable
notify()
add to hashtable
procmsg_msg_list_free
hook on MSGINFO_UPDATE_HOOKLIST
if hook flags & MSGINFO_UPDATE_FLAGS
if !MSG_IS_NEW(msginfo->flags)
remove from hashtable, it's now useless
*/
/* This hash table holds all mails that we already notified about,
and that still are marked as "new". The keys are the msgid's,
the values are just 1's stored in a pointer. */
static GHashTable *notified_hash = NULL;
/* Remove message from the notified_hash if
* - the message flags changed
* - the message is not new
* - the message is in the hash
*/
gboolean notification_notified_hash_msginfo_update(MsgInfoUpdate *msg_update)
{
g_return_val_if_fail(msg_update != NULL, FALSE);
if((msg_update->flags & MSGINFO_UPDATE_FLAGS) &&
!MSG_IS_NEW(msg_update->msginfo->flags)) {
MsgInfo *msg;
gchar *msgid;
msg = msg_update->msginfo;
if(msg->msgid)
msgid = msg->msgid;
else {
debug_print("Notification Plugin: Message has no message ID!\n");
msgid = "";
}
g_return_val_if_fail(msg != NULL, FALSE);
if(g_hash_table_lookup(notified_hash, msgid) != NULL) {
debug_print("Notification Plugin: Removing message id %s from hash "
"table\n", msgid);
g_hash_table_remove(notified_hash, msgid);
}
}
return FALSE;
}
/* On startup, mark all new mails as already notified
* (by including them in the hash) */
void notification_notified_hash_startup_init(void)
{
GList *folder_list, *walk;
Folder *folder;
if(!notified_hash) {
notified_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
debug_print("Notification Plugin: Hash table created\n");
}
folder_list = folder_get_list();
for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
folder = walk->data;
g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
notification_traverse_hash_startup, NULL);
}
}
static gboolean notification_traverse_hash_startup(GNode *node, gpointer data)
{
GSList *walk;
GSList *msg_list;
FolderItem *item = (FolderItem*) node->data;
gint new_msgs_left;
if(!(item->new_msgs))
return FALSE;
new_msgs_left = item->new_msgs;
msg_list = folder_item_get_msg_list(item);
for(walk = msg_list; walk; walk = g_slist_next(walk)) {
MsgInfo *msg = (MsgInfo*) walk->data;
if(MSG_IS_NEW(msg->flags)) {
gchar *msgid;
if(msg->msgid)
msgid = msg->msgid;
else {
debug_print("Notification Plugin: Message has no message ID!\n");
msgid = "";
}
/* If the message id is not yet in the hash, add it */
g_hash_table_insert(notified_hash, g_strdup(msgid),
GINT_TO_POINTER(1));
debug_print("Notification Plugin: Init: Added msg id %s to the hash\n",
msgid);
/* Decrement left count and check if we're already done */
new_msgs_left--;
if(new_msgs_left == 0)
break;
}
}
procmsg_msg_list_free(msg_list);
return FALSE;
}
void notification_core_free(void)
{
if(notified_hash) {
g_hash_table_destroy(notified_hash);
notified_hash = NULL;
}
if(msg_count_hash) {
g_hash_table_destroy(msg_count_hash);
msg_count_hash = NULL;
}
debug_print("Notification Plugin: Freed internal data\n");
}
void notification_new_unnotified_msgs(FolderItemUpdateData *update_data)
{
GSList *msg_list, *walk;
g_return_if_fail(notified_hash != NULL);
msg_list = folder_item_get_msg_list(update_data->item);
for(walk = msg_list; walk; walk = g_slist_next(walk)) {
MsgInfo *msg;
msg = (MsgInfo*) walk->data;
if(MSG_IS_NEW(msg->flags)) {
gchar *msgid;
if(msg->msgid)
msgid = msg->msgid;
else {
debug_print("Notification Plugin: Message has not message ID!\n");
msgid = "";
}
debug_print("Notification Plugin: Found msg %s, "
"checking if it is in hash...\n", msgid);
/* Check if message is in hash table */
if(g_hash_table_lookup(notified_hash, msgid) != NULL)
debug_print("yes.\n");
else {
/* Add to hashtable */
g_hash_table_insert(notified_hash, g_strdup(msgid),
GINT_TO_POINTER(1));
debug_print("no, added to table.\n");
/* Do the notification */
notification_new_unnotified_do_msg(msg);
}
} /* msg is 'new' */
} /* for all messages */
procmsg_msg_list_free(msg_list);
}
#ifdef HAVE_LIBCANBERRA_GTK
static void canberra_finished_cb(ca_context *c, uint32_t id, int error, void *data)
{
canberra_new_email_is_playing = FALSE;
}
#endif
static void notification_new_unnotified_do_msg(MsgInfo *msg)
{
#ifdef NOTIFICATION_POPUP
notification_popup_msg(msg);
#endif
#ifdef NOTIFICATION_COMMAND
notification_command_msg(msg);
#endif
#ifdef NOTIFICATION_TRAYICON
notification_trayicon_msg(msg);
#endif
#ifdef HAVE_LIBCANBERRA_GTK
/* canberra */
if(notify_config.canberra_play_sounds && !canberra_new_email_is_playing) {
MainWindow *mainwin;
ca_proplist *proplist;
mainwin = mainwindow_get_mainwindow();
ca_proplist_create(&proplist);
ca_proplist_sets(proplist,CA_PROP_EVENT_ID ,"message-new-email");
canberra_new_email_is_playing = TRUE;
ca_context_play_full(ca_gtk_context_get(), 0, proplist, canberra_finished_cb, NULL);
ca_proplist_destroy(proplist);
}
#endif
}
/* If folders is not NULL, then consider only those folder items
* If max_msgs is not 0, stop after collecting msg_msgs messages
*/
GSList* notification_collect_msgs(gboolean unread_also, GSList *folder_items,
gint max_msgs)
{
GList *folder_list, *walk;
Folder *folder;
TraverseCollect collect_data;
collect_data.unread_also = unread_also;
collect_data.collected_msgs = NULL;
collect_data.folder_items = folder_items;
collect_data.max_msgs = max_msgs;
collect_data.num_msgs = 0;
folder_list = folder_get_list();
for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
folder = walk->data;
g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
notification_traverse_collect, &collect_data);
}
return collect_data.collected_msgs;
}
void notification_collected_msgs_free(GSList *collected_msgs)
{
if(collected_msgs) {
GSList *walk;
for(walk = collected_msgs; walk != NULL; walk = g_slist_next(walk)) {
CollectedMsg *msg = walk->data;
if(msg->from)
g_free(msg->from);
if(msg->subject)
g_free(msg->subject);
if(msg->folderitem_name)
g_free(msg->folderitem_name);
msg->msginfo = NULL;
g_free(msg);
}
g_slist_free(collected_msgs);
}
}
static gboolean notification_traverse_collect(GNode *node, gpointer data)
{
TraverseCollect *cdata = data;
FolderItem *item = node->data;
gchar *folder_id_cur;
/* Obey global folder type limitations */
if(!notify_include_folder_type(item->folder->klass->type,
item->folder->klass->uistr))
return FALSE;
/* If a folder_items list was given, check it first */
if((cdata->folder_items) && (item->path != NULL) &&
((folder_id_cur = folder_item_get_identifier(item)) != NULL)) {
FolderItem *list_item;
GSList *walk;
gchar *folder_id_list;
gboolean eq;
gboolean folder_in_list = FALSE;
for(walk = cdata->folder_items; walk != NULL; walk = g_slist_next(walk)) {
list_item = walk->data;
folder_id_list = folder_item_get_identifier(list_item);
eq = !strcmp2(folder_id_list,folder_id_cur);
g_free(folder_id_list);
if(eq) {
folder_in_list = TRUE;
break;
}
}
g_free(folder_id_cur);
if(!folder_in_list)
return FALSE;
}
if(item->new_msgs || (cdata->unread_also && item->unread_msgs)) {
GSList *msg_list = folder_item_get_msg_list(item);
GSList *walk;
for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
MsgInfo *msg_info = walk->data;
CollectedMsg *cmsg;
if((cdata->max_msgs != 0) && (cdata->num_msgs >= cdata->max_msgs))
return FALSE;
if(MSG_IS_NEW(msg_info->flags) ||
(MSG_IS_UNREAD(msg_info->flags) && cdata->unread_also)) {
cmsg = g_new(CollectedMsg, 1);
cmsg->from = g_strdup(msg_info->from ? msg_info->from : "");
cmsg->subject = g_strdup(msg_info->subject ? msg_info->subject : "");
if(msg_info->folder && msg_info->folder->name)
cmsg->folderitem_name = g_strdup(msg_info->folder->path);
else
cmsg->folderitem_name = g_strdup("");
cmsg->msginfo = msg_info;
cdata->collected_msgs = g_slist_prepend(cdata->collected_msgs, cmsg);
cdata->num_msgs++;
}
}
procmsg_msg_list_free(msg_list);
}
return FALSE;
}
gboolean notify_include_folder_type(FolderType ftype, gchar *uistr)
{
gboolean retval;
retval = FALSE;
switch(ftype) {
case F_MH:
case F_MBOX:
case F_MAILDIR:
case F_IMAP:
if(notify_config.include_mail)
retval = TRUE;
break;
case F_NEWS:
if(notify_config.include_news)
retval = TRUE;
break;
case F_UNKNOWN:
if(uistr == NULL)
retval = FALSE;
else if(!strcmp(uistr, "vCalendar")) {
if(notify_config.include_calendar)
retval = TRUE;
}
else if(!strcmp(uistr, "RSSyl")) {
if(notify_config.include_rss)
retval = TRUE;
}
else
debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
break;
default:
debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
}
return retval;
}
static void fix_folderview_scroll(MainWindow *mainwin)
{
static gboolean fix_done = FALSE;
if (fix_done)
return;
gtk_widget_queue_resize(mainwin->folderview->ctree);
fix_done = TRUE;
}
void notification_show_mainwindow(MainWindow *mainwin)
{
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));
fix_folderview_scroll(mainwin);
}
#ifdef HAVE_LIBNOTIFY
#define STR_MAX_LEN 511
/* Returns a newly allocated string which needs to be freed */
gchar* notification_libnotify_sanitize_str(gchar *in)
{
gint i_out;
gchar tmp_str[STR_MAX_LEN+1];
if(in == NULL) return NULL;
i_out = 0;
while(*in) {
if(*in == '<') {
if(i_out+3 >= STR_MAX_LEN) break;
memcpy(&(tmp_str[i_out]),"<",4);
in++; i_out += 4;
}
else if(*in == '>') {
if(i_out+3 >= STR_MAX_LEN) break;
memcpy(&(tmp_str[i_out]),">",4);
in++; i_out += 4;
}
else if(*in == '&') {
if(i_out+4 >= STR_MAX_LEN) break;
memcpy(&(tmp_str[i_out]),"&",5);
in++; i_out += 5;
}
else {
if(i_out >= STR_MAX_LEN) break;
tmp_str[i_out++] = *in++;
}
}
tmp_str[i_out] = '\0';
return strdup(tmp_str);
}
gchar* notification_validate_utf8_str(gchar *text)
{
gchar *utf8_str = NULL;
if(!g_utf8_validate(text, -1, NULL)) {
debug_print("Notification plugin: String is not valid utf8, "
"trying to fix it...\n");
/* fix it */
utf8_str = conv_codeset_strdup(text,
conv_get_locale_charset_str_no_utf8(),
CS_INTERNAL);
/* check if the fix worked */
if(utf8_str == NULL || !g_utf8_validate(utf8_str, -1, NULL)) {
debug_print("Notification plugin: String is still not valid utf8, "
"sanitizing...\n");
utf8_str = g_malloc(strlen(text)*2+1);
conv_localetodisp(utf8_str, strlen(text)*2+1, text);
}
}
else {
debug_print("Notification plugin: String is valid utf8\n");
utf8_str = g_strdup(text);
}
return utf8_str;
}
#endif
--- NEW FILE: version.rc ---
1 VERSIONINFO
FILEVERSION 0, 0, 0, 0
PRODUCTVERSION 0, 0, 0, 0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "FileDescription", "notification Plugin\0"
VALUE "FileVersion", "0.0.0.0\0"
VALUE "ProductVersion", "0.0.0.0 Win32\0"
VALUE "LegalCopyright", "GPL / © 1999-2008 Hiroyuki Yamamoto & The Claws Mail Team\0"
VALUE "CompanyName", "GNU / Free Software Foundation\0"
VALUE "ProductName", "Claws Mail\0"
// VALUE "Comments", "\0"
// VALUE "InternalName", "\0"
// VALUE "LegalTrademarks", "\0"
// VALUE "OriginalFilename", "\0"
// VALUE "PrivateBuild", "\0"
// VALUE "SpecialBuild", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0, 1200
END
END
--- NEW FILE: claws.def ---
LIBRARY CLAWS-MAIL.EXE
EXPORTS
get_locale_dir
check_plugin_version
debug_print_real
debug_srcname
hooks_register_hook
hooks_unregister_hook
account_get_list
addressbook_open
alertpanel
app_will_exit
claws_is_exiting
claws_is_starting
compose_mail_cb
compose_new
compose_reply_from_messageview
execute_command_line
file_exist
folder_find_item_from_identifier
folder_func_to_all_folders
folder_get_list
folder_has_parent_of_type
folder_item_get_identifier
folder_item_get_msg_list
folder_item_get_name
get_rc_dir
gtkut_convert_int_to_gdk_color
gtkut_stock_button_set_create
gtkut_window_new
inc_all_account_mail_cb
main_set_show_at_startup
mainwindow_get_mainwindow
main_window_hide
main_window_empty_trash
mainwindow_is_obscured
mainwindow_jump_to
main_window_show
main_window_toggle_work_offline
manage_window_destroy
manage_window_focus_in
manage_window_focus_out
manage_window_set_transient
manage_window_unmap
cm_menu_create_action_group_full
gtkut_ui_manager
cm_menu_create_action_group
cm_toggle_menu_set_active
cm_menu_set_sensitive
prefs_common_get_prefs
prefs_common_translated_header_name
prefs_file_close
prefs_file_close_revert
prefs_gtk_register_page
prefs_gtk_unregister_page
prefs_read_config
prefs_set_block_label
prefs_set_default
prefs_write_open
prefs_write_param
procmsg_get_message_file_path
procmsg_msg_list_free
stock_pixbuf_gdk
stock_pixbuf_gdk follow
strcmp2
xml_attr_new
xml_file_put_xml_decl
xml_free_tree
xml_node_new
xml_parse_file
xml_tag_add_attr
xml_tag_new
xml_write_tree
--- NEW FILE: notification_core.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_CORE_H
#define NOTIFICATION_CORE_H NOTIFICATION_CORE_H
#include "mainwindow.h"
#include "folder.h"
#include "procmsg.h"
typedef struct {
gchar *from;
gchar *subject;
FolderItem *folder_item;
gchar *folderitem_name;
MsgInfo *msginfo;
} CollectedMsg;
typedef enum {
F_TYPE_MAIL=0,
F_TYPE_NEWS,
F_TYPE_CALENDAR,
F_TYPE_RSS,
F_TYPE_LAST
} NotificationFolderType;
typedef struct
{
guint new_msgs;
guint unread_msgs;
guint unreadmarked_msgs;
guint marked_msgs;
guint total_msgs;
} NotificationMsgCount;
/* Collect new and possibly unread messages in all folders */
GSList* notification_collect_msgs(gboolean, GSList*, gint);
void notification_collected_msgs_free(GSList*);
void notification_core_global_includes_changed(void);
void notification_core_free(void);
void notification_update_msg_counts(FolderItem*);
void notification_core_get_msg_count(GSList*,NotificationMsgCount*);
void notification_core_get_msg_count_of_foldername(gchar*, NotificationMsgCount*);
void notification_new_unnotified_msgs(FolderItemUpdateData*);
gboolean notification_notified_hash_msginfo_update(MsgInfoUpdate*);
void notification_notified_hash_startup_init(void);
void notification_show_mainwindow(MainWindow *mainwin);
/* folder type specific settings */
gboolean notify_include_folder_type(FolderType, gchar*);
void notification_toggle_hide_show_window(void);
#ifdef HAVE_LIBNOTIFY
/* Sanitize a string to use with libnotify. Returns a freshly
* allocated string that must be freed by the user. */
gchar* notification_libnotify_sanitize_str(gchar*);
/* Returns a freshly allocated copy of the input string, which
* is guaranteed to be valid UTF8. The returned string has to
* be freed. */
gchar* notification_validate_utf8_str(gchar*);
#endif
#endif /* NOTIFICATION_CORE_H */
--- NEW FILE: notification_command.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_COMMAND_H
#define NOTIFICATION_COMMAND_H NOTIFICATION_COMMAND_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_COMMAND
#include "procmsg.h"
#define COMMAND_SPECIFIC_FOLDER_ID_STR "command"
void notification_command_msg(MsgInfo*);
#endif /* NOTIFICATION_COMMAND */
#endif /* include guard */
--- NEW FILE: notification_hotkeys.h ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_HOTKEYS_H
#define NOTIFICATION_HOTKEYS_H NOTIFICATION_HOTKEYS_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_HOTKEYS
#include <glib.h>
void notification_hotkeys_update_bindings();
void notification_hotkeys_unbind_all();
#endif /* NOTIFICATION_HOTKEYS */
#endif /* NOTIFICATION_HOTKEYS_H */
--- NEW FILE: notification_pixbuf.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "notification_pixbuf.h"
/* The following files were created from the respective .png or
* xpm files with the command
* gdk-pixbuf-csource --raw --name=<name> file.png > <name>.h
*/
/* From the Claws-Mail distribution */
#include "raw_claws_mail_logo_64x64.h"
#include "stock_pixmap.h"
static GdkPixbuf* notification_pixbuf[NOTIFICATION_PIXBUF_LAST];
GdkPixbuf* notification_pixbuf_get(NotificationPixbuf wanted)
{
if(!notification_pixbuf[wanted]) {
switch(wanted) {
case NOTIFICATION_CM_LOGO_64x64:
notification_pixbuf[wanted] =
gdk_pixbuf_new_from_inline(-1, raw_claws_mail_logo_64x64,
FALSE, NULL);
break;
case NOTIFICATION_TRAYICON_NEWMAIL:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMAIL, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_NEWMAIL_OFFLINE:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMAIL_OFFLINE, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_NEWMARKEDMAIL:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMARKEDMAIL, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_NEWMARKEDMAIL_OFFLINE:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMARKEDMAIL_OFFLINE, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_NOMAIL:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NOMAIL, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_NOMAIL_OFFLINE:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NOMAIL_OFFLINE, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_UNREADMAIL:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMAIL, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_UNREADMAIL_OFFLINE:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMAIL_OFFLINE, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_UNREADMARKEDMAIL:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMARKEDMAIL, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_TRAYICON_UNREADMARKEDMAIL_OFFLINE:
stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMARKEDMAIL_OFFLINE, &(notification_pixbuf[wanted]));
g_object_ref(notification_pixbuf[wanted]);
break;
case NOTIFICATION_PIXBUF_LAST:
break;
}
}
return notification_pixbuf[wanted];
}
void notification_pixbuf_free_all(void)
{
gint ii;
for(ii = NOTIFICATION_CM_LOGO_64x64; ii < NOTIFICATION_PIXBUF_LAST; ii++) {
if(notification_pixbuf[ii]) {
g_object_unref(notification_pixbuf[ii]);
notification_pixbuf[ii] = NULL;
}
}
}
--- NEW FILE: notification_hotkeys.c ---
/* Notification plugin for Claws-Mail
* Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_HOTKEYS
#include "notification_hotkeys.h"
#include "notification_prefs.h"
#include "notification_core.h"
#include "gtkhotkey.h"
#define HOTKEYS_APP_ID "claws-mail"
#define HOTKEY_KEY_ID_TOGGLED "toggle-mainwindow"
static GtkHotkeyInfo *hotkey_toggle_mainwindow = NULL;
static void hotkey_toggle_mainwindow_activated(GtkHotkeyInfo *hotkey, guint event_time, gpointer data)
{
g_return_if_fail(GTK_HOTKEY_IS_INFO(hotkey));
debug_print("Notification plugin: Toggled hide/show window due to hotkey %s activation\n", gtk_hotkey_info_get_signature(hotkey));
notification_toggle_hide_show_window();
}
static void unbind_toggle_mainwindow()
{
GError *error;
GtkHotkeyRegistry *registry;
/* clean up old hotkey */
if(hotkey_toggle_mainwindow) {
if(gtk_hotkey_info_is_bound(hotkey_toggle_mainwindow)) {
error = NULL;
gtk_hotkey_info_unbind(hotkey_toggle_mainwindow, &error);
if(error) {
debug_print("Notification plugin: Failed to unbind toggle hotkey\n");
g_error_free(error);
return;
}
}
g_object_unref(hotkey_toggle_mainwindow);
hotkey_toggle_mainwindow = NULL;
}
registry = gtk_hotkey_registry_get_default();
if(gtk_hotkey_registry_has_hotkey(registry, HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED)) {
error = NULL;
gtk_hotkey_registry_delete_hotkey(registry, HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED, &error);
if(error) {
debug_print("Notification plugin: Failed to unregister toggle hotkey: %s\n", error->message);
g_error_free(error);
return;
}
}
}
static void update_hotkey_binding_toggle_mainwindow()
{
GError *error;
/* don't do anything if no signature is given */
if(!notify_config.hotkeys_toggle_mainwindow || !strcmp(notify_config.hotkeys_toggle_mainwindow, ""))
return;
unbind_toggle_mainwindow();
/* (re)create hotkey info */
hotkey_toggle_mainwindow = gtk_hotkey_info_new(HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED, notify_config.hotkeys_toggle_mainwindow, NULL);
if(!hotkey_toggle_mainwindow) {
debug_print("Notification plugin: Failed to create toggle hotkey for '%s'\n", notify_config.hotkeys_toggle_mainwindow);
return;
}
/* try to register hotkey */
error = NULL;
gtk_hotkey_info_bind(hotkey_toggle_mainwindow, &error);
if(error) {
debug_print("Notification plugin: Failed to bind toggle hotkey to '%s': %s\n", notify_config.hotkeys_toggle_mainwindow, error->message);
g_error_free(error);
return;
}
g_signal_connect(hotkey_toggle_mainwindow, "activated", G_CALLBACK(hotkey_toggle_mainwindow_activated), NULL);
}
void notification_hotkeys_update_bindings()
{
debug_print("Notification plugin: Updating keybindings..\n");
if(notify_config.hotkeys_enabled) {
update_hotkey_binding_toggle_mainwindow();
}
else
notification_hotkeys_unbind_all();
}
void notification_hotkeys_unbind_all()
{
debug_print("Notification plugin: Unbinding all keybindings..\n");
unbind_toggle_mainwindow();
}
#endif /* NOTIFICATION_HOTKEYS */
--- NEW FILE: notification_pixbuf.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_PIXBUF_H
#define NOTIFICATION_PIXBUF_H NOTIFICATION_PIXBUF_H
#include <gdk/gdk.h>
/* By convention, the online icons for the trayicon are
* immediately followed by the offline icons, so one can do
* offline = array[online+1] */
typedef enum {
NOTIFICATION_CM_LOGO_64x64 = 0,
NOTIFICATION_TRAYICON_NEWMAIL,
NOTIFICATION_TRAYICON_NEWMAIL_OFFLINE,
NOTIFICATION_TRAYICON_NEWMARKEDMAIL,
NOTIFICATION_TRAYICON_NEWMARKEDMAIL_OFFLINE,
NOTIFICATION_TRAYICON_NOMAIL,
NOTIFICATION_TRAYICON_NOMAIL_OFFLINE,
NOTIFICATION_TRAYICON_UNREADMAIL,
NOTIFICATION_TRAYICON_UNREADMAIL_OFFLINE,
NOTIFICATION_TRAYICON_UNREADMARKEDMAIL,
NOTIFICATION_TRAYICON_UNREADMARKEDMAIL_OFFLINE,
NOTIFICATION_PIXBUF_LAST
} NotificationPixbuf;
/* The reference to the returned GdkPixbuf's belongs to notification_pixbuf,
* and shall not be removed outside. */
GdkPixbuf* notification_pixbuf_get(NotificationPixbuf);
void notification_pixbuf_free_all(void);
#endif /* NOTIFICATION_PIXBUF_H */
--- NEW FILE: notification_lcdproc.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_LCDPROC_H
#define NOTIFICATION_LCDPROC_H NOTIFICATION_LCDPROC_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_LCDPROC
#include <glib.h>
void notification_lcdproc_connect(void);
void notification_lcdproc_disconnect(void);
void notification_update_lcdproc(void);
#endif /* NOTIFICATION_LCDPROC */
#endif /* include guard */
--- NEW FILE: notification_lcdproc.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_LCDPROC
#include "notification_lcdproc.h"
#include "notification_prefs.h"
#include "notification_core.h"
#include "common/socket.h"
#include <string.h>
#include <sys/socket.h>
#define NOTIFICATION_LCDPROC_BUFFER_SIZE 8192
void notification_sock_puts(SockInfo*, gchar*);
void notification_lcdproc_send(gchar*);
static SockInfo *sock = NULL;
void notification_lcdproc_connect(void)
{
gint len, count;
gchar buf[NOTIFICATION_LCDPROC_BUFFER_SIZE];
if(!notify_config.lcdproc_enabled)
return;
if(sock)
notification_lcdproc_disconnect();
sock = sock_connect(notify_config.lcdproc_hostname,
notify_config.lcdproc_port);
/*
* Quietly return when a connection fails; next attempt
* will be made when some folder info has been changed.
*/
if(sock == NULL || sock->state == CONN_FAILED) {
debug_print("Could not connect to LCDd\n");
if(sock && sock->state == CONN_FAILED) {
sock_close(sock);
sock = NULL;
}
return;
}
else
debug_print("Connected to LCDd\n");
sock_set_nonblocking_mode(sock, TRUE);
/* Friendly people say "hello" first */
notification_sock_puts(sock, "hello");
/* FIXME: Ouch. Is this really the way to go? */
count = 50;
len = 0;
while((len <= 0) && (count-- >= 0)) {
g_usleep(125000);
len = sock_read(sock, buf, NOTIFICATION_LCDPROC_BUFFER_SIZE);
}
/*
* This might not be a LCDProc server.
* FIXME: check LCD size, LCDd version etc
*/
if (len <= 0) {
debug_print("Notification plugin: Can't communicate with "
"LCDd server! Are you sure that "
"there is a LCDd server running on %s:%d?\n",
notify_config.lcdproc_hostname, notify_config.lcdproc_port);
notification_lcdproc_disconnect();
return;
}
notification_lcdproc_send("client_set -name \"{Claws-Mail}\"");
notification_lcdproc_send("screen_add msg_counts");
notification_lcdproc_send("screen_set msg_counts -name {Claws-Mail Message Count}");
notification_lcdproc_send("widget_add msg_counts title title");
notification_lcdproc_send("widget_set msg_counts title {Claws-Mail}");
notification_lcdproc_send("widget_add msg_counts line1 string");
notification_lcdproc_send("widget_add msg_counts line2 string");
notification_lcdproc_send("widget_add msg_counts line3 string");
notification_update_msg_counts(NULL);
}
void notification_lcdproc_disconnect(void)
{
if(sock) {
#ifndef G_OS_WIN32
shutdown(sock->sock, SHUT_RDWR);
#endif
sock_close(sock);
sock = NULL;
}
}
void notification_update_lcdproc(void)
{
NotificationMsgCount count;
gchar *buf;
if(!notify_config.lcdproc_enabled || !sock)
return;
if(!sock || sock->state == CONN_FAILED) {
notification_lcdproc_connect();
return;
}
notification_core_get_msg_count(NULL, &count);
if((count.new_msgs + count.unread_msgs) > 0) {
buf =
g_strdup_printf("widget_set msg_counts line1 1 2 {%s: %d}",_("New"),
count.new_msgs);
notification_lcdproc_send(buf);
buf =
g_strdup_printf("widget_set msg_counts line2 1 3 {%s: %d}",_("Unread"),
count.unread_msgs);
notification_lcdproc_send(buf);
buf =
g_strdup_printf("widget_set msg_counts line3 1 4 {%s: %d}",_("Total"),
count.total_msgs);
notification_lcdproc_send(buf);
}
else {
buf = g_strdup_printf("widget_set msg_counts line1 1 2 {%s}",
_("No new messages"));
notification_lcdproc_send(buf);
buf = g_strdup_printf("widget_set msg_counts line2 1 3 {}");
notification_lcdproc_send(buf);
buf = g_strdup_printf("widget_set msg_counts line3 1 4 {}");
notification_lcdproc_send(buf);
}
g_free(buf);
}
void notification_sock_puts(SockInfo *socket, gchar *string)
{
sock_write(socket, string, strlen(string));
sock_write(socket, "\n", 1);
}
void notification_lcdproc_send(gchar *string)
{
notification_sock_puts(sock, string);
/* TODO: Check return message from LCDd */
}
#endif /* NOTIFICATION_LCDPROC */
--- NEW FILE: notification_popup.c ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "pluginconfig.h"
#ifdef NOTIFICATION_POPUP
#include <gtk/gtk.h>
#include "mainwindow.h"
#include "procmsg.h"
#include "folder.h"
#ifndef USE_NEW_ADDRBOOK
#include "addrindex.h"
#endif
#include "common/utils.h"
#include "gtk/gtkutils.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 <libnotify/notify.h>
#endif
#ifndef NOTIFY_CHECK_VERSION
# define NOTIFY_CHECK_VERSION(a,b,c) 0
#endif
typedef struct {
gint count;
gchar *msg_path;
#ifdef HAVE_LIBNOTIFY
NotifyNotification *notification;
GError *error;
#else /* !HAVE_LIBNOTIFY */
guint timeout_id;
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];
static void popup_timeout_fun(NotifyNotification*, gpointer data);
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
static NotificationPopup popup;
static gboolean popup_timeout_fun(gpointer data);
static gboolean notification_popup_add_msg(MsgInfo*);
static gboolean notification_popup_create(MsgInfo*);
static gboolean notification_popup_button(GtkWidget*, GdkEventButton*, gpointer);
#endif
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);
/* 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));
}
#endif /* !HAVE_LIBNOTIFY */
G_UNLOCK(popup);
#ifndef HAVE_LIBNOTIFY
/* GUI update */
while(gtk_events_pending())
gtk_main_iteration();
#endif /* !HAVE_LIBNOTIFY */
}
#ifdef HAVE_LIBNOTIFY
static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
{
NotificationPopup *ppopup;
NotificationFolderType nftype;
nftype = GPOINTER_TO_INT(data);
G_LOCK(popup);
ppopup = &(popup[nftype]);
g_object_unref(G_OBJECT(ppopup->notification));
ppopup->notification = NULL;
g_clear_error(&(ppopup->error));
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");
}
#else
static gboolean popup_timeout_fun(gpointer data)
{
NotificationPopup *ppopup;
NotificationFolderType nftype;
nftype = GPOINTER_TO_INT(data);
G_LOCK(popup);
ppopup = &popup;
if(ppopup->window) {
gtk_widget_destroy(ppopup->window);
ppopup->window = NULL;
}
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;
}
#endif
#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 */
notification_show_mainwindow(mainwin);
/* 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;
gchar *foldname = NULL;
GList *caps = NULL;
gboolean support_actions = FALSE;
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)"));
if (notify_config.popup_display_folder_name) {
foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
}
else
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);
if(foldname) g_free(foldname);
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
#if !NOTIFY_CHECK_VERSION(0, 7, 0)
, NULL
#endif
);
g_free(utf8_str);
if(ppopup->notification == NULL) {
debug_print("Notification Plugin: Failed to create a new "
"notification.\n");
return FALSE;
}
caps = notify_get_server_caps();
if(caps != NULL) {
GList *c;
for(c = caps; c != NULL; c = c->next) {
if(strcmp((char*)c->data, "actions") == 0 ) {
support_actions = TRUE;
break;
}
}
g_list_foreach(caps, (GFunc)g_free, NULL);
g_list_free(caps);
}
/* Default action */
if (support_actions)
notify_notification_add_action(ppopup->notification,
"default", "Present main window",
(NotifyActionCallback)default_action_cb,
GINT_TO_POINTER(nftype),
notification_libnotify_free_func);
/* Icon */
pixbuf = NULL;
#ifndef USE_NEW_ADDRBOOK
if(msginfo && msginfo->from) {
gchar *icon_path;
icon_path = addrindex_get_picture_file(msginfo->from);
if(is_file_exist(icon_path)) {
GError *error = NULL;
gint w, h;
gdk_pixbuf_get_file_info(icon_path, &w, &h);
if((w > 64) || (h > 64))
pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
64, 64, TRUE, &error);
else
pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
if(!pixbuf) {
debug_print("Could not load picture file: %s\n",
error ? error->message : "no details");
g_error_free(error);
}
}
else
debug_print("Picture path does not exist: %s\n",icon_path);
g_free(icon_path);
}
#endif
if(!pixbuf)
pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
if(pixbuf) {
notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
g_object_unref(pixbuf);
}
else /* This is not fatal */
debug_print("Notification plugin: Icon could not be loaded.\n");
/* timeout */
notify_notification_set_timeout(ppopup->notification, notify_config.popup_timeout);
/* Category */
notify_notification_set_category(ppopup->notification, "email.arrived");
/* get notified on bubble close */
g_signal_connect(G_OBJECT(popup->notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
/* Show the popup */
notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
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;
GdkPixbuf *pixbuf;
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;
}
/* make sure we show a logo on many msg arrival */
pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
if(pixbuf)
notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
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 */
notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
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;
notification_show_mainwindow(mainwin);
/* 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 */
--- NEW FILE: notification_popup.h ---
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NOTIFICATION_POPUP_H
#define NOTIFICATION_POPUP_H NOTIFICATION_POPUP_H
#include "pluginconfig.h"
#ifdef NOTIFICATION_POPUP
#include "procmsg.h"
#define POPUP_SPECIFIC_FOLDER_ID_STR "popup"
void notification_popup_msg(MsgInfo*);
#endif /* NOTIFICATION_POPUP */
#endif /* NOTIFICATION_POPUP_H */
More information about the Commits
mailing list