/* * Evolution calendar - Main calendar view widget * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Miguel de Icaza * Federico Mena-Quintero * Seth Alves * Rodrigo Moya * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include "gnome-cal.h" #include #include #include #include #include #include #include "shell/e-shell.h" #include "dialogs/delete-error.h" #include "dialogs/event-editor.h" #include "calendar-config.h" #include "calendar-view.h" #include "comp-util.h" #include "e-cal-list-view.h" #include "e-cal-model-calendar.h" #include "e-day-view-time-item.h" #include "e-day-view.h" #include "e-memo-table.h" #include "e-month-view.h" #include "e-task-table.h" #include "e-week-view.h" #include "ea-calendar.h" #include "misc.h" #include "tag-calendar.h" #define d(x) #define GNOME_CALENDAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), GNOME_TYPE_CALENDAR, GnomeCalendarPrivate)) typedef struct _ViewData ViewData; /* Private part of the GnomeCalendar structure */ struct _GnomeCalendarPrivate { ESourceRegistry *registry; ECalModel *model; /* * Fields for the calendar view */ /* This is the last time explicitly selected by the user */ time_t base_view_time; /* Widgets */ GtkWidget *hpane; ECalendar *date_navigator; GtkWidget *memo_table; /* EMemoTable, but can be NULL */ GtkWidget *task_table; /* ETaskTable, but can be NULL */ GHashTable *date_nav_view_data; /* set of ViewData */ GMutex date_nav_view_data_lock; gchar *sexp; guint update_timeout; guint update_marcus_bains_line_timeout; /* This is the view currently shown. We use it to keep track of the * positions of the panes. range_selected is TRUE if a range of dates * was selected in the date navigator to show the view. */ ECalendarView *views[GNOME_CAL_LAST_VIEW]; GnomeCalendarViewType current_view_type; gboolean range_selected; /* These are the saved positions of the panes. They are multiples of * calendar month widths & heights in the date navigator, so that they * will work OK after theme changes. */ gint hpane_pos; gint hpane_pos_month_view; /* The signal handler id for our GtkCalendar "day_selected" handler. */ guint day_selected_id; /* The dates currently shown. If they are -1 then we have no dates * shown. We only use these to check if we need to emit a * 'dates-shown-changed' signal.*/ time_t visible_start; time_t visible_end; gboolean updating; /* If this is true, list view uses range of showing the events * as the dates selected in date navigator which is one month, * else it uses the date range set in search bar. */ gboolean lview_select_daten_range; GCancellable *cancellable; }; struct _ViewData { volatile gint ref_count; GWeakRef gcal_weak_ref; GCancellable *cancellable; ECalClientView *client_view; gulong objects_added_handler_id; gulong objects_modified_handler_id; gulong objects_removed_handler_id; gulong complete_handler_id; }; enum { PROP_0, PROP_DATE_NAVIGATOR, PROP_MEMO_TABLE, PROP_REGISTRY, PROP_TASK_TABLE, PROP_VIEW }; enum { DATES_SHOWN_CHANGED, CALENDAR_SELECTION_CHANGED, GOTO_DATE, SOURCE_ADDED, SOURCE_REMOVED, CHANGE_VIEW, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void gnome_calendar_do_dispose (GObject *object); static void gnome_calendar_finalize (GObject *object); static void gnome_calendar_goto_date (GnomeCalendar *gcal, GnomeCalendarGotoDateType goto_date); static void gnome_calendar_update_date_navigator (GnomeCalendar *gcal); static void update_task_and_memo_views (GnomeCalendar *gcal); G_DEFINE_TYPE (GnomeCalendar, gnome_calendar, G_TYPE_OBJECT) static ViewData * view_data_new (GnomeCalendar *gcal) { ViewData *view_data; view_data = g_slice_new0 (ViewData); view_data->ref_count = 1; view_data->cancellable = g_cancellable_new (); g_weak_ref_set (&view_data->gcal_weak_ref, gcal); return view_data; } static ViewData * view_data_ref (ViewData *view_data) { g_return_val_if_fail (view_data != NULL, NULL); g_return_val_if_fail (view_data->ref_count > 0, NULL); g_atomic_int_inc (&view_data->ref_count); return view_data; } static void view_data_unref (ViewData *view_data) { g_return_if_fail (view_data != NULL); g_return_if_fail (view_data->ref_count > 0); if (g_atomic_int_dec_and_test (&view_data->ref_count)) { if (view_data->objects_added_handler_id > 0) g_signal_handler_disconnect ( view_data->client_view, view_data->objects_added_handler_id); if (view_data->objects_modified_handler_id > 0) g_signal_handler_disconnect ( view_data->client_view, view_data->objects_modified_handler_id); if (view_data->objects_removed_handler_id > 0) g_signal_handler_disconnect ( view_data->client_view, view_data->objects_removed_handler_id); if (view_data->complete_handler_id > 0) g_signal_handler_disconnect ( view_data->client_view, view_data->complete_handler_id); g_weak_ref_set (&view_data->gcal_weak_ref, NULL); g_cancellable_cancel (view_data->cancellable); g_clear_object (&view_data->cancellable); g_clear_object (&view_data->client_view); g_slice_free (ViewData, view_data); } } static void date_nav_view_data_insert (GnomeCalendar *gcal, ViewData *view_data) { g_return_if_fail (view_data != NULL); g_mutex_lock (&gcal->priv->date_nav_view_data_lock); g_hash_table_add ( gcal->priv->date_nav_view_data, view_data_ref (view_data)); g_mutex_unlock (&gcal->priv->date_nav_view_data_lock); } static void date_nav_view_data_remove_all (GnomeCalendar *gcal) { g_mutex_lock (&gcal->priv->date_nav_view_data_lock); g_hash_table_remove_all (gcal->priv->date_nav_view_data); g_mutex_unlock (&gcal->priv->date_nav_view_data_lock); } static void gcal_update_status_message (GnomeCalendar *gcal, const gchar *message, gdouble percent) { ECalModel *model; g_return_if_fail (gcal != NULL); model = gnome_calendar_get_model (gcal); g_return_if_fail (model != NULL); e_cal_model_update_status_message (model, message, percent); } static void update_adjustment (GnomeCalendar *gcal, GtkAdjustment *adjustment, EWeekView *week_view) { GDate date; GDate first_day_shown; ECalModel *model; gint week_offset; struct icaltimetype start_tt = icaltime_null_time (); time_t lower; guint32 old_first_day_julian, new_first_day_julian; icaltimezone *timezone; gdouble value; e_week_view_get_first_day_shown (week_view, &first_day_shown); /* If we don't have a valid date set yet, just return. */ if (!g_date_valid (&first_day_shown)) return; value = gtk_adjustment_get_value (adjustment); /* Determine the first date shown. */ date = week_view->base_date; week_offset = floor (value + 0.5); g_date_add_days (&date, week_offset * 7); /* Convert the old & new first days shown to julian values. */ old_first_day_julian = g_date_get_julian (&first_day_shown); new_first_day_julian = g_date_get_julian (&date); /* If we are already showing the date, just return. */ if (old_first_day_julian == new_first_day_julian) return; /* Convert it to a time_t. */ start_tt.year = g_date_get_year (&date); start_tt.month = g_date_get_month (&date); start_tt.day = g_date_get_day (&date); model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); lower = icaltime_as_timet_with_zone (start_tt, timezone); e_week_view_set_update_base_date (week_view, FALSE); gnome_calendar_set_selected_time_range (gcal, lower); e_week_view_set_update_base_date (week_view, TRUE); } static void week_view_adjustment_changed_cb (GtkAdjustment *adjustment, GnomeCalendar *gcal) { ECalendarView *view; view = gnome_calendar_get_calendar_view (gcal, GNOME_CAL_WEEK_VIEW); update_adjustment (gcal, adjustment, E_WEEK_VIEW (view)); } static void month_view_adjustment_changed_cb (GtkAdjustment *adjustment, GnomeCalendar *gcal) { ECalendarView *view; view = gnome_calendar_get_calendar_view (gcal, GNOME_CAL_MONTH_VIEW); update_adjustment (gcal, adjustment, E_WEEK_VIEW (view)); } static void view_selection_changed_cb (GnomeCalendar *gcal) { g_signal_emit (gcal, signals[CALENDAR_SELECTION_CHANGED], 0); } static void view_progress_cb (ECalModel *model, const gchar *message, gint percent, ECalClientSourceType type, GnomeCalendar *gcal) { gcal_update_status_message (gcal, message, percent); } static void view_complete_cb (ECalModel *model, const GError *error, ECalClientSourceType type, GnomeCalendar *gcal) { gcal_update_status_message (gcal, NULL, -1); } static void gnome_calendar_notify_week_start_day_cb (GnomeCalendar *gcal) { time_t start_time; start_time = gcal->priv->base_view_time; gnome_calendar_set_selected_time_range (gcal, start_time); } static void gnome_calendar_update_time_range (GnomeCalendar *gcal) { time_t start_time; start_time = gcal->priv->base_view_time; gnome_calendar_set_selected_time_range (gcal, start_time); } static void gnome_calendar_set_registry (GnomeCalendar *gcal, ESourceRegistry *registry) { g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); g_return_if_fail (gcal->priv->registry == NULL); gcal->priv->registry = g_object_ref (registry); } static void gnome_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DATE_NAVIGATOR: gnome_calendar_set_date_navigator ( GNOME_CALENDAR (object), g_value_get_object (value)); return; case PROP_MEMO_TABLE: gnome_calendar_set_memo_table ( GNOME_CALENDAR (object), g_value_get_object (value)); return; case PROP_REGISTRY: gnome_calendar_set_registry ( GNOME_CALENDAR (object), g_value_get_object (value)); return; case PROP_TASK_TABLE: gnome_calendar_set_task_table ( GNOME_CALENDAR (object), g_value_get_object (value)); return; case PROP_VIEW: gnome_calendar_set_view ( GNOME_CALENDAR (object), g_value_get_int (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void gnome_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DATE_NAVIGATOR: g_value_set_object ( value, gnome_calendar_get_date_navigator ( GNOME_CALENDAR (object))); return; case PROP_MEMO_TABLE: g_value_set_object ( value, gnome_calendar_get_memo_table ( GNOME_CALENDAR (object))); return; case PROP_REGISTRY: g_value_set_object ( value, gnome_calendar_get_registry ( GNOME_CALENDAR (object))); return; case PROP_TASK_TABLE: g_value_set_object ( value, gnome_calendar_get_task_table ( GNOME_CALENDAR (object))); return; case PROP_VIEW: g_value_set_int ( value, gnome_calendar_get_view ( GNOME_CALENDAR (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void gnome_calendar_constructed (GObject *object) { GnomeCalendar *gcal = GNOME_CALENDAR (object); ECalendarView *calendar_view; ESourceRegistry *registry; ECalModel *model; GtkAdjustment *adjustment; registry = gnome_calendar_get_registry (gcal); /* Create the model for the views. */ model = e_cal_model_calendar_new (registry); e_cal_model_set_flags (model, E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES); gcal->priv->model = model; g_signal_connect ( model, "cal-view-progress", G_CALLBACK (view_progress_cb), gcal); g_signal_connect ( model, "cal-view-complete", G_CALLBACK (view_complete_cb), gcal); /* Day View */ calendar_view = e_day_view_new (model); e_calendar_view_set_calendar (calendar_view, gcal); gcal->priv->views[GNOME_CAL_DAY_VIEW] = calendar_view; g_object_ref_sink (calendar_view); g_signal_connect_swapped ( calendar_view, "selection-changed", G_CALLBACK (view_selection_changed_cb), gcal); /* Work Week View */ calendar_view = e_day_view_new (model); e_day_view_set_work_week_view (E_DAY_VIEW (calendar_view), TRUE); e_day_view_set_days_shown (E_DAY_VIEW (calendar_view), 5); e_calendar_view_set_calendar (calendar_view, gcal); gcal->priv->views[GNOME_CAL_WORK_WEEK_VIEW] = calendar_view; g_object_ref_sink (calendar_view); g_signal_connect_swapped ( calendar_view, "notify::working-day-monday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-tuesday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-wednesday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-thursday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-friday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-saturday", G_CALLBACK (gnome_calendar_update_time_range), gcal); g_signal_connect_swapped ( calendar_view, "notify::working-day-sunday", G_CALLBACK (gnome_calendar_update_time_range), gcal); /* Week View */ calendar_view = e_week_view_new (model); e_calendar_view_set_calendar (calendar_view, gcal); gcal->priv->views[GNOME_CAL_WEEK_VIEW] = calendar_view; g_object_ref_sink (calendar_view); g_signal_connect_swapped ( calendar_view, "selection-changed", G_CALLBACK (view_selection_changed_cb), gcal); adjustment = gtk_range_get_adjustment ( GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar)); g_signal_connect ( adjustment, "value-changed", G_CALLBACK (week_view_adjustment_changed_cb), gcal); /* Month View */ calendar_view = e_month_view_new (model); e_week_view_set_multi_week_view (E_WEEK_VIEW (calendar_view), TRUE); e_week_view_set_weeks_shown (E_WEEK_VIEW (calendar_view), 6); e_calendar_view_set_calendar (calendar_view, gcal); gcal->priv->views[GNOME_CAL_MONTH_VIEW] = calendar_view; g_object_ref_sink (calendar_view); g_signal_connect_swapped ( calendar_view, "selection-changed", G_CALLBACK (view_selection_changed_cb), gcal); adjustment = gtk_range_get_adjustment ( GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar)); g_signal_connect ( adjustment, "value-changed", G_CALLBACK (month_view_adjustment_changed_cb), gcal); /* List View */ calendar_view = e_cal_list_view_new (model); e_calendar_view_set_calendar (calendar_view, gcal); gcal->priv->views[GNOME_CAL_LIST_VIEW] = calendar_view; g_object_ref_sink (calendar_view); g_signal_connect_swapped ( calendar_view, "selection-changed", G_CALLBACK (view_selection_changed_cb), gcal); g_signal_connect_swapped ( model, "notify::week-start-day", G_CALLBACK (gnome_calendar_notify_week_start_day_cb), gcal); gnome_calendar_goto_today (gcal); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (gnome_calendar_parent_class)->constructed (object); } /* Class initialization function for the gnome calendar */ static void gnome_calendar_class_init (GnomeCalendarClass *class) { GObjectClass *object_class; GtkBindingSet *binding_set; g_type_class_add_private (class, sizeof (GnomeCalendarPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = gnome_calendar_set_property; object_class->get_property = gnome_calendar_get_property; object_class->constructed = gnome_calendar_constructed; object_class->dispose = gnome_calendar_do_dispose; object_class->finalize = gnome_calendar_finalize; class->dates_shown_changed = NULL; class->calendar_selection_changed = NULL; class->source_added = NULL; class->source_removed = NULL; class->goto_date = gnome_calendar_goto_date; class->change_view = gnome_calendar_set_view; g_object_class_install_property ( object_class, PROP_DATE_NAVIGATOR, g_param_spec_object ( "date-navigator", "Date Navigator", NULL, E_TYPE_CALENDAR, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_MEMO_TABLE, g_param_spec_object ( "memo-table", "Memo table", NULL, E_TYPE_MEMO_TABLE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_REGISTRY, g_param_spec_object ( "registry", "Registry", "Data source registry", E_TYPE_SOURCE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TASK_TABLE, g_param_spec_object ( "task-table", "Task table", NULL, E_TYPE_TASK_TABLE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_VIEW, g_param_spec_int ( "view", "View", NULL, GNOME_CAL_DAY_VIEW, GNOME_CAL_LIST_VIEW, GNOME_CAL_DAY_VIEW, G_PARAM_READWRITE)); signals[DATES_SHOWN_CHANGED] = g_signal_new ( "dates_shown_changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GnomeCalendarClass, dates_shown_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CALENDAR_SELECTION_CHANGED] = g_signal_new ( "calendar_selection_changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GnomeCalendarClass, calendar_selection_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[SOURCE_ADDED] = g_signal_new ( "source_added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GnomeCalendarClass, source_added), NULL, NULL, e_marshal_VOID__INT_OBJECT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_OBJECT); signals[SOURCE_REMOVED] = g_signal_new ( "source_removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GnomeCalendarClass, source_removed), NULL, NULL, e_marshal_VOID__INT_OBJECT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_OBJECT); signals[GOTO_DATE] = g_signal_new ( "goto_date", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GnomeCalendarClass, goto_date), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); signals[CHANGE_VIEW] = g_signal_new ( "change_view", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GnomeCalendarClass, change_view), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); /* * Key bindings */ binding_set = gtk_binding_set_new (G_OBJECT_CLASS_NAME (class)); /* Alt+PageUp/PageDown, go to the first/last day of the month */ gtk_binding_entry_add_signal ( binding_set, GDK_KEY_Page_Up, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_FIRST_DAY_OF_MONTH); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_KP_Page_Up, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_FIRST_DAY_OF_MONTH); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_Page_Down, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_LAST_DAY_OF_MONTH); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_KP_Page_Down, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_LAST_DAY_OF_MONTH); /* Alt+Home/End, go to the first/last day of the week */ gtk_binding_entry_add_signal ( binding_set, GDK_KEY_Home, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_FIRST_DAY_OF_WEEK); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_End, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_LAST_DAY_OF_WEEK); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_KP_Home, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_FIRST_DAY_OF_WEEK); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_KP_End, GDK_MOD1_MASK, "goto_date", 1, G_TYPE_ENUM, GNOME_CAL_GOTO_LAST_DAY_OF_WEEK); /*Alt+Left/Right, go to the same day of the previous/next week*/ gtk_binding_entry_add_signal ( binding_set,GDK_KEY_Left, GDK_MOD1_MASK, "goto_date",1, G_TYPE_ENUM, GNOME_CAL_GOTO_SAME_DAY_OF_PREVIOUS_WEEK); gtk_binding_entry_add_signal ( binding_set,GDK_KEY_KP_Left, GDK_MOD1_MASK, "goto_date",1, G_TYPE_ENUM, GNOME_CAL_GOTO_SAME_DAY_OF_PREVIOUS_WEEK); gtk_binding_entry_add_signal ( binding_set,GDK_KEY_Right, GDK_MOD1_MASK, "goto_date",1, G_TYPE_ENUM, GNOME_CAL_GOTO_SAME_DAY_OF_NEXT_WEEK); gtk_binding_entry_add_signal ( binding_set,GDK_KEY_KP_Right, GDK_MOD1_MASK, "goto_date",1, G_TYPE_ENUM, GNOME_CAL_GOTO_SAME_DAY_OF_NEXT_WEEK); /* Ctrl+Y/J/K/M/L to switch between * DayView/WorkWeekView/WeekView/MonthView/ListView */ gtk_binding_entry_add_signal ( binding_set, GDK_KEY_y, GDK_CONTROL_MASK, "change_view", 1, G_TYPE_ENUM, GNOME_CAL_DAY_VIEW); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_j, GDK_CONTROL_MASK, "change_view", 1, G_TYPE_ENUM, GNOME_CAL_WORK_WEEK_VIEW); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_k, GDK_CONTROL_MASK, "change_view", 1, G_TYPE_ENUM, GNOME_CAL_WEEK_VIEW); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_m, GDK_CONTROL_MASK, "change_view", 1, G_TYPE_ENUM, GNOME_CAL_MONTH_VIEW); gtk_binding_entry_add_signal ( binding_set, GDK_KEY_l, GDK_CONTROL_MASK, "change_view", 1, G_TYPE_ENUM, GNOME_CAL_LIST_VIEW); /* init the accessibility support for gnome_calendar */ gnome_calendar_a11y_init (); } /* We do this check since the calendar items are downloaded from the server * in the open_method, since the default timezone might not be set there. */ static void ensure_dates_are_in_default_zone (GnomeCalendar *gcal, icalcomponent *icalcomp) { ECalModel *model; icaltimezone *timezone; icaltimetype dt; model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); if (timezone == NULL) return; dt = icalcomponent_get_dtstart (icalcomp); if (dt.is_utc) { dt = icaltime_convert_to_zone (dt, timezone); icalcomponent_set_dtstart (icalcomp, dt); } dt = icalcomponent_get_dtend (icalcomp); if (dt.is_utc) { dt = icaltime_convert_to_zone (dt, timezone); icalcomponent_set_dtend (icalcomp, dt); } } /* Callback used when the calendar query reports of an updated object */ static void gnome_cal_objects_added_cb (ECalClientView *view, const GSList *list, GWeakRef *weak_ref) { GnomeCalendar *gcal; gcal = g_weak_ref_get (weak_ref); if (gcal != NULL) { const GSList *link; for (link = list; link != NULL; link = g_slist_next (link)) { ECalComponent *comp = NULL; icalcomponent *icalcomp = link->data; ensure_dates_are_in_default_zone (gcal, icalcomp); comp = e_cal_component_new (); if (!e_cal_component_set_icalcomponent ( comp, icalcomponent_new_clone (icalcomp))) { g_object_unref (comp); continue; } tag_calendar_by_comp ( gcal->priv->date_navigator, comp, e_cal_client_view_get_client (view), NULL, FALSE, TRUE, TRUE, gcal->priv->cancellable); g_object_unref (comp); } g_object_unref (gcal); } } static void gnome_cal_objects_modified_cb (ECalClientView *view, const GSList *objects, GWeakRef *weak_ref) { GnomeCalendar *gcal; gcal = g_weak_ref_get (weak_ref); if (gcal != NULL) { /* We have to retag the whole thing: an event may change dates * and the tag_calendar_by_comp() below would not know how to * untag the old dates. */ gnome_calendar_update_query (gcal); g_object_unref (gcal); } } /* Callback used when the calendar query reports of a removed object */ static void gnome_cal_objects_removed_cb (ECalClientView *view, const GSList *ids, GWeakRef *weak_ref) { GnomeCalendar *gcal; gcal = g_weak_ref_get (weak_ref); if (gcal != NULL) { /* Just retag the whole thing */ gnome_calendar_update_query (gcal); g_object_unref (gcal); } } /* Callback used when the calendar query is done */ static void gnome_cal_view_complete_cb (ECalClientView *query, const GError *error, GWeakRef *weak_ref) { /* FIXME Better error reporting */ if (error != NULL) g_warning ( "%s: Query did not complete successfully: %s", G_STRFUNC, error->message); } ECalendarView * gnome_calendar_get_calendar_view (GnomeCalendar *gcal, GnomeCalendarViewType view_type) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); g_return_val_if_fail (view_type < GNOME_CAL_LAST_VIEW, NULL); return gcal->priv->views[view_type]; } static void get_times_for_views (GnomeCalendar *gcal, GnomeCalendarViewType view_type, time_t *start_time, time_t *end_time, time_t *select_time) { GnomeCalendarPrivate *priv; ECalModel *model; EDayView *day_view; EWeekView *week_view; gint shown; GDate date; gint days_shown; GDateWeekday week_start_day; GDateWeekday first_work_day; GDateWeekday last_work_day; GDateWeekday start_day; GDateWeekday weekday; guint offset; struct icaltimetype tt = icaltime_null_time (); icaltimezone *timezone; gboolean range_selected; model = gnome_calendar_get_model (gcal); range_selected = gnome_calendar_get_range_selected (gcal); timezone = e_cal_model_get_timezone (model); week_start_day = e_cal_model_get_week_start_day (model); priv = gcal->priv; switch (view_type) { case GNOME_CAL_DAY_VIEW: day_view = E_DAY_VIEW (priv->views[view_type]); shown = e_day_view_get_days_shown (day_view); *start_time = time_day_begin_with_zone (*start_time, timezone); *end_time = time_add_day_with_zone (*start_time, shown, timezone); break; case GNOME_CAL_WORK_WEEK_VIEW: /* FIXME Kind of gross, but it works */ day_view = E_DAY_VIEW (priv->views[view_type]); time_to_gdate_with_zone (&date, *start_time, timezone); /* The start of the work-week is the first working day after the * week start day. */ /* Get the weekday corresponding to start_time. */ weekday = g_date_get_weekday (&date); /* Find the first working day of the week. */ first_work_day = e_cal_model_get_work_day_first (model); if (first_work_day != G_DATE_BAD_WEEKDAY) { last_work_day = e_cal_model_get_work_day_last (model); /* Now calculate the days we need to show to include * all the working days in the week. Add 1 to make it * inclusive. */ days_shown = e_weekday_get_days_between ( first_work_day, last_work_day) + 1; } else { /* If no working days are set, just use 7. */ days_shown = 7; } if (first_work_day == G_DATE_BAD_WEEKDAY) first_work_day = week_start_day; /* Calculate how many days we need to go back to the first workday. */ if (weekday < first_work_day) offset = (weekday + 7) - first_work_day; else offset = weekday - first_work_day; if (offset > 0) g_date_subtract_days (&date, offset); tt.year = g_date_get_year (&date); tt.month = g_date_get_month (&date); tt.day = g_date_get_day (&date); *start_time = icaltime_as_timet_with_zone (tt, timezone); *end_time = time_add_day_with_zone (*start_time, days_shown, timezone); if (select_time && day_view->selection_start_day == -1) time (select_time); break; case GNOME_CAL_WEEK_VIEW: week_view = E_WEEK_VIEW (priv->views[view_type]); start_day = e_week_view_get_display_start_day (week_view); *start_time = time_week_begin_with_zone ( *start_time, e_weekday_to_tm_wday (start_day), timezone); *end_time = time_add_week_with_zone ( *start_time, 1, timezone); if (select_time && week_view->selection_start_day == -1) time (select_time); break; case GNOME_CAL_MONTH_VIEW: week_view = E_WEEK_VIEW (priv->views[view_type]); shown = e_week_view_get_weeks_shown (week_view); start_day = e_week_view_get_display_start_day (week_view); if (!range_selected && ( !e_week_view_get_multi_week_view (week_view) || !week_view->month_scroll_by_week)) *start_time = time_month_begin_with_zone ( *start_time, timezone); *start_time = time_week_begin_with_zone ( *start_time, e_weekday_to_tm_wday (start_day), timezone); *end_time = time_add_week_with_zone ( *start_time, shown, timezone); if (select_time && week_view->selection_start_day == -1) time (select_time); break; case GNOME_CAL_LIST_VIEW: /* FIXME What to do here? */ *start_time = time_month_begin_with_zone (*start_time, timezone); *end_time = time_add_month_with_zone (*start_time, 1, timezone); break; default: g_return_if_reached (); } } /* Computes the range of time that the date navigator is showing */ static void get_date_navigator_range (GnomeCalendar *gcal, time_t *start_time, time_t *end_time) { ECalModel *model; gint start_year, start_month, start_day; gint end_year, end_month, end_day; struct icaltimetype start_tt; struct icaltimetype end_tt; icaltimezone *timezone; model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); start_tt = icaltime_null_time (); end_tt = icaltime_null_time (); if (!e_calendar_item_get_date_range ( gcal->priv->date_navigator->calitem, &start_year, &start_month, &start_day, &end_year, &end_month, &end_day)) { *start_time = -1; *end_time = -1; return; } start_tt.year = start_year; start_tt.month = start_month + 1; start_tt.day = start_day; end_tt.year = end_year; end_tt.month = end_month + 1; end_tt.day = end_day; icaltime_adjust (&end_tt, 1, 0, 0, 0); *start_time = icaltime_as_timet_with_zone (start_tt, timezone); *end_time = icaltime_as_timet_with_zone (end_tt, timezone); } static const gchar * gcal_get_default_tzloc (GnomeCalendar *gcal) { ECalModel *model; icaltimezone *timezone; const gchar *tzloc = NULL; g_return_val_if_fail (gcal != NULL, ""); model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); if (timezone && timezone != icaltimezone_get_utc_timezone ()) tzloc = icaltimezone_get_location (timezone); return tzloc ? tzloc : ""; } /* Adjusts a given query sexp with the time range of the date navigator */ static gchar * adjust_client_view_sexp (GnomeCalendar *gcal, const gchar *sexp) { time_t start_time, end_time; gchar *start, *end; gchar *new_sexp; get_date_navigator_range (gcal, &start_time, &end_time); if (start_time == -1 || end_time == -1) return NULL; start = isodate_from_time_t (start_time); end = isodate_from_time_t (end_time); if (sexp) { new_sexp = g_strdup_printf ( "(and (occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\") %s)", start, end, gcal_get_default_tzloc (gcal), sexp); } else { new_sexp = g_strdup_printf ( "(occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\")", start, end, gcal_get_default_tzloc (gcal)); } g_free (start); g_free (end); return new_sexp; } static void gnome_cal_get_client_view_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { ECalClient *client; ECalClientView *client_view = NULL; GnomeCalendar *gcal; ViewData *view_data; GError *local_error = NULL; client = E_CAL_CLIENT (source_object); view_data = (ViewData *) user_data; e_cal_client_get_view_finish ( client, result, &client_view, &local_error); /* Sanity check. */ g_return_if_fail ( ((client_view != NULL) && (local_error == NULL)) || ((client_view == NULL) && (local_error != NULL))); gcal = g_weak_ref_get (&view_data->gcal_weak_ref); if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (local_error); } else if (local_error != NULL) { /* FIXME This should be displayed as an EAlert. */ g_warning ("%s: %s", G_STRFUNC, local_error->message); g_error_free (local_error); } else if (gcal != NULL) { gulong handler_id; /* The ViewData struct is only modified from a single * thread, so no locking is necessary when populating * the struct members. */ view_data->client_view = g_object_ref (client_view); handler_id = g_signal_connect_data ( client_view, "objects-added", G_CALLBACK (gnome_cal_objects_added_cb), e_weak_ref_new (gcal), (GClosureNotify) e_weak_ref_free, 0); view_data->objects_added_handler_id = handler_id; handler_id = g_signal_connect_data ( client_view, "objects-modified", G_CALLBACK (gnome_cal_objects_modified_cb), e_weak_ref_new (gcal), (GClosureNotify) e_weak_ref_free, 0); view_data->objects_modified_handler_id = handler_id; handler_id = g_signal_connect_data ( client_view, "objects-removed", G_CALLBACK (gnome_cal_objects_removed_cb), e_weak_ref_new (gcal), (GClosureNotify) e_weak_ref_free, 0); view_data->objects_removed_handler_id = handler_id; handler_id = g_signal_connect_data ( client_view, "complete", G_CALLBACK (gnome_cal_view_complete_cb), e_weak_ref_new (gcal), (GClosureNotify) e_weak_ref_free, 0); view_data->complete_handler_id = handler_id; /* XXX This call blocks with no way to cancel. But the * ECalClientView API does not provide a proper way. */ e_cal_client_view_start (client_view, &local_error); if (local_error != NULL) { g_warning ("%s: %s", G_STRFUNC, local_error->message); g_error_free (local_error); } } g_clear_object (&gcal); g_clear_object (&client_view); view_data_unref (view_data); } /* Restarts a query for the date navigator in the calendar */ void gnome_calendar_update_query (GnomeCalendar *gcal) { GList *list, *link; gchar *real_sexp; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); e_calendar_item_clear_marks (gcal->priv->date_navigator->calitem); /* This cancels any previous view requests still in progress. */ date_nav_view_data_remove_all (gcal); g_return_if_fail (gcal->priv->sexp != NULL); /* Don't start a query unless a time range is set. */ real_sexp = adjust_client_view_sexp (gcal, gcal->priv->sexp); if (real_sexp == NULL) return; list = e_cal_model_list_clients (gcal->priv->model); /* create queries for each loaded client */ for (link = list; link != NULL; link = g_list_next (link)) { ECalClient *client = E_CAL_CLIENT (link->data); ViewData *view_data; view_data = view_data_new (gcal); date_nav_view_data_insert (gcal, view_data); e_cal_client_get_view ( client, real_sexp, view_data->cancellable, gnome_cal_get_client_view_cb, view_data_ref (view_data)); view_data_unref (view_data); } g_list_free_full (list, (GDestroyNotify) g_object_unref); g_free (real_sexp); update_task_and_memo_views (gcal); } void gnome_calendar_set_search_query (GnomeCalendar *gcal, const gchar *sexp, gboolean range_search, time_t start_range, time_t end_range) { GnomeCalendarPrivate *priv; ECalModel *model; gint i; time_t start, end; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); g_return_if_fail (sexp != NULL); priv = gcal->priv; model = gnome_calendar_get_model (gcal); /* Set the query on the date navigator */ g_free (priv->sexp); priv->sexp = g_strdup (sexp); priv->lview_select_daten_range = !range_search; start = start_range; end = end_range; d (g_print ("Changing the queries %s \n", sexp)); gnome_calendar_update_query (gcal); i = priv->current_view_type; /* Set the query on the views */ if (i == GNOME_CAL_LIST_VIEW && !priv->lview_select_daten_range) { start = priv->base_view_time; get_times_for_views (gcal, GNOME_CAL_LIST_VIEW, &start, &end, NULL); e_cal_model_set_search_query_with_time_range ( model, sexp, start, end); if (priv->current_view_type == GNOME_CAL_LIST_VIEW) gnome_calendar_update_date_navigator (gcal); } else e_cal_model_set_search_query (model, sexp); } static void update_task_and_memo_views (GnomeCalendar *gcal) { if (gcal->priv->task_table != NULL) { ECalModel *task_model; ETaskTable *task_table; gchar *hide_completed_tasks_sexp; /* Set the query on the task pad. */ task_table = E_TASK_TABLE (gcal->priv->task_table); task_model = e_task_table_get_model (task_table); hide_completed_tasks_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE); if (hide_completed_tasks_sexp != NULL) { if (gcal->priv->sexp != NULL) { gchar *search_query; search_query = g_strdup_printf ( "(and %s %s)", hide_completed_tasks_sexp, gcal->priv->sexp); e_cal_model_set_search_query ( task_model, search_query); g_free (search_query); } else { e_cal_model_set_search_query ( task_model, hide_completed_tasks_sexp); } } else { e_cal_model_set_search_query ( task_model, gcal->priv->sexp); } g_free (hide_completed_tasks_sexp); } if (gcal->priv->memo_table != NULL) { ECalModel *memo_model; ECalModel *view_model; EMemoTable *memo_table; time_t start = -1, end = -1; /* Set the query on the memo pad. */ memo_table = E_MEMO_TABLE (gcal->priv->memo_table); memo_model = e_memo_table_get_model (memo_table); view_model = gnome_calendar_get_model (gcal); e_cal_model_get_time_range (view_model, &start, &end); if (start != -1 && end != -1) { gchar *search_query; gchar *iso_start; gchar *iso_end; iso_start = isodate_from_time_t (start); iso_end = isodate_from_time_t (end); search_query = g_strdup_printf ( "(and (or (not (has-start?)) " "(occur-in-time-range? (make-time \"%s\") " "(make-time \"%s\") \"%s\")) %s)", iso_start, iso_end, gcal_get_default_tzloc (gcal), gcal->priv->sexp ? gcal->priv->sexp : ""); e_cal_model_set_search_query ( memo_model, search_query); g_free (search_query); g_free (iso_start); g_free (iso_end); } } } static gboolean update_marcus_bains_line_cb (GnomeCalendar *gcal) { GnomeCalendarViewType view_type; ECalendarView *view; time_t now, day_begin; view_type = gnome_calendar_get_view (gcal); view = gnome_calendar_get_calendar_view (gcal, view_type); if (E_IS_DAY_VIEW (view)) e_day_view_marcus_bains_update (E_DAY_VIEW (view)); time (&now); day_begin = time_day_begin (now); /* check in the first two minutes */ if (now >= day_begin && now <= day_begin + 120) { time_t start_time = 0, end_time = 0; g_return_val_if_fail (view != NULL, TRUE); e_calendar_view_get_selected_time_range (view, &start_time, &end_time); if (end_time >= time_add_day (day_begin, -1) && start_time <= day_begin) { gnome_calendar_goto (gcal, now); } } return TRUE; } static void setup_widgets (GnomeCalendar *gcal) { GnomeCalendarPrivate *priv; priv = gcal->priv; /* update_task_and_memo_views (gcal); */ /* Timeout check to hide completed items */ #if 0 /* KILL-BONOBO */ priv->update_timeout = g_timeout_add_full ( G_PRIORITY_LOW, 60000, (GSourceFunc) update_task_and_memo_views_cb, gcal, NULL); #endif /* The Marcus Bains line */ priv->update_marcus_bains_line_timeout = g_timeout_add_full ( G_PRIORITY_LOW, 60000, (GSourceFunc) update_marcus_bains_line_cb, gcal, NULL); /* update_memo_view (gcal); */ } /* Object initialization function for the gnome calendar */ static void gnome_calendar_init (GnomeCalendar *gcal) { GHashTable *date_nav_view_data; date_nav_view_data = g_hash_table_new_full ( (GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, (GDestroyNotify) view_data_unref, (GDestroyNotify) NULL); gcal->priv = GNOME_CALENDAR_GET_PRIVATE (gcal); g_mutex_init (&gcal->priv->date_nav_view_data_lock); gcal->priv->current_view_type = GNOME_CAL_WORK_WEEK_VIEW; gcal->priv->range_selected = FALSE; gcal->priv->lview_select_daten_range = TRUE; setup_widgets (gcal); gcal->priv->date_nav_view_data = date_nav_view_data; gcal->priv->sexp = g_strdup ("#t"); /* Match all */ gcal->priv->visible_start = -1; gcal->priv->visible_end = -1; gcal->priv->updating = FALSE; gcal->priv->cancellable = g_cancellable_new (); } static void gnome_calendar_do_dispose (GObject *object) { GnomeCalendarPrivate *priv; gint ii; priv = GNOME_CALENDAR_GET_PRIVATE (object); if (priv->registry != NULL) { g_object_unref (priv->registry); priv->registry = NULL; } if (priv->model != NULL) { g_signal_handlers_disconnect_by_data (priv->model, object); g_object_unref (priv->model); priv->model = NULL; } for (ii = 0; ii < GNOME_CAL_LAST_VIEW; ii++) { if (priv->views[ii] != NULL) { g_object_unref (priv->views[ii]); priv->views[ii] = NULL; } } g_hash_table_remove_all (priv->date_nav_view_data); if (priv->sexp) { g_free (priv->sexp); priv->sexp = NULL; } if (priv->update_timeout) { g_source_remove (priv->update_timeout); priv->update_timeout = 0; } if (priv->update_marcus_bains_line_timeout) { g_source_remove (priv->update_marcus_bains_line_timeout); priv->update_marcus_bains_line_timeout = 0; } if (priv->cancellable) { g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (gnome_calendar_parent_class)->dispose (object); } static void gnome_calendar_finalize (GObject *object) { GnomeCalendarPrivate *priv; priv = GNOME_CALENDAR_GET_PRIVATE (object); g_mutex_clear (&priv->date_nav_view_data_lock); g_hash_table_destroy (priv->date_nav_view_data); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (gnome_calendar_parent_class)->finalize (object); } static void notify_selected_time_changed (GnomeCalendar *gcal) { GnomeCalendarPrivate *priv; gint i; priv = gcal->priv; for (i = 0; i < GNOME_CAL_LAST_VIEW; i++) { g_signal_emit_by_name (priv->views[i], "selected_time_changed"); } } static void gnome_calendar_goto_date (GnomeCalendar *gcal, GnomeCalendarGotoDateType goto_date) { ECalModel *model; time_t new_time = 0; GDateWeekday week_start_day; gboolean need_updating = FALSE; icaltimezone *timezone; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); model = gnome_calendar_get_model (gcal); week_start_day = e_cal_model_get_week_start_day (model); timezone = e_cal_model_get_timezone (model); switch (goto_date) { /* GNOME_CAL_GOTO_TODAY and GNOME_CAL_GOTO_DATE are * currently not used */ case GNOME_CAL_GOTO_TODAY: break; case GNOME_CAL_GOTO_DATE: break; case GNOME_CAL_GOTO_FIRST_DAY_OF_MONTH: new_time = time_month_begin_with_zone ( gcal->priv->base_view_time, timezone); need_updating = TRUE; break; case GNOME_CAL_GOTO_LAST_DAY_OF_MONTH: new_time = time_add_month_with_zone ( gcal->priv->base_view_time, 1, timezone); new_time = time_month_begin_with_zone (new_time, timezone); new_time = time_add_day_with_zone (new_time, -1, timezone); need_updating = TRUE; break; case GNOME_CAL_GOTO_FIRST_DAY_OF_WEEK: new_time = time_week_begin_with_zone ( gcal->priv->base_view_time, e_weekday_to_tm_wday (week_start_day), timezone); need_updating = TRUE; break; case GNOME_CAL_GOTO_LAST_DAY_OF_WEEK: new_time = time_week_begin_with_zone ( gcal->priv->base_view_time, e_weekday_to_tm_wday (week_start_day), timezone); if (gcal->priv->current_view_type == GNOME_CAL_DAY_VIEW || gcal->priv->current_view_type == GNOME_CAL_WORK_WEEK_VIEW) { /* FIXME Shouldn't hard code work week end */ /* goto Friday of this week */ new_time = time_add_day_with_zone (new_time, 4, timezone); } else { /* goto Sunday of this week */ /* FIXME Shouldn't hard code week end */ new_time = time_add_day_with_zone (new_time, 6, timezone); } need_updating = TRUE; break; case GNOME_CAL_GOTO_SAME_DAY_OF_PREVIOUS_WEEK: new_time = time_add_week_with_zone ( gcal->priv->base_view_time, -1, timezone); need_updating = TRUE; break; case GNOME_CAL_GOTO_SAME_DAY_OF_NEXT_WEEK: new_time = time_add_week_with_zone ( gcal->priv->base_view_time, 1, timezone); need_updating = TRUE; break; default: break; } if (need_updating) { gnome_calendar_set_selected_time_range (gcal, new_time); notify_selected_time_changed (gcal); } } void gnome_calendar_goto (GnomeCalendar *gcal, time_t new_time) { GnomeCalendarPrivate *priv; gint i; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); g_return_if_fail (new_time != -1); priv = gcal->priv; gnome_calendar_set_selected_time_range (gcal, new_time); for (i = 0; i < GNOME_CAL_LAST_VIEW; i++) e_calendar_view_set_selected_time_range ( priv->views[i], new_time, new_time); } void gnome_calendar_update_view_times (GnomeCalendar *gcal, time_t start_time) { GnomeCalendarPrivate *priv; ECalModel *model; time_t real_start_time = start_time; time_t end_time, select_time = 0; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); priv = gcal->priv; priv->base_view_time = start_time; model = gnome_calendar_get_model (gcal); get_times_for_views ( gcal, priv->current_view_type, &real_start_time, &end_time, &select_time); if (priv->current_view_type == GNOME_CAL_LIST_VIEW && !priv->lview_select_daten_range) return; e_cal_model_set_time_range (model, real_start_time, end_time); if (select_time != 0 && select_time >= real_start_time && select_time <= end_time) e_calendar_view_set_selected_time_range ( priv->views[priv->current_view_type], select_time, select_time); } static void gnome_calendar_direction (GnomeCalendar *gcal, gint direction) { ECalModel *model; icaltimezone *timezone; model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); switch (gnome_calendar_get_view (gcal)) { case GNOME_CAL_DAY_VIEW: gcal->priv->base_view_time = time_add_day_with_zone ( gcal->priv->base_view_time, direction, timezone); break; case GNOME_CAL_WORK_WEEK_VIEW: case GNOME_CAL_WEEK_VIEW: gcal->priv->base_view_time = time_add_week_with_zone ( gcal->priv->base_view_time, direction, timezone); break; case GNOME_CAL_MONTH_VIEW: case GNOME_CAL_LIST_VIEW: gcal->priv->base_view_time = time_add_month_with_zone ( gcal->priv->base_view_time, direction, timezone); break; default: g_return_if_reached (); } gnome_calendar_set_selected_time_range ( gcal, gcal->priv->base_view_time); } void gnome_calendar_next (GnomeCalendar *gcal) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); gnome_calendar_direction (gcal, 1); } void gnome_calendar_previous (GnomeCalendar *gcal) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); gnome_calendar_direction (gcal, -1); } void gnome_calendar_dayjump (GnomeCalendar *gcal, time_t time) { ECalModel *model; icaltimezone *timezone; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); gcal->priv->base_view_time = time_day_begin_with_zone (time, timezone); gnome_calendar_update_view_times (gcal, gcal->priv->base_view_time); gnome_calendar_set_view (gcal, GNOME_CAL_DAY_VIEW); } void gnome_calendar_goto_today (GnomeCalendar *gcal) { GnomeCalendarViewType view_type; ECalendarView *view; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); view_type = gnome_calendar_get_view (gcal); view = gnome_calendar_get_calendar_view (gcal, view_type); gnome_calendar_goto (gcal, time (NULL)); gtk_widget_grab_focus (GTK_WIDGET (view)); } /** * gnome_calendar_get_view: * @gcal: A calendar. * * Queries the type of the view that is being shown in a calendar. * * Return value: Type of the view that is currently shown. **/ GnomeCalendarViewType gnome_calendar_get_view (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), GNOME_CAL_DAY_VIEW); return gcal->priv->current_view_type; } /** * gnome_calendar_set_view: * @gcal: A calendar. * @view_type: Type of view to show. * * Sets the view that should be shown in a calendar. If @reset_range is true, * this function will automatically set the number of days or weeks shown in * the view; otherwise the last configuration will be kept. **/ void gnome_calendar_set_view (GnomeCalendar *gcal, GnomeCalendarViewType view_type) { ECalendarView *calendar_view; gint ii; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); if (gcal->priv->current_view_type == view_type && E_CALENDAR_VIEW (gcal->priv->views[view_type])->in_focus) return; gcal->priv->current_view_type = view_type; gnome_calendar_set_range_selected (gcal, FALSE); E_CALENDAR_VIEW (gcal->priv->views[view_type])->in_focus = TRUE; for (ii = 0; ii < GNOME_CAL_LAST_VIEW; ii++) { if (ii == view_type) continue; E_CALENDAR_VIEW (gcal->priv->views[ii])->in_focus = FALSE; } calendar_view = gnome_calendar_get_calendar_view (gcal, view_type); gtk_widget_grab_focus (GTK_WIDGET (calendar_view)); g_object_notify (G_OBJECT (gcal), "view"); } void gnome_calendar_display_view (GnomeCalendar *gcal, GnomeCalendarViewType view_type) { ECalendarView *view; gboolean preserve_day; gboolean range_selected; time_t start_time; view = gnome_calendar_get_calendar_view (gcal, view_type); /* Set the view without changing the selection or updating the date * navigator. If a range of dates isn't selected, also reset the * number of days/weeks shown to the default (i.e. 1 day for the * day view or 6 weeks for the month view). */ preserve_day = FALSE; switch (view_type) { case GNOME_CAL_DAY_VIEW: if (!gnome_calendar_get_range_selected (gcal)) e_day_view_set_days_shown (E_DAY_VIEW (view), 1); gtk_widget_show (GTK_WIDGET (gcal->priv->date_navigator)); break; case GNOME_CAL_WORK_WEEK_VIEW: preserve_day = TRUE; gtk_widget_show (GTK_WIDGET (gcal->priv->date_navigator)); break; case GNOME_CAL_WEEK_VIEW: preserve_day = TRUE; gtk_widget_show (GTK_WIDGET (gcal->priv->date_navigator)); break; case GNOME_CAL_MONTH_VIEW: if (!gnome_calendar_get_range_selected (gcal)) e_week_view_set_weeks_shown (E_WEEK_VIEW (view), 6); preserve_day = TRUE; gtk_widget_show (GTK_WIDGET (gcal->priv->date_navigator)); break; case GNOME_CAL_LIST_VIEW: if (!gcal->priv->lview_select_daten_range) gtk_widget_hide (GTK_WIDGET (gcal->priv->date_navigator)); else gtk_widget_show (GTK_WIDGET (gcal->priv->date_navigator)); break; default: g_return_if_reached (); } range_selected = gnome_calendar_get_range_selected (gcal); gnome_calendar_set_view (gcal, view_type); gnome_calendar_set_range_selected (gcal, range_selected); /* For the week & month views we want the selection in the date * navigator to be rounded to the nearest week when the arrow buttons * are pressed to move to the previous/next month. */ g_object_set ( gcal->priv->date_navigator->calitem, "preserve_day_when_moving", preserve_day, NULL); /* keep week days selected as before for a work week view */ g_object_set ( gcal->priv->date_navigator->calitem, "keep_wdays_on_weeknum_click", view_type == GNOME_CAL_WORK_WEEK_VIEW, NULL); if (!gcal->priv->base_view_time) start_time = time (NULL); else start_time = gcal->priv->base_view_time; gnome_calendar_set_selected_time_range (gcal, start_time); } GtkWidget * gnome_calendar_new (ESourceRegistry *registry) { g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); return g_object_new ( GNOME_TYPE_CALENDAR, "registry", registry, NULL); } ESourceRegistry * gnome_calendar_get_registry (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); return gcal->priv->registry; } ECalendar * gnome_calendar_get_date_navigator (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); return gcal->priv->date_navigator; } void gnome_calendar_set_date_navigator (GnomeCalendar *gcal, ECalendar *date_navigator) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); if (gcal->priv->date_navigator == date_navigator) return; if (date_navigator != NULL) { g_return_if_fail (E_IS_CALENDAR (date_navigator)); g_object_ref (date_navigator); } if (gcal->priv->date_navigator != NULL) g_object_unref (gcal->priv->date_navigator); gcal->priv->date_navigator = date_navigator; /* Update the new date navigator */ gnome_calendar_update_date_navigator (gcal); g_object_notify (G_OBJECT (gcal), "date-navigator"); } GtkWidget * gnome_calendar_get_memo_table (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); return gcal->priv->memo_table; } void gnome_calendar_set_memo_table (GnomeCalendar *gcal, GtkWidget *memo_table) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); if (gcal->priv->memo_table == memo_table) return; if (memo_table != NULL) { g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); g_object_ref (memo_table); } if (gcal->priv->memo_table != NULL) g_object_unref (gcal->priv->memo_table); gcal->priv->memo_table = memo_table; g_object_notify (G_OBJECT (gcal), "memo-table"); } GtkWidget * gnome_calendar_get_task_table (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); return gcal->priv->task_table; } void gnome_calendar_set_task_table (GnomeCalendar *gcal, GtkWidget *task_table) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); if (gcal->priv->task_table == task_table) return; if (task_table != NULL) { g_return_if_fail (E_IS_TASK_TABLE (task_table)); g_object_ref (task_table); } if (gcal->priv->task_table != NULL) g_object_unref (gcal->priv->task_table); gcal->priv->task_table = task_table; g_object_notify (G_OBJECT (gcal), "task-table"); } /** * gnome_calendar_get_model: * @gcal: A calendar view. * * Queries the calendar model object that a calendar view is using. * * Return value: A calendar client interface object. **/ ECalModel * gnome_calendar_get_model (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); return gcal->priv->model; } gboolean gnome_calendar_get_range_selected (GnomeCalendar *gcal) { g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), FALSE); return gcal->priv->range_selected; } void gnome_calendar_set_range_selected (GnomeCalendar *gcal, gboolean range_selected) { g_return_if_fail (GNOME_IS_CALENDAR (gcal)); gcal->priv->range_selected = range_selected; } void gnome_calendar_set_selected_time_range (GnomeCalendar *gcal, time_t start_time) { gnome_calendar_update_view_times (gcal, start_time); gnome_calendar_update_date_navigator (gcal); gnome_calendar_notify_dates_shown_changed (gcal); } /** * gnome_calendar_new_task: * @gcal: An Evolution calendar. * @dtstart: Start time of the task, in same timezone as model. * @dtend: End time of the task, in same timezone as model. * * Opens a task editor dialog for a new task. dtstart or dtend can be NULL. **/ #if 0 /* KILL-BONOBO */ void gnome_calendar_new_task (GnomeCalendar *gcal, time_t *dtstart, time_t *dtend) { GnomeCalendarPrivate *priv; ECal *ecal; ECalModel *model; CompEditor *editor; ECalComponent *comp; icalcomponent *icalcomp; const gchar *category; guint32 flags = 0; ECalComponentDateTime dt; struct icaltimetype itt; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); priv = gcal->priv; model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->todo)); ecal = e_cal_model_get_default_client (model); if (!ecal) return; flags |= COMP_EDITOR_NEW_ITEM; editor = task_editor_new (ecal, flags); icalcomp = e_cal_model_create_component_with_defaults (model, FALSE); comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, icalcomp); dt.value = &itt; dt.tzid = icaltimezone_get_tzid (e_cal_model_get_timezone (model)); if (dtstart) { itt = icaltime_from_timet_with_zone ( *dtstart, FALSE, e_cal_model_get_timezone (model)); e_cal_component_set_dtstart (comp, &dt); } if (dtend) { itt = icaltime_from_timet_with_zone ( *dtend, FALSE, e_cal_model_get_timezone (model)); e_cal_component_set_due (comp, &dt); /* task uses 'due' not 'dtend' */ } if (dtstart || dtend) e_cal_component_commit_sequence (comp); comp_editor_edit_comp (editor, comp); g_object_unref (comp); gtk_window_present (GTK_WINDOW (editor)); } #endif /* Returns the selected time range for the current view. Note that this may be * different from the fields in the GnomeCalendar, since the view may clip * this or choose a more appropriate time. */ void gnome_calendar_get_current_time_range (GnomeCalendar *gcal, time_t *start_time, time_t *end_time) { GnomeCalendarViewType view_type; ECalendarView *view; view_type = gnome_calendar_get_view (gcal); view = gnome_calendar_get_calendar_view (gcal, view_type); e_calendar_view_get_selected_time_range (view, start_time, end_time); } /* This updates the month shown and the days selected in the calendar, if * necessary. */ static void gnome_calendar_update_date_navigator (GnomeCalendar *gcal) { GnomeCalendarPrivate *priv; ECalModel *model; time_t start, end; GDateWeekday week_start_day; GDate start_date, end_date; icaltimezone *timezone; priv = gcal->priv; /* If the ECalendar is not yet set, we just return. */ if (priv->date_navigator == NULL) return; /* If the ECalendar isn't visible, we just return. */ if (!gtk_widget_get_visible (GTK_WIDGET (priv->date_navigator))) return; if (priv->current_view_type == GNOME_CAL_LIST_VIEW && !priv->lview_select_daten_range) return; model = gnome_calendar_get_model (gcal); timezone = e_cal_model_get_timezone (model); week_start_day = e_cal_model_get_week_start_day (model); e_cal_model_get_time_range (model, &start, &end); time_to_gdate_with_zone (&start_date, start, timezone); if (priv->current_view_type == GNOME_CAL_MONTH_VIEW) { EWeekView *week_view = E_WEEK_VIEW (priv->views[priv->current_view_type]); if (week_start_day == G_DATE_MONDAY && (!e_week_view_get_multi_week_view (week_view) || e_week_view_get_compress_weekend (week_view))) g_date_add_days (&start_date, 1); } time_to_gdate_with_zone (&end_date, end, timezone); g_date_subtract_days (&end_date, 1); e_calendar_item_set_selection ( priv->date_navigator->calitem, &start_date, &end_date); } void gnome_calendar_notify_dates_shown_changed (GnomeCalendar *gcal) { GnomeCalendarViewType view_type; ECalendarView *calendar_view; GnomeCalendarPrivate *priv; time_t start_time, end_time; gboolean has_time_range; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); priv = gcal->priv; view_type = gnome_calendar_get_view (gcal); calendar_view = gnome_calendar_get_calendar_view (gcal, view_type); /* If no time range is set yet, just return. */ has_time_range = e_calendar_view_get_visible_time_range ( calendar_view, &start_time, &end_time); if (!has_time_range) return; /* We check if the visible date range has changed, and only emit the * signal if it has. (This makes sure we only change the folder title * bar label in the shell when we need to.) */ if (priv->visible_start != start_time || priv->visible_end != end_time) { priv->visible_start = start_time; priv->visible_end = end_time; gtk_widget_queue_draw (GTK_WIDGET (calendar_view)); g_signal_emit (gcal, signals[DATES_SHOWN_CHANGED], 0); } update_task_and_memo_views (gcal); } /* Returns the number of selected events (0 or 1 at present). */ gint gnome_calendar_get_num_events_selected (GnomeCalendar *gcal) { GnomeCalendarViewType view_type; ECalendarView *view; gint retval = 0; g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), 0); view_type = gnome_calendar_get_view (gcal); view = gnome_calendar_get_calendar_view (gcal, view_type); if (E_IS_DAY_VIEW (view)) retval = e_day_view_get_num_events_selected (E_DAY_VIEW (view)); else retval = e_week_view_get_num_events_selected (E_WEEK_VIEW (view)); return retval; } struct purge_data { gboolean remove; time_t older_than; }; static gboolean check_instance_cb (ECalComponent *comp, time_t instance_start, time_t instance_end, gpointer data) { struct purge_data *pd = data; if (instance_end >= pd->older_than) pd->remove = FALSE; return pd->remove; } void gnome_calendar_purge (GnomeCalendar *gcal, time_t older_than) { ECalModel *model; gchar *sexp, *start, *end; GList *list, *link; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); model = gnome_calendar_get_model (gcal); start = isodate_from_time_t (0); end = isodate_from_time_t (older_than); sexp = g_strdup_printf ( "(occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\")", start, end, gcal_get_default_tzloc (gcal)); gcal_update_status_message (gcal, _("Purging"), -1); /* FIXME Confirm expunge */ list = e_cal_model_list_clients (model); for (link = list; link != NULL; link = g_list_next (link)) { ECalClient *client = E_CAL_CLIENT (link->data); GSList *objects, *m; GError *error = NULL; if (e_client_is_readonly (E_CLIENT (client))) continue; e_cal_client_get_object_list_sync ( client, sexp, &objects, NULL, &error); if (error != NULL) { g_warning ( "%s: Could not get the objects: %s", G_STRFUNC, error->message); g_error_free (error); continue; } for (m = objects; m; m = m->next) { gboolean remove = TRUE; /* FIXME write occur-before and occur-after * sexp funcs so we don't have to use the max * gint */ if (!e_cal_client_check_recurrences_no_master (client)) { struct purge_data pd; pd.remove = TRUE; pd.older_than = older_than; e_cal_client_generate_instances_for_object_sync (client, m->data, older_than, G_MAXINT32, check_instance_cb, &pd); remove = pd.remove; } /* FIXME Better error handling */ if (remove) { const gchar *uid = icalcomponent_get_uid (m->data); GError *error = NULL; if (e_cal_util_component_is_instance (m->data) || e_cal_util_component_has_recurrences (m->data)) { gchar *rid = NULL; struct icaltimetype recur_id; recur_id = icalcomponent_get_recurrenceid (m->data); if (!icaltime_is_null_time (recur_id)) rid = icaltime_as_ical_string_r (recur_id); e_cal_client_remove_object_sync ( client, uid, rid, CALOBJ_MOD_ALL, NULL, &error); g_free (rid); } else { e_cal_client_remove_object_sync ( client, uid, NULL, CALOBJ_MOD_THIS, NULL, &error); } if (error != NULL) { g_warning ( "%s: Unable to purge events: %s", G_STRFUNC, error->message); g_error_free (error); } } } g_slist_foreach (objects, (GFunc) icalcomponent_free, NULL); g_slist_free (objects); } g_list_free_full (list, (GDestroyNotify) g_object_unref); gcal_update_status_message (gcal, NULL, -1); g_free (sexp); g_free (start); g_free (end); }