aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gui/gnome-cal.c
blob: 763fd5eac4ccd2c7c3068545e8e6b87e0725ae66 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                           





                                                  
 
                   

                   
                     
                  


                          

                            

                                  

                                        
                  


                        
                      
                      
                              

 
 


                                                             








                                                                     
 

 








                                                   
                                                                     
                                                 


                                             
                  

                                                                                                  
                  


                                                                             



                                   


























                                                                      


                                   






















































































                                                                                                   
                                                                  
                                             
                                                                                 

                                                              

                                                              
                                                                         


                                                                           

 


                                      






                                                                          

 


                                                          





                                                                   


                                   

                                              

                                         

                                          
                                            




                                  


                                                          

                                                    
                                          
 



























                                                                                



                                                  



                                                             

                                                


                                                  
 
                                 


                                                                   
                                       
                                                                   
                                        
                                                                    
                                          
                                                                   
              
                                                               
                                        
                             

         





                                             


                                                    
                                           
 
 


                                             


                                                    
                                            

 


                                                         


                                                    
                                                  


                                         
    

                                               

                                                    
 


                                                


                                                                              
    

                                                              

                                         
 


                                                    
 


















                                                                   
 







                                                                              

 
 


                                                                             







                                                                  
                                                                     


                                                                  



           


                                                        
 












                                                                



                                                           




                                
 
                                                           
 
                                       



                                                                            
                                         
 
                             
 

                                                  
                                                                     
                                                                    
                                                                     
                                                                    
 
                      

 








                                   

                                              

                                                    
 






























                                                                               
 


                           

 
   


                                                
 

                                  


                                                           
 








                                                                            
 
                                                                    
                                                              
                         
         
 
                 

 
 
    
                                                                
 
                         



                                                    
                                                 
                                                                      
                            
 

    

                                                                   

                   



                                                    
                                                              


    
                                                                               
 
                         



                                                    


                                                                      
 
























                                                      
 


                                      

                                                











                                                              
 
                                                                        
                                          
 




                                                                                      
 
                                    
                        
                                    






                                              
           




                                                             
 


                      
                             








                                                                
                                                          


                                                                                     

                      


                         
           
                                               
 











                                                                                





                             
 


                    






                                  
    
                                                                          

                               
                                  


                                                                    
 
                                   
                                                                      


                             
                                                                         



                                                                 
                                                                                                      









                                                                               
                                   
                                                             















                                                                             


                       
                                   



                                              
                                   
                                                                      
 
                                                                  


                       
                                   



                                                                      

                                    
                                                                         

                                                                 

                                                                         
                                                             












                                                                             


                       
 






                                                                             
                       
                          
 
                                   
                                     
 


                                                     
 


                                                                












                                                                      
 




                                                     



                                 

                                                                                                                              







                                          
                                      
                                           

                                                          
                                    
 
 


                                                                        





                                                        
                                                                    






                                                                               
 
 

                                                             





                                                    
                               
                                                             
 
 

                                                            


                                                    
 
                               
                                                             
 
























































































                                                                           
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * GnomeCalendar widget
 * Copyright (C) 1998 the Free Software Foundation
 *
 * Author: Miguel de Icaza (miguel@kernel.org)
 */

#include <config.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkhpaned.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtknotebook.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkvpaned.h>
#include <libgnomeui/gnome-messagebox.h>
#include <cal-util/timeutil.h>
#include "alarm.h"
#include "e-day-view.h"
#include "e-week-view.h"
#include "gncal-todo.h"
#include "gnome-cal.h"
#include "year-view.h"
#include "calendar-commands.h"



static void gnome_calendar_class_init (GnomeCalendar *class);
static void gnome_calendar_destroy (GtkObject *object);

static void gnome_calendar_update_view_times (GnomeCalendar *gcal,
                          GtkWidget *page);
static void gnome_calendar_update_gtk_calendar (GnomeCalendar *gcal);
static void gnome_calendar_on_day_selected (GtkCalendar   *calendar,
                        GnomeCalendar *gcal);
static void gnome_calendar_on_month_changed (GtkCalendar   *calendar,
                         GnomeCalendar *gcal);

static GtkVBoxClass *parent_class;



guint
gnome_calendar_get_type (void)
{
    static guint gnome_calendar_type = 0;
    if(!gnome_calendar_type) {
        GtkTypeInfo gnome_calendar_info = {
            "GnomeCalendar",
            sizeof(GnomeCalendar),
            sizeof(GnomeCalendarClass),
            (GtkClassInitFunc) gnome_calendar_class_init,
            (GtkObjectInitFunc) NULL,
            (GtkArgSetFunc) NULL,
            (GtkArgGetFunc) NULL,
        };
        /*
        gnome_calendar_type = gtk_type_unique(gnome_app_get_type(), &gnome_calendar_info);
        parent_class = gtk_type_class (gnome_app_get_type());
        */
        gnome_calendar_type = gtk_type_unique (gtk_vbox_get_type (),
                               &gnome_calendar_info);
        parent_class = gtk_type_class (gtk_vbox_get_type ());
    }
    return gnome_calendar_type;
}


static void
gnome_calendar_class_init (GnomeCalendar *class)
{
    GtkObjectClass    *object_class;
    object_class = (GtkObjectClass *) class;
    object_class->destroy = gnome_calendar_destroy;
}


static void
gnome_calendar_destroy (GtkObject *object)
{
    GnomeCalendar *gcal;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (object));

    gcal = GNOME_CALENDAR (object);

    gtk_object_unref (GTK_OBJECT (gcal->client));

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}


static void
setup_widgets (GnomeCalendar *gcal)
{
    GtkWidget *vpane, *w;

    /* The Main Notebook. */
    gcal->main_notebook = gtk_notebook_new ();
    gtk_notebook_set_show_border (GTK_NOTEBOOK (gcal->main_notebook),
                      FALSE);
    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (gcal->main_notebook), FALSE);
    gtk_widget_show (gcal->main_notebook);
    gtk_box_pack_start (GTK_BOX (gcal), gcal->main_notebook,
                TRUE, TRUE, 0);

    /* The First Page of the Main Notebook, containing a HPaned with the
       Sub-Notebook on the left and the GtkCalendar and ToDo list on the
       right. */
    gcal->hpane = gtk_hpaned_new ();
    gtk_widget_show (gcal->hpane);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
                  gcal->hpane, gtk_label_new (""));

    /* The Sub-Notebook, to contain the Day, Work-Week & Week views. */
    gcal->sub_notebook = gtk_notebook_new ();
    gtk_notebook_set_show_border (GTK_NOTEBOOK (gcal->sub_notebook),
                      FALSE);
    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (gcal->sub_notebook), FALSE);
    gtk_widget_show (gcal->sub_notebook);
    gtk_paned_pack1 (GTK_PANED (gcal->hpane), gcal->sub_notebook,
             TRUE, TRUE);

    /* The VPaned widget, to contain the GtkCalendar & ToDo list. */
    vpane = gtk_vpaned_new ();
    gtk_widget_show (vpane);
    gtk_paned_pack2 (GTK_PANED (gcal->hpane), vpane, FALSE, TRUE);

    /* The GtkCalendar. */
    w = gtk_calendar_new ();
    gcal->gtk_calendar = GTK_CALENDAR (w);
    gtk_widget_show (w);
    gtk_paned_pack1 (GTK_PANED (vpane), w, FALSE, TRUE);
    gcal->day_selected_id = gtk_signal_connect (GTK_OBJECT (gcal->gtk_calendar),
                            "day_selected",
                            (GtkSignalFunc) gnome_calendar_on_day_selected,
                            gcal);
    gtk_signal_connect (GTK_OBJECT (gcal->gtk_calendar), "month_changed",
                GTK_SIGNAL_FUNC (gnome_calendar_on_month_changed),
                gcal);

    /* The ToDo list. */
    gcal->todo = gncal_todo_new (gcal);
    gtk_paned_pack2 (GTK_PANED (vpane), gcal->todo, TRUE, TRUE);
    gtk_widget_show (gcal->todo);


    /* The Day View. */
    gcal->day_view = e_day_view_new ();
    e_day_view_set_calendar (E_DAY_VIEW (gcal->day_view), gcal);
    gtk_widget_show (gcal->day_view);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
                  gcal->day_view, gtk_label_new (""));

    /* The Work Week View. */
    gcal->work_week_view = e_day_view_new ();
    e_day_view_set_days_shown (E_DAY_VIEW (gcal->work_week_view), 5);
    e_day_view_set_calendar (E_DAY_VIEW (gcal->work_week_view), gcal);
    gtk_widget_show (gcal->work_week_view);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
                  gcal->work_week_view, gtk_label_new (""));

    /* The Week View. */
    gcal->week_view = e_week_view_new ();
    e_week_view_set_calendar (E_WEEK_VIEW (gcal->week_view), gcal);
    gtk_widget_show (gcal->week_view);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
                  gcal->week_view, gtk_label_new (""));

    /* The Month View. */
    gcal->month_view = e_week_view_new ();
    e_week_view_set_calendar (E_WEEK_VIEW (gcal->month_view), gcal);
    e_week_view_set_display_month (E_WEEK_VIEW (gcal->month_view), TRUE);
    gtk_widget_show (gcal->month_view);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
                  gcal->month_view, gtk_label_new (""));

    /* The Year View. */
    gcal->year_view  = year_view_new (gcal, gcal->selection_start_time);
#if 0
    gtk_widget_show (gcal->year_view);
#endif
    gcal->year_view_sw = gtk_scrolled_window_new (NULL, NULL);
    gtk_widget_show (gcal->year_view_sw);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (gcal->year_view_sw),
                    GTK_POLICY_NEVER,
                    GTK_POLICY_AUTOMATIC);
    gtk_container_add (GTK_CONTAINER (gcal->year_view_sw),
               gcal->year_view);
    GTK_LAYOUT (gcal->year_view)->vadjustment->step_increment = 10.0;
    gtk_adjustment_changed (GTK_LAYOUT (gcal->year_view)->vadjustment);
    gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
                  gcal->year_view_sw, gtk_label_new (""));
}

static GtkWidget *
get_current_page (GnomeCalendar *gcal)
{
    GtkWidget *page;

    page = GTK_NOTEBOOK (gcal->main_notebook)->cur_page->child;
    if (page == gcal->hpane)
        return GTK_NOTEBOOK (gcal->sub_notebook)->cur_page->child;
    else
        return page;
}

char *
gnome_calendar_get_current_view_name (GnomeCalendar *gcal)
{
    GtkWidget *page;

    g_return_val_if_fail (gcal != NULL, "dayview");
    g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), "dayview");

    page = get_current_page (gcal);

    if (page == gcal->day_view)
        return "dayview";
    else if (page == gcal->work_week_view)
        return "workweekview";
    else if (page == gcal->week_view)
        return "weekview";
    else if (page == gcal->month_view)
        return "monthview";
    else if (page == gcal->year_view_sw)
        return "yearview";
    else
        return "dayview";
}

void
gnome_calendar_goto (GnomeCalendar *gcal, time_t new_time)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));
    g_return_if_fail (new_time != -1);

    gcal->selection_start_time = time_day_begin (new_time);
    gcal->selection_end_time = time_add_day (gcal->selection_start_time,
                         1);
    gnome_calendar_update_view_times (gcal, NULL);
    gnome_calendar_update_gtk_calendar (gcal);
}


static void
gnome_calendar_update_view_times (GnomeCalendar *gcal,
                  GtkWidget *page)
{
    if (page == NULL)
        page = get_current_page (gcal);

    if (page == gcal->day_view
        || page == gcal->work_week_view)
        e_day_view_set_selected_time_range (E_DAY_VIEW (page),
                            gcal->selection_start_time,
                            gcal->selection_end_time);
    else if (page == gcal->week_view
         || page == gcal->month_view)
        e_week_view_set_selected_time_range (E_WEEK_VIEW (page),
                             gcal->selection_start_time,
                             gcal->selection_end_time);
    else if (page == gcal->year_view_sw)
        year_view_set (YEAR_VIEW (gcal->year_view),
                   gcal->selection_start_time);
    else {
        g_warning ("My penguin is gone!");
        g_assert_not_reached ();
    }
}

static void
gnome_calendar_direction (GnomeCalendar *gcal, int direction)
{
    GtkWidget *cp = get_current_page (gcal);
    time_t current_time, new_time;

    current_time = gcal->selection_start_time;

    if (cp == gcal->day_view)
        new_time = time_add_day (current_time, direction);
    else if (cp == gcal->work_week_view)
        new_time = time_add_week (current_time, direction);
    else if (cp == gcal->week_view)
        new_time = time_add_week (current_time, direction);
    else if (cp == gcal->month_view)
        new_time = time_add_month (current_time, direction);
    else if (cp == gcal->year_view_sw)
        new_time = time_add_year (current_time, direction);
    else {
        g_warning ("Weee!  Where did the penguin go?");
        g_assert_not_reached ();
        new_time = 0;
    }

    gnome_calendar_goto (gcal, new_time);
}

void
gnome_calendar_next (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    gnome_calendar_direction (gcal, 1);
}

void
gnome_calendar_previous (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    gnome_calendar_direction (gcal, -1);
}

void
gnome_calendar_dayjump (GnomeCalendar *gcal, time_t time)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    gnome_calendar_set_view (gcal, "dayview");
    gnome_calendar_goto (gcal, time);
}

void
gnome_calendar_goto_today (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    gnome_calendar_goto (gcal, time (NULL));
}


/* This sets which view is currently shown. It also updates the selection time
   of the view so it shows the appropriate days. */
void
gnome_calendar_set_view (GnomeCalendar *gcal, char *page_name)
{
    GtkWidget *page;
    int main_page = 0, sub_page = -1;

    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));
    g_return_if_fail (page_name != NULL);

    if (strcmp (page_name, "dayview") == 0) {
        page = gcal->day_view;
        sub_page = 0;
    } else if (strcmp (page_name, "workweekview") == 0) {
        page = gcal->work_week_view;
        sub_page = 1;
    } else if (strcmp (page_name, "weekview") == 0) {
        page = gcal->week_view;
        sub_page = 2;
    } else if (strcmp (page_name, "monthview") == 0) {
        page = gcal->month_view;
        main_page = 1;
    } else if (strcmp (page_name, "yearview") == 0) {
        page = gcal->year_view_sw;
        main_page = 2;
    } else {
        g_warning ("Unknown calendar view: %s", page_name);
        return;
    }

    gnome_calendar_update_view_times (gcal, page);

    if (sub_page != -1)
        gtk_notebook_set_page (GTK_NOTEBOOK (gcal->sub_notebook),
                       sub_page);
    gtk_notebook_set_page (GTK_NOTEBOOK (gcal->main_notebook), main_page);

    gnome_calendar_update_gtk_calendar (gcal);
}


static void
gnome_calendar_update_all (GnomeCalendar *cal, iCalObject *object, int flags)
{
    e_day_view_update_event (E_DAY_VIEW (cal->day_view),
                 object, flags);
    e_day_view_update_event (E_DAY_VIEW (cal->work_week_view),
                 object, flags);
    e_week_view_update_event (E_WEEK_VIEW (cal->week_view),
                  object, flags);
    e_week_view_update_event (E_WEEK_VIEW (cal->month_view),
                  object, flags);
    year_view_update (YEAR_VIEW (cal->year_view), object, flags);

    gncal_todo_update (GNCAL_TODO (cal->todo), object, flags);
    gnome_calendar_tag_calendar (cal, cal->gtk_calendar);
}


static void
gnome_calendar_object_updated_cb (GtkWidget *cal_client,
                  const char *uid,
                  GnomeCalendar  *gcal)
{
    printf ("gnome-cal: got object changed_cb, uid='%s'\n",
        uid?uid:"<NULL>");
    gnome_calendar_update_all (gcal, NULL, CHANGE_NEW);
}


static void
gnome_calendar_object_removed_cb (GtkWidget *cal_client,
                  const char *uid,
                  GnomeCalendar  *gcal)
{
    printf ("gnome-cal: got object removed _cb, uid='%s'\n",
        uid?uid:"<NULL>");
    gnome_calendar_update_all (gcal, NULL, CHANGE_ALL);
}


GtkWidget *
gnome_calendar_new (char *title)
{
    GtkWidget      *retval;
    GnomeCalendar  *gcal;

    retval = gtk_type_new (gnome_calendar_get_type ());

    gcal = GNOME_CALENDAR (retval);

    gcal->selection_start_time = time_day_begin (time (NULL));
    gcal->selection_end_time = time_add_day (gcal->selection_start_time,
                         1);
    gcal->client = cal_client_new ();

    setup_widgets (gcal);

    gnome_calendar_set_view (gcal, "dayview");

    gtk_signal_connect (GTK_OBJECT (gcal->client), "obj_updated",
                gnome_calendar_object_updated_cb, gcal);
    gtk_signal_connect (GTK_OBJECT (gcal->client), "obj_removed",
                gnome_calendar_object_removed_cb, gcal);

    return retval;
}

typedef struct
{
    GnomeCalendar *gcal;
    char *uri;
    GnomeCalendarOpenMode gcom;
    guint signal_handle;
} load_or_create_data;


static void
gnome_calendar_load_cb (GtkWidget *cal_client,
            CalClientLoadStatus success,
            load_or_create_data *locd)
{
    g_return_if_fail (locd);
    g_return_if_fail (GNOME_IS_CALENDAR (locd->gcal));

    switch (success) {
    case CAL_CLIENT_LOAD_SUCCESS:
        gnome_calendar_update_all (locd->gcal, NULL, 0);
        printf ("gnome_calendar_load_cb: success\n");
        break;
    case CAL_CLIENT_LOAD_ERROR:
        printf ("gnome_calendar_load_cb: load error.\n");
        if (locd->gcom == CALENDAR_OPEN_OR_CREATE) {
            printf ("gnome_calendar_load_cb: trying create...\n");
            /* FIXME: connect to the cal_loaded signal of the
             * CalClient and get theasynchronous notification
             * properly! */
            /*gtk_signal_connect (GTK_OBJECT (gcal->client),
                        "cal_loaded",
                        gnome_calendar_create_cb, gcal);*/

            gtk_signal_disconnect (GTK_OBJECT (locd->gcal->client),
                           locd->signal_handle);

            cal_client_create_calendar (locd->gcal->client,
                            locd->uri);
            gnome_calendar_update_all (locd->gcal, NULL, 0);
        }
        break;
    case CAL_CLIENT_LOAD_IN_USE:
        printf ("gnome_calendar_load_cb: in use\n");
        break;
    }

    g_free (locd->uri);
    g_free (locd);
}


int
gnome_calendar_open (GnomeCalendar *gcal,
             char *file,
             GnomeCalendarOpenMode gcom)
{
    load_or_create_data *locd;

    g_return_val_if_fail (gcal != NULL, 0);
    g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), 0);
    g_return_val_if_fail (file != NULL, 0);

    locd = g_new0 (load_or_create_data, 1);
    locd->gcal = gcal;
    locd->uri = g_strdup (file);
    locd->gcom = gcom;

    locd->signal_handle = gtk_signal_connect (GTK_OBJECT (gcal->client),
                          "cal_loaded",
                          gnome_calendar_load_cb,
                          locd);

    if (cal_client_load_calendar (gcal->client, file) == FALSE){
        printf ("Error loading calendar: %s\n", file);
        return 0;
    }

    return 1;
}


void
gnome_calendar_add_object (GnomeCalendar *gcal, iCalObject *obj)
{
    char *obj_string;
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));
    g_return_if_fail (obj != NULL);

    obj_string = ical_object_to_string (obj);
    cal_client_update_object (gcal->client, obj->uid, obj_string);
    g_free (obj_string);
}

void
gnome_calendar_remove_object (GnomeCalendar *gcal, iCalObject *obj)
{
    gboolean r;

    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));
    g_return_if_fail (obj != NULL);

    r = cal_client_remove_object (gcal->client, obj->uid);
}

void
gnome_calendar_object_changed (GnomeCalendar *gcal, iCalObject *obj, int flags)
{
    char *obj_string;
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));
    g_return_if_fail (obj != NULL);

    obj_string = ical_object_to_string (obj);
    cal_client_update_object (gcal->client, obj->uid, obj_string);
    g_free (obj_string);
}

static int
max_open_files (void)
{
        static int files;

        if (files)
                return files;

        files = sysconf (_SC_OPEN_MAX);
        if (files != -1)
                return files;
#ifdef OPEN_MAX
        return files = OPEN_MAX;
#else
        return files = 256;
#endif
}

static void
execute (char *command, int close_standard)
{
    struct sigaction ignore, save_intr, save_quit;
    int status = 0, i;
    pid_t pid;

    ignore.sa_handler = SIG_IGN;
    sigemptyset (&ignore.sa_mask);
    ignore.sa_flags = 0;

    sigaction (SIGINT, &ignore, &save_intr);
    sigaction (SIGQUIT, &ignore, &save_quit);

    if ((pid = fork ()) < 0){
        fprintf (stderr, "\n\nfork () = -1\n");
        return;
    }
    if (pid == 0){
        pid = fork ();
        if (pid == 0){
            const int top = max_open_files ();
            sigaction (SIGINT,  &save_intr, NULL);
            sigaction (SIGQUIT, &save_quit, NULL);

            for (i = (close_standard ? 0 : 3); i < top; i++)
                close (i);

            /* FIXME: As an excercise to the reader, copy the
             * code from mc to setup shell properly instead of
             * /bin/sh.  Yes, this comment is larger than a cut and paste.
             */
            execl ("/bin/sh", "/bin/sh", "-c", command, (char *) 0);

            _exit (127);
        } else {
            _exit (127);
        }
    }
    wait (&status);
    sigaction (SIGINT,  &save_intr, NULL);
    sigaction (SIGQUIT, &save_quit, NULL);
}

static void
mail_notify (char *mail_address, char *text, time_t app_time)
{
    pid_t pid;
    int   p [2];
    char *command;

    pipe (p);
    pid = fork ();
    if (pid == 0){
        int dev_null;

        dev_null = open ("/dev/null", O_RDWR);
        dup2 (p [0], 0);
        dup2 (dev_null, 1);
        dup2 (dev_null, 2);
        execl ("/usr/lib/sendmail", "/usr/lib/sendmail",
               mail_address, NULL);
        _exit (127);
    }
    command = g_strconcat ("To: ", mail_address, "\n",
                  "Subject: ", _("Reminder of your appointment at "),
                  ctime (&app_time), "\n\n", text, "\n", NULL);
    write (p [1], command, strlen (command));
    close (p [1]);
    close (p [0]);
    g_free (command);
}

static void
stop_beeping (GtkObject* object, gpointer data)
{
    guint timer_tag, beep_tag;
    timer_tag = GPOINTER_TO_INT (gtk_object_get_data (object, "timer_tag"));
    beep_tag  = GPOINTER_TO_INT (gtk_object_get_data (object, "beep_tag"));
    
    if (beep_tag > 0) {
        gtk_timeout_remove (beep_tag);
        gtk_object_set_data (object, "beep_tag", GINT_TO_POINTER (0));
    }
    if (timer_tag > 0) {
        gtk_timeout_remove (timer_tag);
        gtk_object_set_data (object, "timer_tag", GINT_TO_POINTER (0));
    }
}

static gint
start_beeping (gpointer data)
{
    gdk_beep ();

    return TRUE;
}

static gint
timeout_beep (gpointer data)
{
    stop_beeping (data, NULL);
    return FALSE;
}

void
calendar_notify (time_t activation_time, CalendarAlarm *which, void *data)
{
    iCalObject *ico = data;
    guint beep_tag, timer_tag;
    int ret;
    gchar* snooze_button = (enable_snooze ? _("Snooze") : NULL);
    time_t now, diff;

    if (&ico->aalarm == which){
        time_t app = ico->aalarm.trigger + ico->aalarm.offset;
        GtkWidget *w;
        char *msg;

        msg = g_strconcat (_("Reminder of your appointment at "),
                    ctime (&app), "`",
                    ico->summary, "'", NULL);

        /* Idea: we need Snooze option :-) */
        w = gnome_message_box_new (msg, GNOME_MESSAGE_BOX_INFO, _("Ok"), snooze_button, NULL);
        beep_tag = gtk_timeout_add (1000, start_beeping, NULL);
        if (enable_aalarm_timeout)
            timer_tag = gtk_timeout_add (audio_alarm_timeout*1000, 
                             timeout_beep, w);
        else
            timer_tag = 0;
        gtk_object_set_data (GTK_OBJECT (w), "timer_tag",
                     GINT_TO_POINTER (timer_tag));
        gtk_object_set_data (GTK_OBJECT (w), "beep_tag",
                     GINT_TO_POINTER (beep_tag));
        gtk_widget_ref (w);
        gtk_window_set_modal (GTK_WINDOW (w), FALSE);
        ret = gnome_dialog_run (GNOME_DIALOG (w));
        switch (ret) {
        case 1:
            stop_beeping (GTK_OBJECT (w), NULL);
            now = time (NULL);
            diff = now - which->trigger;
            which->trigger = which->trigger + diff + snooze_secs;
            which->offset  = which->offset - diff - snooze_secs;
            alarm_add (which, &calendar_notify, data);
            break;
        default:
            stop_beeping (GTK_OBJECT (w), NULL);
            break;
        }
        
        gtk_widget_unref (w);
        return;
    }

        if (&ico->palarm == which){
        execute (ico->palarm.data, 0);
        return;
    }

    if (&ico->malarm == which){
        time_t app = ico->malarm.trigger + ico->malarm.offset;

        mail_notify (ico->malarm.data, ico->summary, app);
        return;
    }

    if (&ico->dalarm == which){
        time_t app = ico->dalarm.trigger + ico->dalarm.offset;
        GtkWidget *w;
        char *msg;

        if (beep_on_display)
            gdk_beep ();
        msg = g_strconcat (_("Reminder of your appointment at "),
                    ctime (&app), "`",
                    ico->summary, "'", NULL);
        w = gnome_message_box_new (msg, GNOME_MESSAGE_BOX_INFO, 
                       _("Ok"), snooze_button, NULL);
        gtk_window_set_modal (GTK_WINDOW (w), FALSE);
        ret = gnome_dialog_run (GNOME_DIALOG (w));
        switch (ret) {
        case 1:
            now = time (NULL);
            diff = now - which->trigger;
            which->trigger = which->trigger + diff + snooze_secs;
            which->offset  = which->offset - diff - snooze_secs;
            alarm_add (which, &calendar_notify, data);
            break;
        default:
            break;
        }
        
        return;
    }
}

/*
 * called from the calendar_iterate routine to mark the days of a GtkCalendar
 */
static int
mark_gtk_calendar_day (iCalObject *obj, time_t start, time_t end, void *c)
{
    GtkCalendar *gtk_cal = c;
    struct tm tm_s;
    time_t t, day_end;

    tm_s = *localtime (&start);
    day_end = time_day_end (end);

    for (t = start; t <= day_end; t += 60*60*24){
        time_t new = mktime (&tm_s);
        struct tm tm_day;

        tm_day = *localtime (&new);
        gtk_calendar_mark_day (gtk_cal, tm_day.tm_mday);
        tm_s.tm_mday++;
    }
    return TRUE;
}

/*
 * Tags the dates with appointments in a GtkCalendar based on the
 * GnomeCalendar contents
 */
void
gnome_calendar_tag_calendar (GnomeCalendar *cal, GtkCalendar *gtk_cal)
{
    time_t month_begin, month_end;
    struct tm tm;

    g_return_if_fail (cal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (cal));
    g_return_if_fail (gtk_cal != NULL);
    g_return_if_fail (GTK_IS_CALENDAR (gtk_cal));

    /* compute month_begin */
    tm.tm_hour = 0;
    tm.tm_min  = 0;
    tm.tm_sec  = 0;
    tm.tm_mday = 1; /* setting this to zero is a no-no; it will set mktime back to the end of the
                       previous month, which may be 28,29,30; this may chop some days from the calendar */
    tm.tm_mon  = gtk_cal->month;
    tm.tm_year = gtk_cal->year - 1900;
    tm.tm_isdst= -1;

    month_begin = mktime (&tm);
    tm.tm_mon++;
    month_end   = mktime (&tm);

    gtk_calendar_freeze (gtk_cal);
    gtk_calendar_clear_marks (gtk_cal);
    calendar_iterate (cal, month_begin, month_end,
              mark_gtk_calendar_day, gtk_cal);
    gtk_calendar_thaw (gtk_cal);
}

/* This is called when the day begin & end times, the AM/PM flag, or the
   week_starts_on_monday flags are changed.
   FIXME: Which of these options do we want the new views to support? */
void
gnome_calendar_time_format_changed (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    year_view_time_format_changed (YEAR_VIEW (gcal->year_view));

    gtk_calendar_display_options (gcal->gtk_calendar,
                      (week_starts_on_monday
                       ? (gcal->gtk_calendar->display_flags
                      | GTK_CALENDAR_WEEK_START_MONDAY)
                       : (gcal->gtk_calendar->display_flags
                      & ~GTK_CALENDAR_WEEK_START_MONDAY)));
}

/* This is called when any of the color settings are changed.
   FIXME: Need to update for the new views. */
void
gnome_calendar_colors_changed (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    todo_style_changed = 1;
    gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, 0);
}

void
gnome_calendar_todo_properties_changed (GnomeCalendar *gcal)
{
    g_return_if_fail (gcal != NULL);
    g_return_if_fail (GNOME_IS_CALENDAR (gcal));

    todo_style_changed = 1;
    gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, 0);
}


void
gnome_calendar_set_selected_time_range (GnomeCalendar *gcal,
                    time_t         start_time,
                    time_t         end_time)
{
    gcal->selection_start_time = start_time;
    gcal->selection_end_time = end_time;

    gnome_calendar_update_gtk_calendar (gcal);
}


/* This updates the month shown and the day selected in the calendar, if
   necessary. */
static void
gnome_calendar_update_gtk_calendar (GnomeCalendar *gcal)
{
    GDate date;
    guint current_year, current_month, current_day;
    guint new_year, new_month, new_day;
    gboolean set_day = FALSE;

    /* If the GtkCalendar isn't visible, we just return. */
    if (!GTK_WIDGET_VISIBLE (gcal->gtk_calendar))
        return;

    gtk_calendar_get_date (gcal->gtk_calendar, &current_year,
                   &current_month, &current_day);

    g_date_clear (&date, 1);
    g_date_set_time (&date, gcal->selection_start_time);
    new_year = g_date_year (&date);
    new_month = g_date_month (&date) - 1;
    new_day = g_date_day (&date);

    /* Block the "day_selected" signal while we update the calendar. */
    gtk_signal_handler_block (GTK_OBJECT (gcal->gtk_calendar),
                  gcal->day_selected_id);

    /* If the year & month don't match, update it. */
    if (new_year != current_year || new_month != current_month) {
        /* FIXME: GtkCalendar bug workaround. If we select a month
           which has less days than the currently selected day, it
           causes a problem next time we set the day. */
        if (current_day > 28) {
            gtk_calendar_select_day (gcal->gtk_calendar, 28);
            set_day = TRUE;
        }
        gtk_calendar_select_month (gcal->gtk_calendar, new_month,
                       new_year);
    }

    /* If the day doesn't match, update it. */
    if (new_day != current_day || set_day)
        gtk_calendar_select_day (gcal->gtk_calendar, new_day);

    gtk_signal_handler_unblock (GTK_OBJECT (gcal->gtk_calendar),
                    gcal->day_selected_id);
}

static void
gnome_calendar_on_day_selected (GtkCalendar   *calendar,
                GnomeCalendar *gcal)
{
    gint y, m, d;
    struct tm tm;

    gtk_calendar_get_date (calendar, &y, &m, &d);

    tm.tm_year = y - 1900;
    tm.tm_mon  = m;
    tm.tm_mday = d;
    tm.tm_hour = 5; /* for daylight savings time fix */
    tm.tm_min  = 0;
    tm.tm_sec  = 0;

    gnome_calendar_goto (gcal, mktime (&tm));
}


static void
gnome_calendar_on_month_changed (GtkCalendar   *calendar,
                 GnomeCalendar *gcal)
{
    gnome_calendar_tag_calendar (gcal, gcal->gtk_calendar);
}