aboutsummaryrefslogblamecommitdiffstats
path: root/plugins/mail-notification/mail-notification.c
blob: 72e3881b23d0ed4a77e78c7932ae822b2fc872fd (plain) (tree)
1
2
3
4
5
6
7
8
9
10
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  
  



                                                                  











                               

                    
                         
      
 

                                    
 

                 








                                
 
                     
                             
      
 
                                                                                    
                                                                          


                                                                       

                                
                                        
                                                
 







                                                                
                                   








                                                                               
 


                                                                          
 

                                        
 
                                            


                            
 





                                                                      

         
                                
 
                   

 











                                                                          
                                                                                                                                            







                                                                                   
                                                                                       

                  
                                                                            
                                                  
                                                                                                  
                                                             

         












                                                                                                         








                                                  
 







                                                         
                                                                                      




































                                                                                     





                                                                          
                                                                                                         





                                          
                                                                                           


           
                         








                                            
 







                                                                            
                                  
                              
 
                     
                                         
      

           
                          
 
                     



                                                         
      
 
                                                         
                                     
 




                                                   

                           

 






































                                                                          
                     
               

                                       
                                                          


      










                                                                  
                                                                          
 
           


                                                      
                    

























                                                                                                            





                                                                  




                                                                            


                                                         











                                                                                                     
                                                            




                                                        
 
                                              
                                                            
                               

                                                                               
                                                            




                                                  





                                                                             
                     
           


                                                                      
 








                                                         
                         

                                       
















                                                                           
                                                                                











                                                                
      
 
           

                                          
                              
                                         
 
                       
                                                     
                                                                                
         
 



                                                            
                            










                                                                         
                                      

                                                                                                           

                                                                                             



























                                                                                                                           

                                       

                                                                                                                    
         
 
                                                            





                                                                                      

                                                        
                     

                                                                    
                                                                  
                             
                                                                                                     



                                                                         
                                                                                                          

                                                                                        





                                                                                                                               
                         
                 
                                 
         
      


                     
                       


                                                                    
 


                                                              
         







                                            
                               

 


                               





                             
 
                                     
 

                                       
 
                         
 



                                                                          
 


                                                 
 
                        
 





                                                                          

                                     





                                                              
 




















                                                                          












                                                                          
                                                                        

                                                                        
                    
                                           
      

           


                                                                    
                    
                                                






                                                             
      
         
            
                           
 
 



                             
                             
                        


                               
 
           



                                                    
 





                                                                                       
 
                                














                                                                                    
                                                                                   
                                                                                        
                             


















                                                                              
 


                                                                   
















                                                                          
                                                     














                                                                                                          
                          
 
                    
                     
                                                     




                                                  
         
            
                                                     
      




                              



                             
                    
                            
                            
                             
                                           


                                     
 

                                                                 

                                       
 
                         
 

















                                                                          

                                     




















                                                                          
 



                                                                          
 


                                                  
 




































                                                                          









                                                                                          





                                                     



                                                                          






                                                                          


                     



                             
 
                                     
 












                                                                          
 







                                                                          
 

                                                                          
 

                                                                          
 
                         

 


                                                                       
                                                    

                                                            











                                                                                                   
 





                                                       


                                       











                                                                 
 

                                                       
 




                                                      
 
    
                                              
 


                                                             
 


                                                              
                               

                                     

                                      
                                
         
 


                 


                                                
                                 
 
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *      Miguel Angel Lopez Hernandez <miguel@gulev.org.mx>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>

#ifdef HAVE_CANBERRA
#include <canberra-gtk.h>
#endif

#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

#include <time.h>

#include <camel/camel-folder.h>

#include <e-util/e-binding.h>
#include <e-util/e-config.h>
#include <e-util/gconf-bridge.h>
#include <mail/em-utils.h>
#include <mail/em-event.h>
#include <mail/em-folder-tree.h>
#include <shell/e-shell-view.h>

#ifdef HAVE_LIBNOTIFY
#include <libnotify/notify.h>
#endif

#define GCONF_KEY_ROOT          "/apps/evolution/eplugin/mail-notification/"
#define GCONF_KEY_NOTIFY_ONLY_INBOX GCONF_KEY_ROOT "notify-only-inbox"
#define GCONF_KEY_ENABLED_DBUS      GCONF_KEY_ROOT "dbus-enabled"
#define GCONF_KEY_ENABLED_STATUS    GCONF_KEY_ROOT "status-enabled"
#define GCONF_KEY_ENABLED_SOUND     GCONF_KEY_ROOT "sound-enabled"

static gboolean enabled = FALSE;
static GtkWidget *get_cfg_widget (void);
static GStaticMutex mlock = G_STATIC_MUTEX_INIT;

/**
 * each part should "implement" its own "public" functions:
 * a) void new_notify_... (EMEventTargetFolder *t)
 *    when new_notify message is sent by Evolution
 *
 * b) void read_notify_... (EMEventTargetMessage *t)
 *    it is called when read_notify message is sent by Evolution
 *
 * c) void enable_... (gint enable)
 *    when plugin itself or the part is enabled/disabled
 *
 * d) GtkWidget *get_config_widget_...(void)
 *    to obtain config widget for the particular part
 *
 * It also should have its own gconf key for enabled state. In each particular
 * function it should do its work as expected. enable_... will be called always
 * when disabling plugin, but only when enabling/disabling part itself.
 **/

/* -------------------------------------------------------------------  */
/*                       Helper functions                               */
/* -------------------------------------------------------------------  */

static gboolean
is_part_enabled (const gchar *gconf_key)
{
    /* the part is enabled by default */
    gboolean res = TRUE;
    GConfClient *client;
    GConfValue  *is_key;

    client = gconf_client_get_default ();

    is_key = gconf_client_get (client, gconf_key, NULL);
    if (is_key) {
        res = gconf_client_get_bool (client, gconf_key, NULL);
        gconf_value_free (is_key);
    }

    g_object_unref (client);

    return res;
}

/* -------------------------------------------------------------------  */
/*                           DBUS part                                  */
/* -------------------------------------------------------------------  */

#define DBUS_PATH       "/org/gnome/evolution/mail/newmail"
#define DBUS_INTERFACE      "org.gnome.evolution.mail.dbus.Signal"

static DBusConnection *bus = NULL;

static gboolean init_dbus (void);

static void
send_dbus_message (const gchar *name, const gchar *data, guint new, const gchar *msg_uid, const gchar *msg_sender, const gchar *msg_subject)
{
    DBusMessage *message;

    /* Create a new message on the DBUS_INTERFACE */
    if (!(message = dbus_message_new_signal (DBUS_PATH, DBUS_INTERFACE, name)))
        return;

    /* Appends the data as an argument to the message */
    dbus_message_append_args (message, DBUS_TYPE_STRING, &data, DBUS_TYPE_INVALID);

    if (new) {
        gchar * display_name = em_utils_folder_name_from_uri (data);
        dbus_message_append_args (message,
                      DBUS_TYPE_STRING, &display_name, DBUS_TYPE_UINT32, &new,
                      DBUS_TYPE_INVALID);
    }

    #define add_named_param(name, value)    \
        if (value) {    \
            gchar *val; \
            val = g_strconcat (name, ":", value, NULL); \
            dbus_message_append_args (message, DBUS_TYPE_STRING, &val, DBUS_TYPE_INVALID);  \
        }

    add_named_param ("msg_uid", msg_uid);
    add_named_param ("msg_sender", msg_sender);
    add_named_param ("msg_subject", msg_subject);

    #undef add_named_param

    /* Sends the message */
    dbus_connection_send (bus, message, NULL);

    /* Frees the message */
    dbus_message_unref (message);
}

static gboolean
reinit_dbus (gpointer user_data)
{
    if (!enabled || init_dbus ())
        return FALSE;

    /* keep trying to re-establish dbus connection */
    return TRUE;
}

static DBusHandlerResult
filter_function (DBusConnection *connection, DBusMessage *message, gpointer user_data)
{
    if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
        strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
        dbus_connection_unref (bus);
        bus = NULL;

        g_timeout_add (3000, reinit_dbus, NULL);

        return DBUS_HANDLER_RESULT_HANDLED;
    }

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static gboolean
init_dbus (void)
{
    DBusError error;

    if (bus != NULL)
        return TRUE;

    dbus_error_init (&error);
    if (!(bus = dbus_bus_get (DBUS_BUS_SESSION, &error))) {
        g_warning ("could not get system bus: %s\n", error.message);
        dbus_error_free (&error);
        return FALSE;
    }

    dbus_connection_setup_with_g_main (bus, NULL);
    dbus_connection_set_exit_on_disconnect (bus, FALSE);

    dbus_connection_add_filter (bus, filter_function, NULL, NULL);

    return TRUE;
}

/* -------------------------------------------------------------------  */

static void
new_notify_dbus (EMEventTargetFolder *t)
{
    if (bus != NULL)
        send_dbus_message ("Newmail", t->uri, t->new, t->msg_uid, t->msg_sender, t->msg_subject);
}

static void
read_notify_dbus (EMEventTargetMessage *t)
{
    if (bus != NULL)
        send_dbus_message ("MessageReading", t->folder->name, 0, NULL, NULL, NULL);
}

static void
enable_dbus (gint enable)
{
    if (enable) {
        /* we ignore errors here */
        init_dbus ();
    } else if (bus != NULL) {
        dbus_connection_unref (bus);
        bus = NULL;
    }
}

/* -------------------------------------------------------------------  */
/*                     Notification area part                           */
/* -------------------------------------------------------------------  */

#define GCONF_KEY_STATUS_BLINK      GCONF_KEY_ROOT "status-blink-icon"
#define GCONF_KEY_STATUS_NOTIFICATION   GCONF_KEY_ROOT "status-notification"

static GtkStatusIcon *status_icon = NULL;
static guint blink_timeout_id = 0;
static guint status_count = 0;

#ifdef HAVE_LIBNOTIFY
static NotifyNotification *notify = NULL;
#endif

static void
remove_notification (void)
{
#ifdef HAVE_LIBNOTIFY
    if (notify)
        notify_notification_close (notify, NULL);

    notify = NULL;
#endif

    gtk_status_icon_set_visible (status_icon, FALSE);
    g_object_unref (status_icon);

    if (blink_timeout_id) {
        g_source_remove (blink_timeout_id);
        blink_timeout_id = 0;
    }

    status_icon = NULL;
    status_count = 0;
}

static void
status_icon_activate_cb (void)
{
    EShell *shell;
    EShellView *shell_view;
    EShellWindow *shell_window;
    EShellSidebar *shell_sidebar;
    EMFolderTree *folder_tree;
    GtkAction *action;
    GList *list;
    const gchar *uri;

    shell = e_shell_get_default ();
    list = e_shell_get_watched_windows (shell);

    /* Find the first EShellWindow in the list. */
    while (list != NULL && !E_IS_SHELL_WINDOW (list->data))
        list = g_list_next (list);

    g_return_if_fail (list != NULL);

    /* Present the shell window. */
    shell_window = E_SHELL_WINDOW (list->data);
    gtk_window_present (GTK_WINDOW (shell_window));

    /* Switch to the mail view. */
    shell_view = e_shell_window_get_shell_view (shell_window, "mail");
    action = e_shell_view_get_action (shell_view);
    gtk_action_activate (action);

    /* Select the latest folder with new mail. */
    uri = g_object_get_data (G_OBJECT (status_icon), "uri");
    shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
    g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
    em_folder_tree_set_selected (folder_tree, uri, FALSE);

    remove_notification ();
}

#ifdef HAVE_LIBNOTIFY
static gboolean
notification_callback (gpointer notify)
{
    return (!notify_notification_show (notify, NULL));
}
#endif

static gboolean
stop_blinking_cb (gpointer data)
{
    blink_timeout_id = 0;

    if (status_icon)
        gtk_status_icon_set_blinking (status_icon, FALSE);

    return FALSE;
}

/* -------------------------------------------------------------------  */

static void
do_properties (GtkMenuItem *item, gpointer user_data)
{
    GtkWidget *cfg, *dialog, *vbox, *label, *hbox;
    gchar *text;

    cfg = get_cfg_widget ();
    if (!cfg)
        return;

    text = g_strconcat ("<span size=\"x-large\">", _("Evolution's Mail Notification"), "</span>", NULL);

    vbox = gtk_vbox_new (FALSE, 10);
    label = gtk_label_new (NULL);
    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
    gtk_label_set_markup (GTK_LABEL (label), text);
    g_free (text);

    gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
    gtk_widget_show (label);
    gtk_widget_show (vbox);

    hbox = gtk_hbox_new (FALSE, 10);
    label = gtk_label_new ("   ");
    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    gtk_widget_show_all (hbox);

    gtk_box_pack_start (GTK_BOX (hbox), cfg, TRUE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);

    dialog = gtk_dialog_new_with_buttons (
        _("Mail Notification Properties"),
        NULL,
        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
        NULL);

    gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
    gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
    gtk_widget_set_size_request (dialog, 400, -1);
    g_signal_connect_swapped (
        dialog, "response",
        G_CALLBACK (gtk_widget_destroy), dialog);
    gtk_widget_show (dialog);
}

static void
popup_menu_status (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
{
    GtkMenu *menu;
    GtkWidget *item;

    menu = GTK_MENU (gtk_menu_new ());

    item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE, NULL);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
    gtk_widget_show (item);

    g_signal_connect (
        item, "activate",
        G_CALLBACK (remove_notification), NULL);

    item = gtk_separator_menu_item_new ();
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
    gtk_widget_show (item);

    item = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
    gtk_widget_show (item);

    g_signal_connect (
        item, "activate",
        G_CALLBACK (do_properties), NULL);

    g_object_ref_sink (menu);
    gtk_menu_popup (menu, NULL, NULL, NULL, NULL, button, activate_time);
    g_object_unref (menu);
}

#ifdef HAVE_LIBNOTIFY
static void
notifyActionCallback (NotifyNotification *n, gchar *label, gpointer a)
{
    g_static_mutex_lock (&mlock);

    gtk_status_icon_set_visible (status_icon, FALSE);
    g_object_unref (status_icon);

    if (blink_timeout_id) {
        g_source_remove (blink_timeout_id);
        blink_timeout_id = 0;
    }

    status_icon = NULL;
    status_count = 0;
    g_static_mutex_unlock (&mlock);
}

/* Function to check if actions are supported by the notification daemon */
static gboolean
can_support_actions (void)
{
    static gboolean supports_actions = FALSE;
    static gboolean have_checked = FALSE;

    if (!have_checked) {
        GList *caps = NULL;
        GList *c;

        have_checked = TRUE;

        caps = notify_get_server_caps ();
        if (caps != NULL) {
            for (c = caps; c != NULL; c = c->next) {
                if (strcmp ((gchar *)c->data, "actions") == 0) {
                    supports_actions = TRUE;
                    break;
                }
            }
        }

        g_list_foreach (caps, (GFunc)g_free, NULL);
        g_list_free (caps);
    }

    return supports_actions;
}
#endif

static void
new_notify_status (EMEventTargetFolder *t)
{
    gchar *msg, *safetext;
    gboolean new_icon = !status_icon;

    if (new_icon) {
        status_icon = gtk_status_icon_new ();
        gtk_status_icon_set_from_icon_name (status_icon, "mail-unread");
    }

    g_object_set_data_full (
        G_OBJECT (status_icon), "uri",
        g_strdup (t->uri), (GDestroyNotify) g_free);

    if (!status_count) {
        EAccount *account;
        gchar *name = t->name;

        account = mail_config_get_account_by_source_url (t->uri);

        if (account != NULL) {
            name = g_strdup_printf (
                "%s/%s", e_account_get_string (
                account, E_ACCOUNT_NAME), name);
        }

        status_count = t->new;

        /* To translators: '%d' is the count of mails received and '%s' is the name of the folder*/
        msg = g_strdup_printf (ngettext ("You have received %d new message\nin %s.",
                         "You have received %d new messages\nin %s.",
                         status_count), status_count, name);

        if (name != t->name)
            g_free (name);

        if (t->msg_sender) {
            gchar *tmp, *str;

            /* To Translators: "From:" is preceding a new mail sender address, like "From: user@example.com" */
            str = g_strdup_printf (_("From: %s"), t->msg_sender);
            tmp = g_strconcat (msg, "\n", str, NULL);

            g_free (msg);
            g_free (str);
            msg = tmp;
        }

        if (t->msg_subject) {
            gchar *tmp, *str;

            /* To Translators: "Subject:" is preceding a new mail subject, like "Subject: It happened again" */
            str = g_strdup_printf (_("Subject: %s"), t->msg_subject);
            tmp = g_strconcat (msg, "\n", str, NULL);

            g_free (msg);
            g_free (str);
            msg = tmp;
        }
    } else {
        status_count += t->new;
        msg = g_strdup_printf (ngettext ("You have received %d new message.",
                         "You have received %d new messages.", status_count), status_count);
    }

    gtk_status_icon_set_tooltip_text (status_icon, msg);

    if (new_icon && is_part_enabled (GCONF_KEY_STATUS_BLINK)) {
        gtk_status_icon_set_blinking (status_icon, TRUE);
        blink_timeout_id = g_timeout_add_seconds (15, stop_blinking_cb, NULL);
    }

    gtk_status_icon_set_visible (status_icon, TRUE);

#ifdef HAVE_LIBNOTIFY
    /* Now check whether we're supposed to send notifications */
    if (is_part_enabled (GCONF_KEY_STATUS_NOTIFICATION)) {
        safetext = g_markup_escape_text(msg, strlen(msg));
        if (notify) {
            notify_notification_update (notify, _("New email"), safetext, "mail-unread");
        } else {
            if (!notify_init ("evolution-mail-notification"))
                fprintf (stderr,"notify init error");

            notify  = notify_notification_new (_("New email"), safetext, "mail-unread", NULL);
            notify_notification_attach_to_status_icon (notify, status_icon);

            /* Check if actions are supported */
            if (can_support_actions ()) {
                notify_notification_set_urgency (notify, NOTIFY_URGENCY_NORMAL);
                notify_notification_set_timeout (notify, NOTIFY_EXPIRES_DEFAULT);
                notify_notification_add_action(notify, "default", "Default", notifyActionCallback, NULL, NULL);
                g_timeout_add (500, notification_callback, notify);
            }
        }
        g_free(safetext);
    }
#endif

    g_free (msg);

    if (new_icon) {
        g_signal_connect (
            status_icon, "activate",
            G_CALLBACK (status_icon_activate_cb), NULL);

        g_signal_connect (
            status_icon, "popup-menu",
            G_CALLBACK (popup_menu_status), NULL);
    }
}

static void
read_notify_status (EMEventTargetMessage *t)
{
    if (!status_icon)
        return;

    remove_notification ();
}

static GtkWidget *
get_config_widget_status (void)
{
    GtkWidget *vbox;
    GtkWidget *master;
    GtkWidget *container;
    GtkWidget *widget;
    GConfBridge *bridge;
    const gchar *text;

    bridge = gconf_bridge_get ();

    vbox = gtk_vbox_new (FALSE, 6);
    gtk_widget_show (vbox);

    container = vbox;

    text = _("Show icon in _notification area");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_ENABLED_STATUS,
        G_OBJECT (widget), "active");

    master = widget;

    widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
    gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    e_binding_new (
        master, "active",
        widget, "sensitive");

    container = widget;

    widget = gtk_vbox_new (FALSE, 6);
    gtk_container_add (GTK_CONTAINER (container), widget);
    gtk_widget_show (widget);

    container = widget;

    text = _("B_link icon in notification area");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_STATUS_BLINK,
        G_OBJECT (widget), "active");

#ifdef HAVE_LIBNOTIFY
    text = _("Popup _message together with the icon");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_STATUS_NOTIFICATION,
        G_OBJECT (widget), "active");
#endif

    return vbox;
}

/* -------------------------------------------------------------------  */
/*                         Sound part                                   */
/* -------------------------------------------------------------------  */

/* min no. seconds between newmail notifications */
#define NOTIFY_THROTTLE 30

#define GCONF_KEY_SOUND_BEEP        GCONF_KEY_ROOT "sound-beep"
#define GCONF_KEY_SOUND_FILE        GCONF_KEY_ROOT "sound-file"
#define GCONF_KEY_SOUND_PLAY_FILE   GCONF_KEY_ROOT "sound-play-file"
#define GCONF_KEY_SOUND_USE_THEME       GCONF_KEY_ROOT "sound-use-theme"

#ifdef HAVE_CANBERRA
static ca_context *mailnotification = NULL;
#endif

static void
do_play_sound (gboolean beep, gboolean use_theme, const gchar *file)
{
    if (!beep) {
#ifdef HAVE_CANBERRA
        if (!use_theme && file && *file)
            ca_context_play(mailnotification, 0,
            CA_PROP_MEDIA_FILENAME, file,
            NULL);
        else
            ca_context_play(mailnotification, 0,
            CA_PROP_EVENT_ID,"message-new-email",
            NULL);
#endif
    }
    else
        gdk_beep();
}

struct _SoundConfigureWidgets
{
    GtkWidget *enable;
    GtkWidget *beep;
    GtkWidget *use_theme;
    GtkWidget *file;
    GtkWidget *filechooser;
    GtkWidget *play;
};

static void
sound_file_set_cb (GtkWidget *widget, gpointer data)
{
    gchar *file;
    GConfClient *client;

    g_return_if_fail (widget != NULL);

    client = gconf_client_get_default ();
    file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));

    gconf_client_set_string (client, GCONF_KEY_SOUND_FILE, file ? file : "", NULL);

    g_object_unref (client);
    g_free (file);
}

static void
sound_play_cb (GtkWidget *widget, gpointer data)
{
    struct _SoundConfigureWidgets *scw = (struct _SoundConfigureWidgets *) data;
    gchar *file;

    g_return_if_fail (data != NULL);

    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scw->enable)))
        return;

    file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (scw->filechooser));
    do_play_sound (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (scw->beep)),
               gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (scw->use_theme)),
               file);
    g_free (file);
}

struct _SoundNotifyData {
    time_t last_notify;
    guint notify_idle_id;
};

static gboolean
sound_notify_idle_cb (gpointer user_data)
{
    gchar *file;
    GConfClient *client;
    struct _SoundNotifyData *data = (struct _SoundNotifyData *) user_data;

    g_return_val_if_fail (data != NULL, FALSE);

    client = gconf_client_get_default ();
    file = gconf_client_get_string (client, GCONF_KEY_SOUND_FILE, NULL);

    do_play_sound (is_part_enabled (GCONF_KEY_SOUND_BEEP),
               is_part_enabled (GCONF_KEY_SOUND_USE_THEME),
               file);

    g_object_unref (client);
    g_free (file);

    time (&data->last_notify);

    data->notify_idle_id = 0;

    return FALSE;
}

/* -------------------------------------------------------------------  */

static void
new_notify_sound (EMEventTargetFolder *t)
{
    time_t last_newmail;
    static struct _SoundNotifyData data = {0, 0};

    time (&last_newmail);

    /* just put it to the idle queue */
    if (data.notify_idle_id == 0 && (last_newmail - data.last_notify >= NOTIFY_THROTTLE))
        data.notify_idle_id = g_idle_add_full (G_PRIORITY_LOW, sound_notify_idle_cb, &data, NULL);
}

static void
read_notify_sound (EMEventTargetMessage *t)
{
    /* we do nothing here */
}

static void
enable_sound (gint enable)
{
#ifdef HAVE_CANBERRA
    if (enable) {
        ca_context_create(&mailnotification);
        ca_context_change_props(
            mailnotification,
            CA_PROP_APPLICATION_NAME,
            "mailnotification Plugin",
            NULL);
    }
    else
        ca_context_destroy(mailnotification);
#endif
}

static GtkWidget *
get_config_widget_sound (void)
{
    GtkWidget *vbox;
    GtkWidget *container;
    GtkWidget *master;
    GtkWidget *widget;
    gchar *file;
    GConfBridge *bridge;
    GConfClient *client;
    GSList *group = NULL;
    struct _SoundConfigureWidgets *scw;
    const gchar *text;

    bridge = gconf_bridge_get ();

    scw = g_malloc0 (sizeof (struct _SoundConfigureWidgets));

    vbox = gtk_vbox_new (FALSE, 6);
    gtk_widget_show (vbox);

    container = vbox;

    text = _("_Play sound when new messages arrive");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_ENABLED_SOUND,
        G_OBJECT (widget), "active");

    master = widget;
    scw->enable = widget;

    widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
    gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    e_binding_new (
        master, "active",
        widget, "sensitive");

    container = widget;

    widget = gtk_vbox_new (FALSE, 6);
    gtk_container_add (GTK_CONTAINER (container), widget);
    gtk_widget_show (widget);

    container = widget;

    text = _("_Beep");
    widget = gtk_radio_button_new_with_mnemonic (group, text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_SOUND_BEEP,
        G_OBJECT (widget), "active");

    scw->beep = widget;

    group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));

    text = _("Use sound _theme");
    widget = gtk_radio_button_new_with_mnemonic (group, text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_SOUND_USE_THEME,
        G_OBJECT (widget), "active");

    scw->use_theme = widget;

    group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget));

    widget = gtk_hbox_new (FALSE, 6);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    container = widget;

    text = _("Play _file:");
    widget = gtk_radio_button_new_with_mnemonic (group, text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_SOUND_PLAY_FILE,
        G_OBJECT (widget), "active");

    scw->file = widget;

    text = _("Select sound file");
    widget = gtk_file_chooser_button_new (
        text, GTK_FILE_CHOOSER_ACTION_OPEN);
    gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
    gtk_widget_show (widget);

    scw->filechooser = widget;

    widget = gtk_button_new ();
    gtk_button_set_image (
        GTK_BUTTON (widget), gtk_image_new_from_icon_name (
        "media-playback-start", GTK_ICON_SIZE_BUTTON));
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    scw->play = widget;

    client = gconf_client_get_default ();
    file = gconf_client_get_string (client, GCONF_KEY_SOUND_FILE, NULL);

    if (file && *file)
        gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (scw->filechooser), file);

    g_object_unref (client);
    g_free (file);

    g_signal_connect (
        scw->filechooser, "file-set",
        G_CALLBACK (sound_file_set_cb), scw);
    g_signal_connect (
        scw->play, "clicked",
        G_CALLBACK (sound_play_cb), scw);

    /* to let structure free properly */
    g_object_set_data_full (G_OBJECT (vbox), "scw-data", scw, g_free);

    return vbox;
}

/* -------------------------------------------------------------------  */
/*                     Plugin itself part                               */
/* -------------------------------------------------------------------  */

static GtkWidget *
get_cfg_widget (void)
{
    GtkWidget *container;
    GtkWidget *widget;
    GConfBridge *bridge;
    const gchar *text;

    bridge = gconf_bridge_get ();

    widget = gtk_vbox_new (FALSE, 12);
    gtk_widget_show (widget);

    container = widget;

    text = _("Notify new messages for _Inbox only");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_NOTIFY_ONLY_INBOX,
        G_OBJECT (widget), "active");

    text = _("Generate a _D-Bus message");
    widget = gtk_check_button_new_with_mnemonic (text);
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
    gtk_widget_show (widget);

    gconf_bridge_bind_property (
        bridge, GCONF_KEY_ENABLED_DBUS,
        G_OBJECT (widget), "active");

    widget = get_config_widget_status ();
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);

    widget = get_config_widget_sound ();
    gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);

    return container;
}

void org_gnome_mail_new_notify (EPlugin *ep, EMEventTargetFolder *t);
void org_gnome_mail_read_notify (EPlugin *ep, EMEventTargetMessage *t);

gint e_plugin_lib_enable (EPlugin *ep, gint enable);
GtkWidget *e_plugin_lib_get_configure_widget (EPlugin *epl);

void
org_gnome_mail_new_notify (EPlugin *ep, EMEventTargetFolder *t)
{
    g_return_if_fail (t != NULL);

    if (!enabled || !t->new || (!t->is_inbox && is_part_enabled (GCONF_KEY_NOTIFY_ONLY_INBOX)))
        return;

    g_static_mutex_lock (&mlock);

    if (is_part_enabled (GCONF_KEY_ENABLED_DBUS))
        new_notify_dbus (t);

    if (is_part_enabled (GCONF_KEY_ENABLED_STATUS))
        new_notify_status (t);

    if (is_part_enabled (GCONF_KEY_ENABLED_SOUND))
        new_notify_sound (t);

    g_static_mutex_unlock (&mlock);
}

void
org_gnome_mail_read_notify (EPlugin *ep, EMEventTargetMessage *t)
{
    g_return_if_fail (t != NULL);

    if (!enabled)
        return;

    g_static_mutex_lock (&mlock);

    if (is_part_enabled (GCONF_KEY_ENABLED_DBUS))
        read_notify_dbus (t);

    if (is_part_enabled (GCONF_KEY_ENABLED_STATUS))
        read_notify_status (t);

    if (is_part_enabled (GCONF_KEY_ENABLED_SOUND))
        read_notify_sound (t);

    g_static_mutex_unlock (&mlock);
}

gint
e_plugin_lib_enable (EPlugin *ep, gint enable)
{
    if (enable) {
        if (is_part_enabled (GCONF_KEY_ENABLED_DBUS))
            enable_dbus (enable);

        if (is_part_enabled (GCONF_KEY_ENABLED_SOUND))
            enable_sound (enable);

        enabled = TRUE;
    } else {
        enable_dbus (enable);
        enable_sound (enable);

        enabled = FALSE;
    }

    return 0;
}

GtkWidget *
e_plugin_lib_get_configure_widget (EPlugin *epl)
{
    return get_cfg_widget ();
}