/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "e-util/e-util.h" #include "libevolution-utils/e-alert-dialog.h" #include "e-util/e-util-private.h" #include "shell/e-shell.h" #include "dialogs/delete-error.h" #include "dialogs/event-editor.h" #include "comp-util.h" #include "e-cal-model-calendar.h" #include "e-day-view.h" #include "e-day-view-time-item.h" #include "e-month-view.h" #include "e-week-view.h" #include "e-cal-list-view.h" #include "gnome-cal.h" #include "calendar-config.h" #include "calendar-view.h" #include "calendar-view-factory.h" #include "tag-calendar.h" #include "misc.h" #include "ea-calendar.h" #include "e-memo-table.h" #include "e-task-table.h" #define d(x) #define GNOME_CALENDAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), GNOME_TYPE_CALENDAR, GnomeCalendarPrivate)) /* Private part of the GnomeCalendar structure */ struct _GnomeCalendarPrivate { 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 */ /* Calendar query for the date navigator */ GMutex *dn_query_lock; GList *dn_queries; /* list of CalQueries */ gchar *sexp; gchar *todo_sexp; gchar *memo_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; /* Used in update_todo_view, to prevent interleaving when * called in separate thread. */ GMutex *todo_update_lock; GCancellable *cancellable; }; enum { PROP_0, PROP_DATE_NAVIGATOR, PROP_MEMO_TABLE, 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_todo_view (GnomeCalendar *gcal); static void update_memo_view (GnomeCalendar *gcal); /* Simple asynchronous message dispatcher */ typedef struct _Message Message; typedef void (*MessageFunc) (Message *msg); struct _Message { MessageFunc func; GSourceFunc done; }; static void message_proxy (Message *msg) { g_return_if_fail (msg->func != NULL); msg->func (msg); if (msg->done) g_idle_add (msg->done, msg); } static gpointer create_thread_pool (void) { /* once created, run forever */ return g_thread_pool_new ((GFunc) message_proxy, NULL, 1, FALSE, NULL); } static void message_push (Message *msg) { static GOnce once = G_ONCE_INIT; g_once (&once, (GThreadFunc) create_thread_pool, NULL); g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL); } G_DEFINE_TYPE (GnomeCalendar, gnome_calendar, G_TYPE_OBJECT) 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; 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; /* If we don't have a valid date set yet, just return. */ if (!g_date_valid (&week_view->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 (&week_view->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_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_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_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; ECalModel *model; GtkAdjustment *adjustment; /* Create the model for the views. */ model = e_cal_model_calendar_new (); 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-days", 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_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 dn_client_view_objects_added_cb (ECalClientView *view, const GSList *objects, gpointer data) { GnomeCalendar *gcal; GnomeCalendarPrivate *priv; const GSList *l; gcal = GNOME_CALENDAR (data); priv = gcal->priv; for (l = objects; l; l = l->next) { ECalComponent *comp = NULL; ensure_dates_are_in_default_zone (gcal, l->data); comp = e_cal_component_new (); if (!e_cal_component_set_icalcomponent ( comp, icalcomponent_new_clone (l->data))) { g_object_unref (comp); continue; } tag_calendar_by_comp ( priv->date_navigator, comp, e_cal_client_view_get_client (view), NULL, FALSE, TRUE, TRUE, priv->cancellable); g_object_unref (comp); } } static void dn_client_view_objects_modified_cb (ECalClientView *view, const GSList *objects, gpointer data) { GnomeCalendar *gcal; gcal = GNOME_CALENDAR (data); /* 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); } /* Callback used when the calendar query reports of a removed object */ static void dn_client_view_objects_removed_cb (ECalClientView *view, const GSList *ids, gpointer data) { GnomeCalendar *gcal; gcal = GNOME_CALENDAR (data); /* Just retag the whole thing */ gnome_calendar_update_query (gcal); } /* Callback used when the calendar query is done */ static void dn_client_view_complete_cb (ECalClientView *query, const GError *error, gpointer data) { /* 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, display_start; GDate date; gint week_start_day; gint weekday, first_day, last_day, days_shown, i; gboolean has_working_days = FALSE; 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, 0 (Sun) to 6 (Sat). */ weekday = g_date_get_weekday (&date) % 7; /* Find the first working day in the week, 0 (Sun) to 6 (Sat). */ first_day = (week_start_day + 1) % 7; for (i = 0; i < 7; i++) { if (day_view->working_days & (1 << first_day)) { has_working_days = TRUE; break; } first_day = (first_day + 1) % 7; } if (has_working_days) { /* Now find the last working day of the week, backwards. */ last_day = week_start_day % 7; for (i = 0; i < 7; i++) { if (day_view->working_days & (1 << last_day)) break; last_day = (last_day + 6) % 7; } /* 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 = (last_day + 7 - first_day) % 7 + 1; } else { /* If no working days are set, just use 7. */ days_shown = 7; } /* Calculate how many days we need to go back to the first workday. */ if (weekday < first_day) { offset = (first_day - weekday) % 7; g_date_add_days (&date, offset); } else { offset = (weekday - first_day) % 7; 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: /* FIXME We should be using the same day * of the week enum everywhere. */ week_view = E_WEEK_VIEW (priv->views[view_type]); display_start = (week_view->display_start_day + 1) % 7; *start_time = time_week_begin_with_zone ( *start_time, display_start, 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: /* FIXME We should be using the same day * of the week enum everywhere. */ week_view = E_WEEK_VIEW (priv->views[view_type]); shown = e_week_view_get_weeks_shown (week_view); display_start = (week_view->display_start_day + 1) % 7; if (!range_selected && ( !week_view->multi_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, display_start, 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; } struct _date_query_msg { Message header; GnomeCalendar *gcal; }; static void free_dn_queries (GnomeCalendar *gcal) { GList *l; GnomeCalendarPrivate *priv; priv = gcal->priv; g_mutex_lock (priv->dn_query_lock); for (l = priv->dn_queries; l != NULL; l = l->next) { if (!l->data) continue; g_signal_handlers_disconnect_matched ( l->data, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gcal); g_object_unref (l->data); } g_list_free (priv->dn_queries); priv->dn_queries = NULL; g_mutex_unlock (priv->dn_query_lock); } static void update_query_async (struct _date_query_msg *msg) { GnomeCalendar *gcal = msg->gcal; GnomeCalendarPrivate *priv; ECalClientView *new_view; gchar *real_sexp; GList *list, *iter; priv = gcal->priv; /* free the previous queries */ free_dn_queries (gcal); g_return_if_fail (priv->sexp != NULL); real_sexp = adjust_client_view_sexp (gcal, priv->sexp); if (!real_sexp) { return; /* No time range is set, so don't start a query */ } list = e_cal_model_get_client_list (priv->model); g_list_foreach (list, (GFunc) g_object_ref, NULL); /* create queries for each loaded client */ for (iter = list; iter != NULL; iter = iter->next) { ECalClient *client = E_CAL_CLIENT (iter->data); GError *error = NULL; gint tries = 0; /* don't create queries for clients not loaded yet */ if (!e_client_is_opened (E_CLIENT (client))) continue; try_again: new_view = NULL; if (!e_cal_client_get_view_sync (client, real_sexp, &new_view, NULL, &error)) { /* If calendar is busy try again for 3 times. */ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY) && tries != 10) { tries++; /*TODO chose an optimal value */ g_usleep (500); g_clear_error (&error); goto try_again; } g_warning (G_STRLOC ": Could not create the view: %s ", error->message); g_clear_error (&error); continue; } g_signal_connect ( new_view, "objects-added", G_CALLBACK (dn_client_view_objects_added_cb), gcal); g_signal_connect ( new_view, "objects-modified", G_CALLBACK (dn_client_view_objects_modified_cb), gcal); g_signal_connect ( new_view, "objects-removed", G_CALLBACK (dn_client_view_objects_removed_cb), gcal); g_signal_connect ( new_view, "complete", G_CALLBACK (dn_client_view_complete_cb), gcal); g_mutex_lock (priv->dn_query_lock); priv->dn_queries = g_list_append (priv->dn_queries, new_view); e_cal_client_view_start (new_view, &error); if (error != NULL) { g_warning ( "%s: Failed to start view: %s", G_STRFUNC, error->message); g_clear_error (&error); } g_mutex_unlock (priv->dn_query_lock); } g_list_foreach (list, (GFunc) g_object_unref, NULL); g_list_free (list); /* free memory */ g_free (real_sexp); update_todo_view (gcal); } static gboolean update_query_done (struct _date_query_msg *msg) { g_object_unref (msg->gcal); g_slice_free (struct _date_query_msg, msg); return FALSE; } /* Restarts a query for the date navigator in the calendar */ void gnome_calendar_update_query (GnomeCalendar *gcal) { struct _date_query_msg *msg; g_return_if_fail (GNOME_IS_CALENDAR (gcal)); e_calendar_item_clear_marks (gcal->priv->date_navigator->calitem); msg = g_slice_new0 (struct _date_query_msg); msg->header.func = (MessageFunc) update_query_async; msg->header.done = (GSourceFunc) update_query_done; msg->gcal = g_object_ref (gcal); message_push ((Message *) msg); } 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); } struct _mupdate_todo_msg { Message header; GnomeCalendar *gcal; }; static void update_todo_view_async (struct _mupdate_todo_msg *msg) { GnomeCalendar *gcal; GnomeCalendarPrivate *priv; ECalModel *model; gchar *sexp = NULL; g_return_if_fail (msg != NULL); gcal = msg->gcal; priv = gcal->priv; g_return_if_fail (priv->task_table != NULL); g_mutex_lock (priv->todo_update_lock); /* Set the query on the task pad */ if (priv->todo_sexp) { g_free (priv->todo_sexp); priv->todo_sexp = NULL; } model = e_task_table_get_model (E_TASK_TABLE (priv->task_table)); if ((sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE)) != NULL) { priv->todo_sexp = g_strdup_printf ("(and %s %s)", sexp, priv->sexp ? priv->sexp : ""); e_cal_model_set_search_query (model, priv->todo_sexp); g_free (sexp); } else { priv->todo_sexp = g_strdup (priv->sexp); e_cal_model_set_search_query (model, priv->todo_sexp); } update_memo_view (msg->gcal); g_mutex_unlock (priv->todo_update_lock); } static gboolean update_todo_view_done (struct _mupdate_todo_msg *msg) { EMemoTable *memo_table; ETaskTable *task_table; EShellView *shell_view; GnomeCalendar *gcal; g_return_val_if_fail (msg != NULL, FALSE); gcal = msg->gcal; g_return_val_if_fail (gcal->priv->task_table != NULL, FALSE); g_return_val_if_fail (gcal->priv->memo_table != NULL, FALSE); task_table = E_TASK_TABLE (gcal->priv->task_table); shell_view = e_task_table_get_shell_view (task_table); e_shell_view_unblock_update_actions (shell_view); memo_table = E_MEMO_TABLE (gcal->priv->memo_table); shell_view = e_memo_table_get_shell_view (memo_table); e_shell_view_unblock_update_actions (shell_view); g_object_unref (msg->gcal); g_slice_free (struct _mupdate_todo_msg, msg); return FALSE; } static void update_todo_view (GnomeCalendar *gcal) { EMemoTable *memo_table; ETaskTable *task_table; EShellView *shell_view; struct _mupdate_todo_msg *msg; /* they are both or none anyway */ if (!gcal->priv->task_table || !gcal->priv->memo_table) return; msg = g_slice_new0 (struct _mupdate_todo_msg); msg->header.func = (MessageFunc) update_todo_view_async; msg->header.done = (GSourceFunc) update_todo_view_done; msg->gcal = g_object_ref (gcal); task_table = E_TASK_TABLE (gcal->priv->task_table); shell_view = e_task_table_get_shell_view (task_table); e_shell_view_block_update_actions (shell_view); memo_table = E_MEMO_TABLE (gcal->priv->memo_table); shell_view = e_memo_table_get_shell_view (memo_table); e_shell_view_block_update_actions (shell_view); message_push ((Message *) msg); } static void update_memo_view (GnomeCalendar *gcal) { GnomeCalendarPrivate *priv; ECalModel *model, *view_model; time_t start = -1, end = -1; gchar *iso_start, *iso_end; priv = gcal->priv; if (!priv->memo_table) return; /* Set the query on the memo pad*/ model = e_memo_table_get_model (E_MEMO_TABLE (priv->memo_table)); view_model = gnome_calendar_get_model (gcal); e_cal_model_get_time_range (view_model, &start, &end); if (start != -1 && end != -1) { iso_start = isodate_from_time_t (start); iso_end = isodate_from_time_t (end); g_free (priv->memo_sexp); priv->memo_sexp = 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), priv->sexp ? priv->sexp : ""); e_cal_model_set_search_query (model, priv->memo_sexp); 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_todo_view (gcal); */ /* Timeout check to hide completed items */ #if 0 /* KILL-BONOBO */ priv->update_timeout = g_timeout_add_full ( G_PRIORITY_LOW, 60000, (GSourceFunc) update_todo_view_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) { gcal->priv = GNOME_CALENDAR_GET_PRIVATE (gcal); gcal->priv->todo_update_lock = g_mutex_new (); gcal->priv->dn_query_lock = g_mutex_new (); 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->dn_queries = NULL; gcal->priv->sexp = g_strdup ("#t"); /* Match all */ gcal->priv->todo_sexp = g_strdup ("#t"); gcal->priv->memo_sexp = g_strdup ("#t"); 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->model != NULL) { g_signal_handlers_disconnect_by_func ( priv->model, view_progress_cb, object); g_signal_handlers_disconnect_by_func ( priv->model, view_complete_cb, 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; } } free_dn_queries (GNOME_CALENDAR (object)); 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_free (priv->todo_update_lock); g_mutex_free (priv->dn_query_lock); /* 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; gint 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, 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, 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)); 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 (void) { return g_object_new (GNOME_TYPE_CALENDAR, NULL); } 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 (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 (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 (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; gint 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 == 0 && (!week_view->multi_week_view || week_view->compress_weekend)) 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_todo_view (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) { gchar *sexp, *start, *end; GList *clients, *l; g_return_if_fail (GNOME_IS_CALENDAR (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 */ clients = e_cal_model_get_client_list (gnome_calendar_get_model (gcal)); for (l = clients; l != NULL; l = l->next) { ECalClient *client = l->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 (clients); gcal_update_status_message (gcal, NULL, -1); g_free (sexp); g_free (start); g_free (end); }