/* * 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 #include #include #include #include #include "calendar.h" #include "alarm.h" #include "timeutil.h" #include "../libversit/vcc.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; #ifdef HAVE_LIBICAL hi; case CAL_ICAL: icalendar_calendar_load (cal, fname); break; #endif 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; }