aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gui/calendar.c
blob: 172ef3390e918a0f1dbe7a71850030817cc185b3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10





                                                                                        
                                                        


                                     
                                     

   
 

                  
                   
                   
                     
                     
                  
                     
                             
                      
 



                       


                                            
                                                 
                                    
 
          
                                                     



                                   
 
                                      



                                        
 


                                                                 

                                                                     


                                             
        


                   








                                                                         
                                                 


          
                                                                            














                                                        
           

                                        











                                                 
                                                                                                                


    

                                                    



                                            
                     

                           
                                                                     
                                                                
                                             
                           

                                         
      











                                                                  
 



                                                                                       
                                             

         

                             
                                    







                                                               
                                                                











                                                                 

                             




                                
                                                                        

                                  
                                                                      

                                
                                                                         

                                   

                                               







                                       





                                                                                                    
                                                                              








                                                                                        
       
                                                                                                




                                                   

                                                                          



                                                                                                  
                 
         
 




                                                                                
                          


       
                                                                                            
 
                                                                                
 
 
       
                                                                                               
 














                                                                                   
 






                                                         
                          




















                                                                          


                                                              







                                                                           









                                                             
                                  
      


                                          
                      


                                                           
                                        


                                         
 

                                    

                            









                                                             
                  


                                                     
                  



                                                 
                    
 
 



























                                                                  

                                              


                      
                                  
                     
        
                                                                     
                               


                                                                                 
                         
                                                        


                                                       


                                                    


                                               




                                                                      









                                                                      






                                          

                       
                       

                   



                                                    
                                   
                                                                   






                                                 
 

                              










                                                                           
                                                                                 

                                     
        
                             
                       

 
















                                                    
           
                                                                   
 

                                     





                                                    
          





                                                                        
 



                                                                             

                 







                                      
                                 










                                                                      



                                                                   
                                    

                                                





                                                                 
 
                                     
 



                                                                      


                                
                            

                                   




                                                                  










                                                                




















                                                                          
                                                          
































































                                                                           
                                                          







                                                
/*
 * Calendar manager object
 *
 * This keeps track of a given calendar.  Eventually this will abtract everything
 * related to getting calendars/saving calendars locally or to a remote Calendar Service
 *
 * Copyright (C) 1998, 1999 the Free Software Foundation
 *
 * Authors:
 *   Miguel de Icaza (miguel@gnu.org)
 *   Federico Mena (quartic@gimp.org)
 *
 */

#include <gnome.h>
#include <stdio.h>
#include <config.h>
#include <unistd.h>
#include <sys/stat.h>
#include "calendar.h"
#include "alarm.h"
#include "timeutil.h"
#include "../libversit/vcc.h"
#include "icalendar.h"

#ifdef HAVE_TZNAME
extern char *tzname[2];
#endif

/* Our day range */
time_t calendar_day_begin, calendar_day_end;

static void calendar_init_alarms (Calendar *cal);
static void calendar_set_day (void);

Calendar *
calendar_new (char *title,CalendarNewOptions options)
{
    Calendar *cal;

    cal = g_new0 (Calendar, 1);

    cal->title = g_strdup (title);
    if (options & CALENDAR_USE_ICAL)
      cal->format = CAL_ICAL;
    else
      cal->format = CAL_VCAL;

    if ((calendar_day_begin == 0) || (calendar_day_end == 0))
        calendar_set_day ();

    cal->event_hash = g_hash_table_new (g_str_hash, g_str_equal);
    
    if (options & CALENDAR_INIT_ALARMS) {
        calendar_init_alarms (cal);
    }
    
    return cal;
}

static void
try_add (iCalObject *ico, CalendarAlarm *alarm, time_t start, time_t end)
{
    alarm->trigger = start-alarm->offset;
    
    if (alarm->trigger < calendar_day_begin)
        return;
    if (alarm->trigger > calendar_day_end)
        return;
    alarm_add (alarm, &calendar_notify, ico);
}

static int
add_object_alarms (iCalObject *obj, time_t start, time_t end, void *closure)
{
    if (obj->aalarm.enabled)
        try_add (obj, &obj->aalarm, start, end);
    if (obj->dalarm.enabled)
        try_add (obj, &obj->dalarm, start, end);
    if (obj->palarm.enabled)
        try_add (obj,&obj->palarm, start, end);
    if (obj->malarm.enabled)
        try_add (obj, &obj->malarm, start, end);
    
    return TRUE;
}

#define max(a,b) ((a > b) ? a : b)

static void
ical_object_try_alarms (iCalObject *obj)
{
    int ao, po, od, mo;
    int max_o;
    
    ao = alarm_compute_offset (&obj->aalarm);
    po = alarm_compute_offset (&obj->palarm);
    od = alarm_compute_offset (&obj->dalarm);
    mo = alarm_compute_offset (&obj->malarm);
    
    max_o = max (ao, max (po, max (od, mo)));
    if (max_o == -1)
        return;
    
    ical_object_generate_events (obj, calendar_day_begin, calendar_day_end + max_o, add_object_alarms, obj);
}

void
calendar_add_object (Calendar *cal, iCalObject *obj)
{
    g_return_if_fail (cal != NULL);
    g_return_if_fail (obj != NULL);
    g_return_if_fail (obj->uid != NULL);
    
    obj->new = 0;
    switch (obj->type){
    case ICAL_EVENT:
        g_hash_table_insert (cal->event_hash, obj->uid, obj);
        cal->events = g_list_prepend (cal->events, obj);
        ical_object_try_alarms (obj);
#ifdef DEBUGGING_MAIL_ALARM
        obj->malarm.trigger = 0;
        calendar_notify (0, obj);
#endif
        break;

    case ICAL_TODO:
        cal->todo = g_list_prepend (cal->todo, obj);
        break;

    case ICAL_JOURNAL:
        cal->journal = g_list_prepend (cal->journal, obj);
        break;
    default:
        g_assert_not_reached ();
    }

    if (!obj->uid){
        char buffer [80];

        snprintf (buffer, sizeof (buffer), "GnomeCalendar-%ld\n", time (NULL));
        obj->uid = g_strdup (buffer);
    }
    
    cal->modified = TRUE;

    obj->last_mod = time (NULL);
}

void
calendar_remove_object (Calendar *cal, iCalObject *obj)
{
    switch (obj->type){
    case ICAL_EVENT:
        cal->events = g_list_remove (cal->events, obj);
        g_hash_table_remove (cal->event_hash, obj->uid);
        break;

    case ICAL_TODO:
        cal->todo = g_list_remove (cal->todo, obj);
        break;

    case ICAL_JOURNAL:
        cal->journal = g_list_remove (cal->journal, obj);
        break;
    default:
        g_assert_not_reached ();
    }

    cal->modified = TRUE;
}

void
calendar_destroy (Calendar *cal)
{
    g_list_foreach (cal->events, (GFunc) ical_object_destroy, NULL);
    g_list_free (cal->events);
    
    g_list_foreach (cal->todo, (GFunc) ical_object_destroy, NULL);
    g_list_free (cal->todo);
    
    g_list_foreach (cal->journal, (GFunc) ical_object_destroy, NULL);
    g_list_free (cal->journal);

    g_hash_table_destroy (cal->event_hash);
    
    if (cal->title)
        g_free (cal->title);
    if (cal->filename)
        g_free (cal->filename);
    
    g_free (cal);
}

void
calendar_iterate_on_objects (GList *objects, time_t start, time_t end, calendarfn cb, void *closure)
{
    for (; objects; objects = objects->next){
        iCalObject *object = objects->data;

        ical_object_generate_events (object, start, end, cb, closure);
    }
}

void
calendar_iterate (Calendar *cal, time_t start, time_t end, calendarfn cb, void *closure)
{
    calendar_iterate_on_objects (cal->events, start, end, cb, closure);
}

GList *
calendar_get_objects_in_range (GList *objects, time_t start, time_t end, GCompareFunc sort_func)
{
    GList *new_events = 0;

    for (; objects; objects = objects->next){
        iCalObject *object = objects->data;

        if ((start <= object->dtstart) && (object->dtend <= end)){
            if (sort_func)
                new_events = g_list_insert_sorted (new_events, object, sort_func);
            else
                new_events = g_list_prepend (new_events, object);
        }
    }

    /* Put the list in increasing order if no sort function was specified */

    if (!sort_func)
        new_events = g_list_reverse (new_events);

    return new_events;
}

GList *
calendar_get_todo_in_range (Calendar *cal, time_t start, time_t end, GCompareFunc sort_func)
{
    return calendar_get_objects_in_range (cal->todo, start, end, sort_func);
}

GList *
calendar_get_journal_in_range (Calendar *cal, time_t start, time_t end, GCompareFunc sort_func)
{
    return calendar_get_objects_in_range (cal->journal, start, end, sort_func);
}

gint
calendar_compare_by_dtstart (gpointer a, gpointer b)
{
    iCalObject *obj1, *obj2;
    time_t diff;

    obj1 = a;
    obj2 = b;

    diff = obj1->dtstart - obj2->dtstart;

    return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}

#define str_val(obj) (char *) vObjectUStringZValue (obj)

/* Loads our calendar contents from a vObject */
void
calendar_load_from_vobject (Calendar *cal, VObject *vcal)
{
    VObjectIterator i;
    
    initPropIterator (&i, vcal);

    while (moreIteration (&i)){
        VObject *this = nextVObject (&i);
        iCalObject *ical;
        const char *object_name = vObjectName (this);

        if (strcmp (object_name, VCDCreatedProp) == 0){
            cal->created = time_from_isodate (str_val (this));
            continue;
        }
        
        if (strcmp (object_name, VCLocationProp) == 0)
            continue; /* FIXME: imlement */

        if (strcmp (object_name, VCProdIdProp) == 0)
            continue; /* FIXME: implement */

        if (strcmp (object_name, VCVersionProp) == 0)
            continue; /* FIXME: implement */

        if (strcmp (object_name, VCTimeZoneProp) == 0)
            continue; /* FIXME: implement */
        
        ical = ical_object_create_from_vobject (this, object_name);

        if (ical)
            calendar_add_object (cal, ical);
    }
}

static void
calendar_set_day (void)
{
    time_t calendar_today;
    
    calendar_today     = time (NULL);
    calendar_day_begin = time_day_begin (calendar_today);
    calendar_day_end   = time_day_end (calendar_today);
}

/* Loads a calendar from a file */
char *
calendar_load (Calendar *cal, char *fname)
{
    VObject *vcal;
    struct stat s;
    
    if (cal->filename){
        g_warning ("Calendar load called again\n");
        return "Internal error";
    }

    cal->filename = g_strdup (fname);

    stat (fname, &s);
    cal->file_time = s.st_mtime;

    calendar_set_day ();

    switch (cal->format) {
    case CAL_VCAL:
        vcal = Parse_MIME_FromFileName (fname);
        if (!vcal)
            return "Could not load the calendar";
        calendar_load_from_vobject (cal, vcal);
        cleanVObject (vcal);
        cleanStrTbl ();
        break;
        /*
    case CAL_ICAL:
        icalendar_calendar_load (cal, fname);
        break;
        */
    default:
        return "Unknown calendar format";
    }

    return NULL;
}

/*
 * calendar_load_from_memory:
 * @cal: calendar on which we load the information
 * @buffer: A buffer that contains a vCalendar file
 *
 * Loads the information from the vCalendar information in @buffer
 * into the Calendar
 */
char *
calendar_load_from_memory (Calendar *cal, const char *buffer)
{
    VObject *vcal;
    
    g_return_val_if_fail (buffer != NULL, NULL);
    
    cal->filename = g_strdup ("memory-based-calendar");
    vcal = Parse_MIME (buffer, strlen (buffer));
    if (!vcal)
        return "Could not load the calendar";

    cal->file_time = time (NULL);
    calendar_load_from_vobject (cal, vcal);
    cleanVObject (vcal);
    cleanStrTbl ();

    return NULL;
}

static VObject *
vcalendar_create_from_calendar (Calendar *cal)
{
    VObject *vcal;
    GList   *l;
    time_t  now = time (NULL);
    struct tm tm;
    
    /* WE call localtime for the side effect of setting tzname */
    tm = *localtime (&now);
    
    vcal = newVObject (VCCalProp);
    addPropValue (vcal, VCProdIdProp, "-//GNOME//NONSGML GnomeCalendar//EN");
#if defined(HAVE_TM_ZONE)
    addPropValue (vcal, VCTimeZoneProp, tm.tm_zone);
#elif defined(HAVE_TZNAME)
    addPropValue (vcal, VCTimeZoneProp, tzname[0]);
#endif
    addPropValue (vcal, VCVersionProp, VERSION);
    cal->temp = vcal;

    /* Events */

    for (l = cal->events; l; l = l->next) {
        VObject *obj;
            
        obj = ical_object_to_vobject ((iCalObject *) l->data);
        addVObjectProp (vcal, obj);
    }

    /* To-do entries */

    for (l = cal->todo; l; l = l->next) {
        VObject *obj;

        obj = ical_object_to_vobject ((iCalObject *) l->data);
        addVObjectProp (vcal, obj);
    }

    return vcal;
}

void
calendar_save (Calendar *cal, char *fname)
{
    VObject *vcal;
    FILE *fp;
    GtkWidget *dlg;
    struct  stat s;
    int status;
    
    if (fname == NULL)
        fname = cal->filename;

    vcal = vcalendar_create_from_calendar (cal);
    if (g_file_exists (fname)){
        char *backup_name = g_strconcat (fname, "~", NULL);

        if (g_file_exists (backup_name)){
            unlink (backup_name);
        }
        rename (fname, backup_name);
        g_free (backup_name);
    }

    fp = fopen(fname,"w");
    if (fp) {
        writeVObject(fp, vcal);
        fclose(fp);
        if (strcmp(cal->filename, fname)) {
            if (cal->filename)
                g_free (cal->filename);
            cal->filename = g_strdup (fname);
        }
        status = stat (fname, &s);
        cal->file_time = s.st_mtime;
    } else {
        dlg = gnome_message_box_new(_("Failed to save calendar!"), 
                        GNOME_MESSAGE_BOX_ERROR, "Ok", NULL);
        gtk_widget_show(dlg);
    }
    
    cleanVObject (vcal); 
    cleanStrTbl ();
}

char *
calendar_get_as_vcal_string (Calendar *cal)
{
    VObject *vcal;
    char *result;
    
    g_return_val_if_fail (cal != NULL, NULL);

    vcal = vcalendar_create_from_calendar (cal);
    result = writeMemVObject (NULL, 0, vcal);

    cleanVObject (vcal);
    cleanStrTbl ();

    return result;
}

static gint
calendar_object_compare_by_start (gconstpointer a, gconstpointer b)
{
    const CalendarObject *ca = a;
    const CalendarObject *cb = b;
    time_t diff;
    
    diff = ca->ev_start - cb->ev_start;
    return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}

static int
assemble_event_list (iCalObject *obj, time_t start, time_t end, void *c)
{
    CalendarObject *co;
    GList **l = c;
    
    co = g_new (CalendarObject, 1);

    co->ev_start = start;
    co->ev_end   = end;
    co->ico      = obj;
    *l = g_list_insert_sorted (*l, co, calendar_object_compare_by_start);

    return 1;
}

void
calendar_destroy_event_list (GList *l)
{
    GList *p;

    for (p = l; p; p = p->next)
        g_free (p->data);
    g_list_free (l);
}

GList *
calendar_get_events_in_range (Calendar *cal, time_t start, time_t end)
{
    GList *l = 0;
    
    calendar_iterate (cal, start, end, assemble_event_list, &l);
    return l;
}

void
calendar_object_changed (Calendar *cal, iCalObject *obj, int flags)
{
    obj->last_mod = time (NULL);
    obj->pilot_status = ICAL_PILOT_SYNC_MOD;

    if (!(flags & CHANGE_DATES))
        return;
    
    /* Remove any alarms on the alarm list for this object */
    while (alarm_kill (obj))
        ;

    ical_object_try_alarms (obj);
}

static void
calendar_day_change (time_t time, CalendarAlarm *which, void *closure)
{
    GList *events;
    Calendar *cal = closure;
    
    calendar_set_day ();
    calendar_init_alarms (cal);
    
    for (events = cal->events; events; events = events->next){
        iCalObject *obj = events->data;

        ical_object_try_alarms (obj);
    }
}

static void
calendar_init_alarms (Calendar *cal)
{
    CalendarAlarm day_change_alarm;

    day_change_alarm.trigger = calendar_day_end;
    alarm_add (&day_change_alarm, calendar_day_change, cal);
}

static iCalObject *
calendar_object_find_in_list (Calendar *cal, GList *list, const char *uid)
{
    GList *l;

    for (l = list; l; l = l->next){
        iCalObject *obj = l->data;

        if (strcmp (obj->uid, uid) == 0)
            return obj;
    }

    return NULL;
}

iCalObject *
calendar_object_find_event (Calendar *cal, const char *uid)
{
    g_return_val_if_fail (cal != NULL, NULL);
    g_return_val_if_fail (uid != NULL, NULL);

    return g_hash_table_lookup (cal->event_hash, uid);
}

iCalObject *
calendar_object_find_todo (Calendar *cal, const char *uid)
{
    g_return_val_if_fail (cal != NULL, NULL);
    g_return_val_if_fail (uid != NULL, NULL);

    return calendar_object_find_in_list (cal, cal->todo, uid);
}

iCalObject *
calendar_object_find (Calendar *cal, const char *uid)
{
    iCalObject *obj;
    
    g_return_val_if_fail (cal != NULL, NULL);
    g_return_val_if_fail (uid != NULL, NULL);

    obj = calendar_object_find_in_list (cal, cal->todo, uid);

    if (obj == NULL)
        obj = calendar_object_find_in_list (cal, cal->events, uid);

    return obj;
}

iCalObject *
calendar_object_find_by_pilot (Calendar *cal, int pilot_id)
{
    GList *l;
    
    g_return_val_if_fail (cal != NULL, NULL);

    for (l = cal->events; l; l = l->next){
        iCalObject *obj = l->data;

        if (obj->pilot_id == pilot_id)
            return obj;
    }

    for (l = cal->todo; l; l = l->next){
        iCalObject *obj = l->data;

        if (obj->pilot_id == pilot_id)
            return obj;
    }

    return NULL;
}

/*
 * calendar_string_from_object:
 *
 * Returns the iCalObject @object armored around a vCalendar
 * object as a string.
 */
char *
calendar_string_from_object (iCalObject *object)
{
    Calendar *cal;
    char *str;

    g_return_val_if_fail (object != NULL, NULL);
    
    cal = calendar_new ("Temporal",CALENDAR_INIT_NIL);
    calendar_add_object (cal, object);
    str = calendar_get_as_vcal_string (cal);
    calendar_remove_object (cal, object);

    calendar_destroy (cal);
    
    return str;
}