aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gui/event-editor.c
blob: 52b10b0eeb1d74ae9037e34847f3486770b2def9 (plain) (tree)





















                                                                            


                   

                                    
                              













                                                   


                                            


                                         
                                   


                              
                                 











                                             
                                                   



                                      

                                        






                                            


























                                                          






                                               
 






                                                              






                                 









                                                                              
 



                         

         

                                                                         
  
































































































                                                                                                      



                                                                              









                                                       
                                                       


                                             
                                                   










                                                                 

                                                                                       

                                                           


                                                                 







                                                                         


























                                                                                                     









                                                                                
                                        

                                   
                                      










                                                  
                                                        


                                           
                                             





                                                 















                                                               






                                                    









































































































































































































































































                                                                                                   

















































































                                                                                                     


                          




                                                                                                    

                          





                                                                               





















                                                                    
                      





                                                              
                                                           
 
                                                                   

               
                                     
 
                  

 



                                                                           











                                                                 
      

           
                             


                    
                         

                       
                                                         

                                    

                                  

 





                                                               
 
                                                                                      
           
                                       
 

                                 
                               





                                 
 

                                      
 
                                                                      
 

                                                                
 
                                                                  
 



                                                                        
 



                                                                               
 



                                                                      
 

                                          
 

                                          
 









                                                                                   
 


                                                  
 


                                                       
 

                                        
         





                                    
         
 











































                                                                                                  
                                                                          






                                                                            
 

                                                
 

                                                                      
 
                      
 




                                                                    
 

                                        
         
 






























                                                                                  

           
                                          
 




                                   
 

                                                                  
            
                                                                      
 

                               

           
                                              
 






                                              


         
           
                                                 
 



                                 
 
                        
 





















                                                                                   





                                                                     
                               
 






                                                            



                                                      
                                                                
            
                                                                 

 



                                      
                                                
 
                                 


                       


                                                           



                                   


                                                               


                                     
 

                                                             




                                                  
                                                 
 
                                 

                                   
 
                        
 

                                                         




                                               
                                                           



                                                          

                                                                                 



                                                          
                                                                                        



                 




                                                              
                                                 
 
                                 

                                   
 
                        



                                              

                                                         




                                               
                                                           

                                                         







                                                                                     

                                                                                 

                                                         







                                                              
                                                                                     




                                                         
                           

 
           
                                                      
 










                                                                                        


 




                               
                                                                       




                   
                                            
 
                                 
                   




                        





                                        




                                                             
 
                                                              



           
                                                               
 






                                                                     



           
                                                                 
 
                                 
                        
                
 



                                                             
                       

                                                       



                                                                                        
                               
                      
 
                                             



           
                                                                 
 
                                 



                        



                                                             

                       
                                                       

                                                
                                                                      




                                                                      

                     
 



                                                  

                               
 


                                                 


                                              

                                                   




                                                                                      






















                                                                              





                                                           


                                                                   
   
/* Evolution calendar - Event editor dialog
 *
 * Copyright (C) 2000 Helix Code, Inc.
 *
 * Authors: Miguel de Icaza <miguel@helixcode.com>
 *          Federico Mena-Quintero <federico@helixcode.com>
 *          Seth Alves <alves@helixcode.com>
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

#include <config.h>
#include <gnome.h>
#include <glade/glade.h>
#include <e-util/e-dialog-widgets.h>
#include <cal-util/timeutil.h>
#include "event-editor.h"



typedef struct {
    /* Glade XML data */
    GladeXML *xml;

    /* Calendar this editor is associated to */
    GnomeCalendar *gcal;

    /* UI handler */
    BonoboUIHandler *uih;

    /* Calendar object we are editing */
    iCalObject *ico;

    /* Widgets from the Glade file */

    GtkWidget *general_owner;
    GtkWidget *general_summary;

    GtkWidget *start_time;
    GtkWidget *end_time;
    GtkWidget *all_day_event;

    GtkWidget *alarm_display;
    GtkWidget *alarm_program;
    GtkWidget *alarm_audio;
    GtkWidget *alarm_mail;
    GtkWidget *alarm_display_amount;
    GtkWidget *alarm_display_unit;
    GtkWidget *alarm_audio_amount;
    GtkWidget *alarm_audio_unit;
    GtkWidget *alarm_program_amount;
    GtkWidget *alarm_program_unit;
    GtkWidget *alarm_program_run_program;
    GtkWidget *alarm_program_run_program_entry;
    GtkWidget *alarm_mail_amount;
    GtkWidget *alarm_mail_unit;
    GtkWidget *alarm_mail_mail_to;

    GtkWidget *classification_radio;

    GtkWidget *recurrence_rule_notebook;
    GtkWidget *recurrence_rule_none;
    GtkWidget *recurrence_rule_daily;
    GtkWidget *recurrence_rule_weekly;
    GtkWidget *recurrence_rule_monthly;
    GtkWidget *recurrence_rule_yearly;

    GtkWidget *recurrence_rule_daily_days;

    GtkWidget *recurrence_rule_weekly_weeks;
    GtkWidget *recurrence_rule_weekly_sun;
    GtkWidget *recurrence_rule_weekly_mon;
    GtkWidget *recurrence_rule_weekly_tue;
    GtkWidget *recurrence_rule_weekly_wed;
    GtkWidget *recurrence_rule_weekly_thu;
    GtkWidget *recurrence_rule_weekly_fri;
    GtkWidget *recurrence_rule_weekly_sat;

    GtkWidget *recurrence_rule_monthly_on_day;
    GtkWidget *recurrence_rule_monthly_weekday;
    GtkWidget *recurrence_rule_monthly_day_nth;
    GtkWidget *recurrence_rule_monthly_week;
    GtkWidget *recurrence_rule_monthly_weekpos;
    GtkWidget *recurrence_rule_monthly_every_n_months;
    GtkWidget *recurrence_rule_yearly_every_n_years;

    GtkWidget *recurrence_ending_date_repeat_forever;
    GtkWidget *recurrence_ending_date_end_on;
    GtkWidget *recurrence_ending_date_end_on_date;
    GtkWidget *recurrence_ending_date_end_after;
    GtkWidget *recurrence_ending_date_end_after_count;

    GtkWidget *recurrence_exceptions_date;
    GtkWidget *recurrence_exceptions_list;
    GtkWidget *recurrence_exception_add;
    GtkWidget *recurrence_exception_delete;
    GtkWidget *recurrence_exception_change;

    GtkWidget *exception_list;
    GtkWidget *exception_date;
} EventEditorPrivate;



static void event_editor_class_init (EventEditorClass *class);
static void event_editor_init (EventEditor *ee);
static void event_editor_destroy (GtkObject *object);

static GnomeAppClass *parent_class;

extern int day_begin, day_end;
extern char *user_name;
extern int am_pm_flag;
extern int week_starts_on_monday;


static void append_exception (EventEditor *ee, time_t t);
static void check_all_day (EventEditor *ee);
static void set_all_day (GtkWidget *toggle, EventEditor *ee);
static void alarm_toggle (GtkWidget *toggle, EventEditor *ee);
static void check_dates (GnomeDateEdit *gde, EventEditor *ee);
static void check_times (GnomeDateEdit *gde, EventEditor *ee);
static void recurrence_toggled (GtkWidget *radio, EventEditor *ee);
static void recurrence_exception_added (GtkWidget *widget, EventEditor *ee);
static void recurrence_exception_deleted (GtkWidget *widget, EventEditor *ee);
static void recurrence_exception_changed (GtkWidget *widget, EventEditor *ee);



/**
 * event_editor_get_type:
 * @void:
 *
 * Registers the #EventEditor class if necessary, and returns the type ID
 * associated to it.
 *
 * Return value: The type ID of the #EventEditor class.
 **/
GtkType
event_editor_get_type (void)
{
    static GtkType event_editor_type = 0;

    if (!event_editor_type) {
        static const GtkTypeInfo event_editor_info = {
            "EventEditor",
            sizeof (EventEditor),
            sizeof (EventEditorClass),
            (GtkClassInitFunc) event_editor_class_init,
            (GtkObjectInitFunc) event_editor_init,
            NULL, /* reserved_1 */
            NULL, /* reserved_2 */
            (GtkClassInitFunc) NULL
        };

        event_editor_type = gtk_type_unique (gnome_app_get_type (), &event_editor_info);
    }

    return event_editor_type;
}

/* Class initialization function for the event editor */
static void
event_editor_class_init (EventEditorClass *class)
{
    GtkObjectClass *object_class;

    object_class = (GtkObjectClass *) class;

    parent_class = gtk_type_class (gnome_app_get_type ());

    object_class->destroy = event_editor_destroy;
}

/* Object initialization function for the event editor */
static void
event_editor_init (EventEditor *ee)
{
    EventEditorPrivate *priv;

    priv = g_new0 (EventEditorPrivate, 1);
    ee->priv = priv;
}

/* Destroy handler for the event editor */
static void
event_editor_destroy (GtkObject *object)
{
    EventEditor *ee;
    EventEditorPrivate *priv;

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

    ee = EVENT_EDITOR (object);
    priv = ee->priv;

    if (priv->xml) {
        gtk_object_unref (GTK_OBJECT (priv->xml));
        priv->xml = NULL;
    }

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

/* Creates an appropriate title for the event editor dialog */
static char *
make_title_from_ico (iCalObject *ico)
{
    const char *summary;

    if (ico->summary)
        summary = ico->summary;
    else
        summary =  _("No summary");

    switch (ico->type) {
    case ICAL_EVENT:
        return g_strdup_printf (_("Appointment - %s"), summary);

    case ICAL_TODO:
        return g_strdup_printf (_("Task - %s"), summary);

    case ICAL_JOURNAL:
        return g_strdup_printf (_("Journal entry - %s"), summary);

    default:
        g_message ("make_title_from_ico(): Cannot handle object of type %d", (int) ico->type);
        return NULL;
    }
}

/* Gets the widgets from the XML file and returns if they are all available.
 * For the widgets whose values can be simply set with e-dialog-utils, it does
 * that as well.
 */
static gboolean
get_widgets (EventEditor *ee)
{
    EventEditorPrivate *priv;

    priv = ee->priv;

#define GW(name) glade_xml_get_widget (priv->xml, name)

    priv->general_owner = GW ("general-owner");
    priv->general_summary = GW ("general-summary");

    priv->start_time = GW ("start-time");
    priv->end_time = GW ("end-time");
    priv->all_day_event = GW ("all-day-event");

    priv->alarm_display = GW ("alarm-display");
    priv->alarm_program = GW ("alarm-program");
    priv->alarm_audio = GW ("alarm-audio");
    priv->alarm_mail = GW ("alarm-mail");
    priv->alarm_display_amount = GW ("alarm-display-amount");
    priv->alarm_display_unit = GW ("alarm-display-unit");
    priv->alarm_audio_amount = GW ("alarm-audio-amount");
    priv->alarm_audio_unit = GW ("alarm-audio-unit");
    priv->alarm_program_amount = GW ("alarm-program-amount");
    priv->alarm_program_unit = GW ("alarm-program-unit");
    priv->alarm_program_run_program = GW ("alarm-program-run-program");
    priv->alarm_program_run_program_entry = GW ("alarm-program-run-program-entry");
    priv->alarm_mail_amount = GW ("alarm-mail-amount");
    priv->alarm_mail_unit = GW ("alarm-mail-unit");
    priv->alarm_mail_mail_to = GW ("alarm-mail-mail-to");

    priv->classification_radio = GW ("classification-radio");

    priv->recurrence_rule_notebook = GW ("recurrence-rule-notebook");
    priv->recurrence_rule_none = GW ("recurrence-rule-none");
    priv->recurrence_rule_daily = GW ("recurrence-rule-daily");
    priv->recurrence_rule_weekly = GW ("recurrence-rule-weekly");
    priv->recurrence_rule_monthly = GW ("recurrence-rule-monthly");
    priv->recurrence_rule_yearly = GW ("recurrence-rule-yearly");

    priv->recurrence_rule_daily_days = GW ("recurrence-rule-daily-days");

    priv->recurrence_rule_weekly_weeks = GW ("recurrence-rule-weekly-weeks");
    priv->recurrence_rule_weekly_sun = GW ("recurrence-rule-weekly-sun");
    priv->recurrence_rule_weekly_mon = GW ("recurrence-rule-weekly-mon");
    priv->recurrence_rule_weekly_tue = GW ("recurrence-rule-weekly-tue");
    priv->recurrence_rule_weekly_wed = GW ("recurrence-rule-weekly-wed");
    priv->recurrence_rule_weekly_thu = GW ("recurrence-rule-weekly-thu");
    priv->recurrence_rule_weekly_fri = GW ("recurrence-rule-weekly-fri");
    priv->recurrence_rule_weekly_sat = GW ("recurrence-rule-weekly-sat");

    priv->recurrence_rule_monthly_on_day = GW ("recurrence-rule-monthly-on-day");
    priv->recurrence_rule_monthly_weekday = GW ("recurrence-rule-monthly-weekday");
    priv->recurrence_rule_monthly_day_nth = GW ("recurrence-rule-monthly-day-nth");
    priv->recurrence_rule_monthly_week = GW ("recurrence-rule-monthly-week");
    priv->recurrence_rule_monthly_weekpos = GW ("recurrence-rule-monthly-weekpos");
    priv->recurrence_rule_monthly_every_n_months = GW ("recurrence-rule-monthly-every-n-months");
    priv->recurrence_rule_yearly_every_n_years = GW ("recurrence-rule-yearly-every-n-years");

    priv->recurrence_ending_date_repeat_forever = GW ("recurrence-ending-date-repeat-forever");
    priv->recurrence_ending_date_end_on = GW ("recurrence-ending-date-end-on");
    priv->recurrence_ending_date_end_on_date = GW ("recurrence-ending-date-end-on-date");
    priv->recurrence_ending_date_end_after = GW ("recurrence-ending-date-end-after");
    priv->recurrence_ending_date_end_after_count = GW ("recurrence-ending-date-end-after-count");

    priv->recurrence_exceptions_date = GW ("recurrence-exceptions-date");
    priv->recurrence_exceptions_list = GW ("recurrence-exceptions-list");
    priv->recurrence_exception_add = GW ("recurrence-exceptions-add");
    priv->recurrence_exception_delete = GW ("recurrence-exceptions-delete");
    priv->recurrence_exception_change = GW ("recurrence-exceptions-change");

    priv->exception_list = GW ("recurrence-exceptions-list");
    priv->exception_date = GW ("recurrence-exceptions-date");

#undef GW

    return (priv->general_owner
        && priv->general_summary
        && priv->start_time
        && priv->end_time
        && priv->all_day_event
        && priv->alarm_display
        && priv->alarm_program
        && priv->alarm_audio
        && priv->alarm_mail
        && priv->alarm_display_amount
        && priv->alarm_display_unit
        && priv->alarm_audio_amount
        && priv->alarm_audio_unit
        && priv->alarm_program_amount
        && priv->alarm_program_unit
        && priv->alarm_program_run_program
        && priv->alarm_program_run_program_entry
        && priv->alarm_mail_amount
        && priv->alarm_mail_unit
        && priv->alarm_mail_mail_to
        && priv->classification_radio
        && priv->recurrence_rule_notebook
        && priv->recurrence_rule_none
        && priv->recurrence_rule_daily
        && priv->recurrence_rule_weekly
        && priv->recurrence_rule_monthly
        && priv->recurrence_rule_yearly
        && priv->recurrence_rule_daily_days
        && priv->recurrence_rule_weekly_weeks
        && priv->recurrence_rule_monthly_on_day
        && priv->recurrence_rule_monthly_weekday
        && priv->recurrence_rule_monthly_day_nth
        && priv->recurrence_rule_monthly_week
        && priv->recurrence_rule_monthly_weekpos
        && priv->recurrence_rule_monthly_every_n_months
        && priv->recurrence_rule_yearly_every_n_years
        && priv->recurrence_ending_date_repeat_forever
        && priv->recurrence_ending_date_end_on
        && priv->recurrence_ending_date_end_on_date
        && priv->recurrence_ending_date_end_after
        && priv->recurrence_ending_date_end_after_count
        && priv->recurrence_exceptions_date
        && priv->recurrence_exceptions_list
        && priv->recurrence_exception_add
        && priv->recurrence_exception_delete
        && priv->recurrence_exception_change
        && priv->exception_list
        && priv->exception_date);
}

/* Classification types; just an enum for mapping them to radio buttons
 * (iCalObject uses strings for these).
 */
typedef enum {
    CLASSIFICATION_PUBLIC,
    CLASSIFICATION_PRIVATE,
    CLASSIFICATION_CONFIDENTIAL
} ClassificationType;

static const int classification_map[] = {
    CLASSIFICATION_PUBLIC,
    CLASSIFICATION_PRIVATE,
    CLASSIFICATION_CONFIDENTIAL,
    -1
};

static const int alarm_unit_map[] = {
    ALARM_MINUTES,
    ALARM_HOURS,
    ALARM_DAYS,
    -1
};

static void
alarm_unit_set (GtkWidget *widget, enum AlarmUnit unit)
{
    e_dialog_option_menu_set (widget, unit, alarm_unit_map);
}

static enum AlarmUnit
alarm_unit_get (GtkWidget *widget)
{
    return e_dialog_option_menu_get (widget, alarm_unit_map);
}

/* Recurrence types for mapping them to radio buttons */
typedef enum {
    RECUR_OPTION_NONE,
    RECUR_OPTION_DAILY,
    RECUR_OPTION_WEEKLY,
    RECUR_OPTION_MONTHLY,
    RECUR_OPTION_YEARLY,
} RecurOptions;

static const int recur_options_map[] = {
    RECUR_OPTION_NONE,
    RECUR_OPTION_DAILY,
    RECUR_OPTION_WEEKLY,
    RECUR_OPTION_MONTHLY,
    RECUR_OPTION_YEARLY,
    -1
};

static RecurOptions
recur_options_get (GtkWidget *widget)
{
    return e_dialog_radio_get (widget, recur_options_map);
}

static const int month_pos_map[] = { 0, 1, 2, 3, 4, -1 };
static const int weekday_map[] = { 0, 1, 2, 3, 4, 5, 6, -1 };

/* Initializes the widget's values and signals */
static void
init_widgets (EventEditor *ee)
{
    EventEditorPrivate *priv;
    GList *list;

    priv = ee->priv;

    /* Owner, summary */

    e_dialog_editable_set (priv->general_owner,
                   priv->ico->organizer->addr ?
                   priv->ico->organizer->addr : _("?"));

    e_dialog_editable_set (priv->general_summary, priv->ico->summary);

    /* Start and end times */

    gtk_signal_connect (GTK_OBJECT (priv->start_time), "date_changed",
                GTK_SIGNAL_FUNC (check_dates), ee);
    gtk_signal_connect (GTK_OBJECT (priv->start_time), "time_changed",
                GTK_SIGNAL_FUNC (check_times), ee);

    gtk_signal_connect (GTK_OBJECT (priv->end_time), "date_changed",
                GTK_SIGNAL_FUNC (check_dates), ee);
    gtk_signal_connect (GTK_OBJECT (priv->end_time), "time_changed",
                GTK_SIGNAL_FUNC (check_times), ee);

    e_dialog_dateedit_set (priv->start_time, priv->ico->dtstart);
    e_dialog_dateedit_set (priv->end_time, priv->ico->dtend);

    check_all_day (ee);

    gtk_signal_connect (GTK_OBJECT (priv->all_day_event), "toggled",
                GTK_SIGNAL_FUNC (set_all_day), ee);

    /* Alarms */

    e_dialog_toggle_set (priv->alarm_display, priv->ico->dalarm.enabled);
    e_dialog_toggle_set (priv->alarm_program, priv->ico->palarm.enabled);
    e_dialog_toggle_set (priv->alarm_audio, priv->ico->aalarm.enabled);
    e_dialog_toggle_set (priv->alarm_mail, priv->ico->malarm.enabled);
    alarm_toggle (priv->alarm_display, ee);
    alarm_toggle (priv->alarm_program, ee);
    alarm_toggle (priv->alarm_audio, ee);
    alarm_toggle (priv->alarm_mail, ee);
    gtk_signal_connect (GTK_OBJECT (priv->alarm_display), "toggled",
                GTK_SIGNAL_FUNC (alarm_toggle), ee);
    gtk_signal_connect (GTK_OBJECT (priv->alarm_program), "toggled",
                GTK_SIGNAL_FUNC (alarm_toggle), ee);
    gtk_signal_connect (GTK_OBJECT (priv->alarm_audio), "toggled",
                GTK_SIGNAL_FUNC (alarm_toggle), ee);
    gtk_signal_connect (GTK_OBJECT (priv->alarm_mail), "toggled",
                GTK_SIGNAL_FUNC (alarm_toggle), ee);

    /* Alarm data */

    e_dialog_spin_set (priv->alarm_display_amount, priv->ico->dalarm.count);
    e_dialog_spin_set (priv->alarm_audio_amount, priv->ico->aalarm.count);
    e_dialog_spin_set (priv->alarm_program_amount, priv->ico->palarm.count);
    e_dialog_spin_set (priv->alarm_mail_amount, priv->ico->malarm.count);

    alarm_unit_set (priv->alarm_display_unit, priv->ico->dalarm.units);
    alarm_unit_set (priv->alarm_audio_unit, priv->ico->aalarm.units);
    alarm_unit_set (priv->alarm_program_unit, priv->ico->palarm.units);
    alarm_unit_set (priv->alarm_mail_unit, priv->ico->malarm.units);

    e_dialog_editable_set (priv->alarm_program_run_program_entry, priv->ico->palarm.data);
    e_dialog_editable_set (priv->alarm_mail_mail_to, priv->ico->malarm.data);

    /* Classification */

    if (strcmp (priv->ico->class, "PUBLIC") == 0)
            e_dialog_radio_set (priv->classification_radio, CLASSIFICATION_PUBLIC,
                    classification_map);
    else if (strcmp (priv->ico->class, "PRIVATE") == 0)
            e_dialog_radio_set (priv->classification_radio, CLASSIFICATION_PRIVATE,
                    classification_map);
    else if (strcmp (priv->ico->class, "CONFIDENTIAL") == 0)
            e_dialog_radio_set (priv->classification_radio, CLASSIFICATION_CONFIDENTIAL,
                    classification_map);
    else {
        /* What do do? */
    }

    /* Recurrence types */

    gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_none), "toggled",
                GTK_SIGNAL_FUNC (recurrence_toggled), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_daily), "toggled",
                GTK_SIGNAL_FUNC (recurrence_toggled), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_weekly), "toggled",
                GTK_SIGNAL_FUNC (recurrence_toggled), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_monthly), "toggled",
                GTK_SIGNAL_FUNC (recurrence_toggled), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_rule_yearly), "toggled",
                GTK_SIGNAL_FUNC (recurrence_toggled), ee);

    /* Recurrence rules */

    if (!priv->ico->recur)
        e_dialog_radio_set (priv->recurrence_rule_none, RECUR_OPTION_NONE,
                    recur_options_map);
    else {
        switch (priv->ico->recur->type) {
        case RECUR_DAILY:
            e_dialog_radio_set (priv->recurrence_rule_daily, RECUR_OPTION_DAILY,
                        recur_options_map);
            e_dialog_spin_set (priv->recurrence_rule_daily_days,
                       priv->ico->recur->interval);
            break;

        case RECUR_WEEKLY:
            e_dialog_radio_set (priv->recurrence_rule_weekly, RECUR_OPTION_WEEKLY,
                        recur_options_map);
            e_dialog_spin_set (priv->recurrence_rule_weekly_weeks,
                       priv->ico->recur->interval);

            e_dialog_toggle_set (priv->recurrence_rule_weekly_sun,
                         (priv->ico->recur->weekday & (1 << 0)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_mon,
                         (priv->ico->recur->weekday & (1 << 1)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_tue,
                         (priv->ico->recur->weekday & (1 << 2)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_wed,
                         (priv->ico->recur->weekday & (1 << 3)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_thu,
                         (priv->ico->recur->weekday & (1 << 4)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_fri,
                         (priv->ico->recur->weekday & (1 << 5)) != 0);
            e_dialog_toggle_set (priv->recurrence_rule_weekly_sat,
                         (priv->ico->recur->weekday & (1 << 6)) != 0);
            break;

        case RECUR_MONTHLY_BY_DAY:
            e_dialog_radio_set (priv->recurrence_rule_monthly, RECUR_OPTION_MONTHLY,
                        recur_options_map);
            e_dialog_toggle_set (priv->recurrence_rule_monthly_on_day, TRUE);
            e_dialog_spin_set (priv->recurrence_rule_monthly_day_nth,
                       priv->ico->recur->u.month_day);
            e_dialog_spin_set (priv->recurrence_rule_monthly_every_n_months,
                       priv->ico->recur->interval);
            break;

        case RECUR_MONTHLY_BY_POS:
            e_dialog_radio_set (priv->recurrence_rule_monthly, RECUR_OPTION_MONTHLY,
                        recur_options_map);
            e_dialog_toggle_set (priv->recurrence_rule_monthly_weekday, TRUE);
            e_dialog_option_menu_set (priv->recurrence_rule_monthly_week,
                          priv->ico->recur->u.month_pos,
                          month_pos_map);
            e_dialog_option_menu_set (priv->recurrence_rule_monthly_weekpos,
                          priv->ico->recur->weekday,
                          weekday_map);
            e_dialog_spin_set (priv->recurrence_rule_monthly_every_n_months,
                       priv->ico->recur->interval);
            break;

        case RECUR_YEARLY_BY_DAY:
        case RECUR_YEARLY_BY_MONTH:
            e_dialog_radio_set (priv->recurrence_rule_yearly, RECUR_OPTION_YEARLY,
                        recur_options_map);
            e_dialog_spin_set (priv->recurrence_rule_yearly_every_n_years,
                       priv->ico->recur->interval);
            break;

        default:
            g_assert_not_reached ();
        }

        if (priv->ico->recur->_enddate == 0) {
            if (priv->ico->recur->duration == 0)
                e_dialog_toggle_set (priv->recurrence_ending_date_repeat_forever,
                             TRUE);
            else {
                e_dialog_toggle_set (priv->recurrence_ending_date_end_after, TRUE);
                e_dialog_spin_set (priv->recurrence_ending_date_end_after_count,
                           priv->ico->recur->duration);
            }
        } else {
            e_dialog_toggle_set (priv->recurrence_ending_date_end_on, TRUE);
            /* Shorten by one day, as we store end-on date a day ahead */
            /* FIX ME is this correct? */
            e_dialog_dateedit_set (priv->recurrence_ending_date_end_on_date,
                           time_add_day (priv->ico->recur->enddate, -1));
        }
    }

    /* Exception buttons */

    gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_add), "clicked",
                GTK_SIGNAL_FUNC (recurrence_exception_added), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_delete), "clicked",
                GTK_SIGNAL_FUNC (recurrence_exception_deleted), ee);
    gtk_signal_connect (GTK_OBJECT (priv->recurrence_exception_change), "clicked",
                GTK_SIGNAL_FUNC (recurrence_exception_changed), ee);

    /* Exceptions list */

    for (list = priv->ico->exdate; list; list = list->next)
        append_exception (ee, *((time_t *) list->data));
}

static GnomeUIInfo main_menu[] = {
    /* FIXME */
    GNOMEUIINFO_END
};

/* Creates the menu bar for the event editor */
static void
create_menu (EventEditor *ee)
{
    EventEditorPrivate *priv;
    BonoboUIHandlerMenuItem *list;

    priv = ee->priv;

    bonobo_ui_handler_create_menubar (priv->uih);

    list = bonobo_ui_handler_menu_parse_uiinfo_list_with_data (main_menu, ee);
    bonobo_ui_handler_menu_add_list (priv->uih, "/", list);
}

static GnomeUIInfo toolbar[] = {
    /* FIXME */
    GNOMEUIINFO_END
};

/* Creates the toolbar for the event editor */
static void
create_toolbar (EventEditor *ee)
{
    EventEditorPrivate *priv;
    BonoboUIHandlerToolbarItem *list;

    priv = ee->priv;

    bonobo_ui_handler_create_toolbar (priv->uih, "Toolbar");

    list = bonobo_ui_handler_toolbar_parse_uiinfo_list_with_data (toolbar, ee);
    bonobo_ui_handler_toolbar_add_list (priv->uih, "/Toolbar", list);
}

GtkWidget *
event_editor_construct (EventEditor *ee, GnomeCalendar *gcal, iCalObject *ico)
{
    EventEditorPrivate *priv;
    char *title;
    GtkWidget *toplevel;
    GtkWidget *contents;

    g_return_val_if_fail (ee != NULL, NULL);
    g_return_val_if_fail (IS_EVENT_EDITOR (ee), NULL);
    g_return_val_if_fail (gcal != NULL, NULL);
    g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL);
    g_return_val_if_fail (ico != NULL, NULL);
    g_return_val_if_fail (ico->uid != NULL, NULL);

    priv = ee->priv;

    /* Create the UI handler */

    priv->uih = bonobo_ui_handler_new ();
    if (!priv->uih) {
        g_message ("event_editor_construct(): Could not create the UI handler");
        goto error;
    }

    bonobo_ui_handler_set_app (priv->uih, GNOME_APP (ee));

    /* Load the content widgets */

    priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/event-editor-dialog.glade", NULL);
    if (!priv->xml) {
        g_message ("event_editor_construct(): Could not load the Glade XML file!");
        goto error;
    }

    toplevel = glade_xml_get_widget (priv->xml, "event-editor-dialog");
    contents = glade_xml_get_widget (priv->xml, "dialog-contents");
    if (!(toplevel && contents)) {
        g_message ("event_editor_construct(): Could not find the contents in the XML file!");
        goto error;
    }

    priv->gcal = gcal;
    priv->ico = ico;

    if (!get_widgets (ee)) {
        g_message ("event_editor_construct(): Could not find all widgets in the XML file!");
        goto error;
    }

    init_widgets (ee);

    gtk_object_ref (GTK_OBJECT (contents));
    gtk_container_remove (GTK_CONTAINER (toplevel), GTK_WIDGET (contents));
    gtk_widget_destroy (GTK_WIDGET (toplevel));

    /* Construct the app */

    title = make_title_from_ico (ico);
    gnome_app_construct (GNOME_APP (ee), "event-editor", title);
    g_free (title);

    create_menu (ee);
    create_toolbar (ee);

    gnome_app_set_contents (GNOME_APP (ee), contents);
    gtk_widget_show (contents);
    gtk_object_unref (GTK_OBJECT (contents));

    return GTK_WIDGET (ee);

 error:

    gtk_object_unref (GTK_OBJECT (ee));
    return NULL;
}

GtkWidget *
event_editor_new (GnomeCalendar *gcal, iCalObject *ico)
{
    GtkWidget *ee;

    g_return_val_if_fail (gcal != NULL, NULL);
    g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL);
    g_return_val_if_fail (ico != NULL, NULL);
    g_return_val_if_fail (ico->uid != NULL, NULL);

    ee = GTK_WIDGET (gtk_type_new (TYPE_EVENT_EDITOR));

    ee = event_editor_construct (EVENT_EDITOR (ee), gcal, ico);

    if (ee)
        gtk_widget_show (ee);

    return ee;
}

#if 0
/* FIXME: put this logic in whatever code calls the event editor with a new
 * iCalObject.
 */
static void
fill_in_dialog_from_defaults (EventEditorDialog *dialog)
{
    time_t now = time (NULL);
    time_t soon = time_add_minutes (now, 30);

    store_to_editable (dialog->gui, "general-owner", "?");

    /* start and end time */
    store_to_gnome_dateedit (dialog->gui, "start-time", now);
    store_to_gnome_dateedit (dialog->gui, "end-time", soon);
}
#endif

static void
free_exdate (iCalObject *ico)
{
    GList *list;

    if (!ico->exdate)
        return;

    for (list = ico->exdate; list; list = list->next)
        g_free (list->data);

    g_list_free (ico->exdate);
    ico->exdate = NULL;
}

/* Decode the radio button group for classifications */
static ClassificationType
classification_get (GtkWidget *widget)
{
    return e_dialog_radio_get (widget, classification_map);
}

/* Get the values of the widgets in the event editor and put them in the iCalObject */
static void
dialog_to_ical_object (EventEditor *ee)
{
    EventEditorPrivate *priv;
    iCalObject *ico;
    gboolean all_day_event;
    int i;
    time_t *t;
    GtkCList *exception_list;

    priv = ee->priv;
    ico = priv->ico;

    if (ico->summary)
        g_free (ico->summary);

    ico->summary  = e_dialog_editable_get (priv->general_summary);

    ico->dtstart = e_dialog_dateedit_get (priv->start_time);
    ico->dtend = e_dialog_dateedit_get (priv->end_time);

    all_day_event = e_dialog_toggle_get (priv->all_day_event);

    ico->dalarm.enabled = e_dialog_toggle_get (priv->alarm_display);
    ico->aalarm.enabled = e_dialog_toggle_get (priv->alarm_program);
    ico->palarm.enabled = e_dialog_toggle_get (priv->alarm_audio);
    ico->malarm.enabled = e_dialog_toggle_get (priv->alarm_mail);

    ico->dalarm.count = e_dialog_spin_get_int (priv->alarm_display_amount);
    ico->aalarm.count = e_dialog_spin_get_int (priv->alarm_audio_amount);
    ico->palarm.count = e_dialog_spin_get_int (priv->alarm_program_amount);
    ico->malarm.count = e_dialog_spin_get_int (priv->alarm_mail_amount);

    ico->dalarm.units = alarm_unit_get (priv->alarm_display_unit);
    ico->aalarm.units = alarm_unit_get (priv->alarm_audio_unit);
    ico->palarm.units = alarm_unit_get (priv->alarm_program_unit);
    ico->malarm.units = alarm_unit_get (priv->alarm_mail_unit);

    if (ico->palarm.data)
        g_free (ico->palarm.data);

    if (ico->malarm.data)
        g_free (ico->malarm.data);

    ico->palarm.data = e_dialog_editable_get (priv->alarm_program_run_program);
    ico->malarm.data = e_dialog_editable_get (priv->alarm_mail_mail_to);

    if (ico->class)
        g_free (ico->class);

    switch (classification_get (priv->classification_radio)) {
    case CLASSIFICATION_PUBLIC:
        ico->class = g_strdup ("PUBLIC");
        break;

    case CLASSIFICATION_PRIVATE:
        ico->class = g_strdup ("PRIVATE");
        break;

    case CLASSIFICATION_CONFIDENTIAL:
        ico->class = g_strdup ("CONFIDENTIAL");
        break;

    default:
        g_assert_not_reached ();
    }

    /* Recurrence information */

    if (ico->recur) {
        g_free (ico->recur);
        ico->recur = NULL;
    }

    switch (recur_options_get (priv->recurrence_rule_none)) {
    case RECUR_OPTION_NONE:
        /* nothing */
        break;

    case RECUR_OPTION_DAILY:
        ico->recur = g_new0 (Recurrence, 1);
        ico->recur->type = RECUR_DAILY;
        ico->recur->interval = e_dialog_spin_get_int (priv->recurrence_rule_daily_days);
        break;

    case RECUR_OPTION_WEEKLY:
        ico->recur = g_new0 (Recurrence, 1);
        ico->recur->type = RECUR_WEEKLY;
        ico->recur->interval = e_dialog_spin_get_int (priv->recurrence_rule_weekly_weeks);
        ico->recur->weekday = 0;

        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_sun))
            ico->recur->weekday |= 1 << 0;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_mon))
            ico->recur->weekday |= 1 << 1;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_tue))
            ico->recur->weekday |= 1 << 2;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_wed))
            ico->recur->weekday |= 1 << 3;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_thu))
            ico->recur->weekday |= 1 << 4;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_fri))
            ico->recur->weekday |= 1 << 5;
        if (e_dialog_toggle_get (priv->recurrence_rule_weekly_sat))
            ico->recur->weekday |= 1 << 6;

        break;

    case RECUR_OPTION_MONTHLY:
        ico->recur = g_new0 (Recurrence, 1);

        if (e_dialog_toggle_get (priv->recurrence_rule_monthly_on_day)) {
            /* by day of in the month (ex: the 5th) */
            ico->recur->type = RECUR_MONTHLY_BY_DAY;
            ico->recur->u.month_day = e_dialog_spin_get_int (
                priv->recurrence_rule_monthly_day_nth);
        } else if (e_dialog_toggle_get (priv->recurrence_rule_monthly_weekday)) {
            /* "recurrence-rule-monthly-weekday" is TRUE */
            /* by position on the calender (ex: 2nd monday) */
            ico->recur->type = RECUR_MONTHLY_BY_POS;
            ico->recur->u.month_pos = e_dialog_option_menu_get (
                priv->recurrence_rule_monthly_week,
                month_pos_map);
            ico->recur->weekday = e_dialog_option_menu_get (
                priv->recurrence_rule_monthly_weekpos,
                weekday_map);

        } else
            g_assert_not_reached ();

        ico->recur->interval = e_dialog_spin_get_int (
            priv->recurrence_rule_monthly_every_n_months);

        break;

    case RECUR_OPTION_YEARLY:
        ico->recur = g_new0 (Recurrence, 1);
        ico->recur->type = RECUR_YEARLY_BY_DAY;
        ico->recur->interval = e_dialog_spin_get_int (
            priv->recurrence_rule_yearly_every_n_years);

    default:
        g_assert_not_reached ();
    }

    /* recurrence ending date */

    if (e_dialog_toggle_get (priv->recurrence_ending_date_repeat_forever)) {
        ico->recur->_enddate = 0;
        ico->recur->enddate = 0;
        ico->recur->duration = 0;
    } else if (e_dialog_toggle_get (priv->recurrence_ending_date_end_on)) {
        /* Also here, to ensure that the event is used, we add 86400
         * secs to get get next day, in accordance to the RFC
         */
        ico->recur->_enddate = e_dialog_dateedit_get (
            priv->recurrence_ending_date_end_on_date) + 86400;
        ico->recur->enddate = ico->recur->_enddate;
        ico->recur->duration = 0;
    } else if (e_dialog_toggle_get (priv->recurrence_ending_date_end_after)) {
        ico->recur->duration = e_dialog_spin_get_int (
            priv->recurrence_ending_date_end_after_count);
        ical_object_compute_end (ico);
    } else
        g_assert_not_reached ();

    /* Get exceptions from clist into ico->exdate */

    free_exdate (ico);
    exception_list = GTK_CLIST (priv->recurrence_exceptions_list);

    for (i = 0; i < exception_list->rows; i++) {
        t = gtk_clist_get_row_data (exception_list, i);
        ico->exdate = g_list_prepend (ico->exdate, t);
    }
}

static void
ee_ok (GtkWidget *widget, EventEditor *ee)
{
    EventEditorPrivate *priv;

    priv = ee->priv;

    dialog_to_ical_object (ee);

    if (priv->ico->new)
        gnome_calendar_add_object (priv->gcal, priv->ico);
    else
        gnome_calendar_object_changed (priv->gcal, priv->ico);

    priv->ico->new = FALSE;
}

static void
ee_cancel (GtkWidget *widget, EventEditor *ee)
{
    EventEditorPrivate *priv;

    priv = ee->priv;

    if (priv->ico) {
        ical_object_unref (priv->ico);
        priv->ico = NULL;
    }
}

static void
alarm_toggle (GtkWidget *toggle, EventEditor *ee)
{
    EventEditorPrivate *priv;
    GtkWidget *alarm_amount;
    GtkWidget *alarm_unit;
    gboolean active;

    priv = ee->priv;

    active = GTK_TOGGLE_BUTTON (toggle)->active;

    if (toggle == priv->alarm_display) {
        alarm_amount = priv->alarm_display_amount;
        alarm_unit = priv->alarm_display_unit;
    } else if (toggle == priv->alarm_audio) {
        alarm_amount = priv->alarm_audio_amount;
        alarm_unit = priv->alarm_audio_unit;
    } else if (toggle == priv->alarm_program) {
        alarm_amount = priv->alarm_program_amount;
        alarm_unit = priv->alarm_program_unit;
        gtk_widget_set_sensitive (priv->alarm_program_run_program, active);
    } else if (toggle == priv->alarm_mail) {
        alarm_amount = priv->alarm_mail_amount;
        alarm_unit = priv->alarm_mail_unit;
        gtk_widget_set_sensitive (priv->alarm_mail_mail_to, active);
    } else
        g_assert_not_reached ();

    gtk_widget_set_sensitive (alarm_amount, active);
    gtk_widget_set_sensitive (alarm_unit, active);
}

/*
 * Checks if the day range occupies all the day, and if so, check the
 * box accordingly
 */
static void
check_all_day (EventEditor *ee)
{
    EventEditorPrivate *priv;
    time_t ev_start, ev_end;

    priv = ee->priv;

    ev_start = e_dialog_dateedit_get (priv->start_time);
    ev_end = e_dialog_dateedit_get (priv->end_time);

    /* all day event checkbox */
    if (get_time_t_hour (ev_start) <= day_begin &&
        get_time_t_hour (ev_end) >= day_end)
        e_dialog_toggle_set (priv->all_day_event, TRUE);
    else
        e_dialog_toggle_set (priv->all_day_event, FALSE);
}

/*
 * Callback: all day event box clicked
 */
static void
set_all_day (GtkWidget *toggle, EventEditor *ee)
{
    EventEditorPrivate *priv;
    struct tm tm;
    time_t start_t;

    priv = ee->priv;

    start_t = e_dialog_dateedit_get (priv->start_time);
    tm = *localtime (&start_t);
    tm.tm_hour = day_begin;
    tm.tm_min  = 0;
    tm.tm_sec  = 0;
    e_dialog_dateedit_set (priv->start_time, mktime (&tm));

    if (GTK_TOGGLE_BUTTON (toggle)->active)
        tm.tm_hour = day_end;
    else
        tm.tm_hour++;

    e_dialog_dateedit_set (priv->end_time, mktime (&tm));
}

/*
 * Callback: checks that the dates are start < end
 */
static void
check_dates (GnomeDateEdit *gde, EventEditor *ee)
{
    EventEditorPrivate *priv;
    time_t start, end;
    struct tm tm_start, tm_end;

    priv = ee->priv;

    start = e_dialog_dateedit_get (priv->start_time);
    end = e_dialog_dateedit_get (priv->end_time);

    if (start > end) {
        tm_start = *localtime (&start);
        tm_end = *localtime (&end);

        if (GTK_WIDGET (gde) == priv->start_time) {
            tm_end.tm_year = tm_start.tm_year;
            tm_end.tm_mon  = tm_start.tm_mon;
            tm_end.tm_mday = tm_start.tm_mday;

            e_dialog_dateedit_set (priv->end_time, mktime (&tm_end));
        } else if (GTK_WIDGET (gde) == priv->end_time) {
            tm_start.tm_year = tm_end.tm_year;
            tm_start.tm_mon  = tm_end.tm_mon;
            tm_start.tm_mday = tm_end.tm_mday;

/*              e_dialog_dateedit_set (priv->start_time, mktime (&tm_start)); */
        }
    }
}

/*
 * Callback: checks that start_time < end_time and whether the
 * selected hour range spans all of the day
 */
static void
check_times (GnomeDateEdit *gde, EventEditor *ee)
{
    EventEditorPrivate *priv;
    time_t start, end;
    struct tm tm_start, tm_end;

    priv = ee->priv;

    gdk_pointer_ungrab (GDK_CURRENT_TIME);
    gdk_flush ();

    start = e_dialog_dateedit_get (priv->start_time);
    end = e_dialog_dateedit_get (priv->end_time);

    if (start >= end) {
        tm_start = *localtime (&start);
        tm_end = *localtime (&end);

        if (GTK_WIDGET (gde) == priv->start_time) {
            tm_end.tm_min  = tm_start.tm_min;
            tm_end.tm_sec  = tm_start.tm_sec;
            tm_end.tm_hour = tm_start.tm_hour + 1;

            if (tm_end.tm_hour >= 24) {
                tm_end.tm_hour = 24; /* mktime() will bump the day */
                tm_end.tm_min = 0;
                tm_end.tm_sec = 0;
            }

            e_dialog_dateedit_set (priv->end_time, mktime (&tm_end));
        } else if (GTK_WIDGET (gde) == priv->end_time) {
            tm_start.tm_min  = tm_end.tm_min;
            tm_start.tm_sec  = tm_end.tm_sec;
            tm_start.tm_hour = tm_end.tm_hour - 1;

            if (tm_start.tm_hour < 0) {
                tm_start.tm_hour = 0;
                tm_start.tm_min = 0;
                tm_start.tm_min = 0;
            }

            e_dialog_dateedit_set (priv->start_time, mktime (&tm_start));
        }
    }

    /* Check whether the event spans the whole day */

    check_all_day (ee);
}

static void
recurrence_toggled (GtkWidget *radio, EventEditor *ee)
{
    EventEditorPrivate *priv;
    RecurOptions ro;

    priv = ee->priv;

    if (!GTK_TOGGLE_BUTTON (radio)->active)
        return;

    ro = e_dialog_radio_get (radio, recur_options_map);

    gtk_notebook_set_page (GTK_NOTEBOOK (priv->recurrence_rule_notebook), (int) ro);
}


static char *
get_exception_string (time_t t)
{
    static char buf[256];

    strftime (buf, sizeof (buf), _("%a %b %d %Y"), localtime (&t));
    return buf;
}


static void
append_exception (EventEditor *ee, time_t t)
{
    EventEditorPrivate *priv;
    time_t *tt;
    char *c[1];
    int i;
    GtkCList *clist;

    priv = ee->priv;

    c[0] = get_exception_string (t);

    tt = g_new (time_t, 1);
    *tt = t;

    clist = GTK_CLIST (priv->recurrence_exceptions_list);

    i = gtk_clist_append (clist, c);
    gtk_clist_set_row_data (clist, i, tt);
    gtk_clist_select_row (clist, i, 0);

/*      gtk_widget_set_sensitive (ee->recur_ex_vbox, TRUE); */
}


static void
recurrence_exception_added (GtkWidget *widget, EventEditor *ee)
{
    EventEditorPrivate *priv;
    time_t t;

    priv = ee->priv;

    t = e_dialog_dateedit_get (priv->recurrence_exceptions_date);
    append_exception (ee, t);
}


static void
recurrence_exception_deleted (GtkWidget *widget, EventEditor *ee)
{
    EventEditorPrivate *priv;
    GtkCList *clist;
    int sel;

    priv = ee->priv;

    clist = GTK_CLIST (priv->recurrence_exceptions_list);
    if (!clist->selection)
        return;

    sel = GPOINTER_TO_INT (clist->selection->data);

    g_free (gtk_clist_get_row_data (clist, sel)); /* free the time_t stored there */

    gtk_clist_remove (clist, sel);
    if (sel >= clist->rows)
        sel--;

    gtk_clist_select_row (clist, sel, 0);
}


static void
recurrence_exception_changed (GtkWidget *widget, EventEditor *ee)
{
    EventEditorPrivate *priv;
    GtkCList *clist;
    time_t *t;
    int sel;

    priv = ee->priv;

    clist = GTK_CLIST (priv->recurrence_exceptions_list);
    if (!clist->selection)
        return;

    sel = GPOINTER_TO_INT (clist->selection->data);

    t = gtk_clist_get_row_data (clist, sel);
    *t = e_dialog_dateedit_get (priv->recurrence_exceptions_date);

    gtk_clist_set_text (clist, sel, 0, get_exception_string (*t));
}


GtkWidget *
make_date_edit (void)
{
    return date_edit_new (time (NULL), FALSE);
}


GtkWidget *
make_date_edit_with_time (void)
{
    return date_edit_new (time (NULL), TRUE);
}


GtkWidget *
date_edit_new (time_t the_time, int show_time)
{
    return gnome_date_edit_new_flags (the_time,
                      ((show_time ? GNOME_DATE_EDIT_SHOW_TIME : 0)
                       | (am_pm_flag ? 0 : GNOME_DATE_EDIT_24_HR)
                       | (week_starts_on_monday
                          ? GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY
                          : 0)));
}



GtkWidget *
make_spin_button (int val, int low, int high)
{
    GtkAdjustment *adj;
    GtkWidget *spin;

    adj = GTK_ADJUSTMENT (gtk_adjustment_new (val, low, high, 1, 10, 10));
    spin = gtk_spin_button_new (adj, 0.5, 0);
    gtk_widget_set_usize (spin, 60, 0);

    return spin;
}


/* todo

   build some of the recur stuff by hand to take into account
   the start-on-monday preference?

   get the apply button to work right

   make the properties stuff unglobal

   figure out why alarm units aren't sticking between edits

   closing the dialog window with the wm caused a crash
   Gtk-WARNING **: invalid cast from `(unknown)' to `GnomeDialog'
   on line 669:  gnome_dialog_close (GNOME_DIALOG(dialog->dialog));
 */