/*
*
* Evolution calendar - Utilities for tagging ECalendar widgets
*
* 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 <http://www.gnu.org/licenses/>
*
*
* Authors:
* Damon Chaplin <damon@ximian.com>
* Federico Mena-Quintero <federico@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <libecal/e-cal-time-util.h>
#include "shell/e-shell.h"
#include "shell/e-shell-settings.h"
#include "calendar-config.h"
#include "tag-calendar.h"
struct calendar_tag_closure {
ECalendarItem *calitem;
icaltimezone *zone;
time_t start_time;
time_t end_time;
gboolean skip_transparent_events;
gboolean recur_events_italic;
};
/* Clears all the tags in a calendar and fills a closure structure with the
* necessary information for iterating over occurrences. Returns FALSE if
* the calendar has no dates shown. */
static gboolean
prepare_tag (ECalendar *ecal,
struct calendar_tag_closure *c,
icaltimezone *zone,
gboolean clear_first)
{
gint start_year, start_month, start_day;
gint end_year, end_month, end_day;
struct icaltimetype start_tt = icaltime_null_time ();
struct icaltimetype end_tt = icaltime_null_time ();
if (clear_first)
e_calendar_item_clear_marks (ecal->calitem);
if (!e_calendar_item_get_date_range (
ecal->calitem,
&start_year, &start_month, &start_day,
&end_year, &end_month, &end_day))
return FALSE;
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);
c->calitem = ecal->calitem;
if (zone)
c->zone = zone;
else
c->zone = calendar_config_get_icaltimezone ();
c->start_time = icaltime_as_timet_with_zone (start_tt, c->zone);
c->end_time = icaltime_as_timet_with_zone (end_tt, c->zone);
return TRUE;
}
/* Marks the specified range in an ECalendar;
* called from e_cal_generate_instances() */
static gboolean
tag_calendar_cb (ECalComponent *comp,
time_t istart,
time_t iend,
gpointer data)
{
struct calendar_tag_closure *c = data;
struct icaltimetype start_tt, end_tt;
ECalComponentTransparency transparency;
guint8 style = 0;
/* If we are skipping TRANSPARENT events, return if the event is
* transparent. */
e_cal_component_get_transparency (comp, &transparency);
if (transparency == E_CAL_COMPONENT_TRANSP_TRANSPARENT) {
if (c->skip_transparent_events)
return TRUE;
style = E_CALENDAR_ITEM_MARK_ITALIC;
} else if (c->recur_events_italic && e_cal_component_is_instance (comp)) {
style = E_CALENDAR_ITEM_MARK_ITALIC;
} else {
style = E_CALENDAR_ITEM_MARK_BOLD;
}
start_tt = icaltime_from_timet_with_zone (istart, FALSE, c->zone);
end_tt = icaltime_from_timet_with_zone (iend - 1, FALSE, c->zone);
e_calendar_item_mark_days (
c->calitem,
start_tt.year, start_tt.month - 1, start_tt.day,
end_tt.year, end_tt.month - 1, end_tt.day,
style, TRUE);
return TRUE;
}
static gboolean
get_recur_events_italic (void)
{
EShell *shell;
EShellSettings *shell_settings;
shell = e_shell_get_default ();
shell_settings = e_shell_get_shell_settings (shell);
return e_shell_settings_get_boolean (shell_settings, "cal-recur-events-italic");
}
/**
* tag_calendar_by_client:
* @ecal: Calendar widget to tag.
* @client: A calendar client object.
* @cancellable: A #GCancellable; can be %NULL
*
* Tags an #ECalendar widget with the events that occur in its current time
* range. The occurrences are extracted from the specified calendar @client.
**/
void
tag_calendar_by_client (ECalendar *ecal,
ECalClient *client,
GCancellable *cancellable)
{
struct calendar_tag_closure *c;
g_return_if_fail (E_IS_CALENDAR (ecal));
g_return_if_fail (E_IS_CAL_CLIENT (client));
/* If the ECalendar isn't visible, we just return. */
if (!gtk_widget_get_visible (GTK_WIDGET (ecal)))
return;
if (!e_client_is_opened (E_CLIENT (client)))
return;
c = g_new0 (struct calendar_tag_closure, 1);
if (!prepare_tag (ecal, c, NULL, TRUE)) {
g_free (c);
return;
}
c->skip_transparent_events = TRUE;
c->recur_events_italic = get_recur_events_italic ();
e_cal_client_generate_instances (
client, c->start_time, c->end_time, cancellable, tag_calendar_cb, c, g_free);
}
/* Resolves TZIDs for the recurrence generator, for when the comp is not on
* the server. We need to try to use builtin timezones first, as they may not
* be added to the server yet. */
static icaltimezone *
resolve_tzid_cb (const gchar *tzid,
gpointer data)
{
ECalClient *client;
icaltimezone *zone = NULL;
g_return_val_if_fail (E_IS_CAL_CLIENT (data), NULL);
client = E_CAL_CLIENT (data);
/* Try to find the builtin timezone first. */
zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
if (!zone && tzid) {
/* FIXME: Handle errors. */
GError *error = NULL;
e_cal_client_get_timezone_sync (
client, tzid, &zone, NULL, &error);
if (error != NULL) {
g_warning (
"%s: Failed to get timezone '%s': %s",
G_STRFUNC, tzid, error->message);
g_error_free (error);
}
}
return zone;
}
/**
* tag_calendar_by_comp:
* @ecal: Calendar widget to tag.
* @comp: A calendar component object.
* @clear_first: Whether the #ECalendar should be cleared of any marks first.
*
* Tags an #ECalendar widget with any occurrences of a specific calendar
* component that occur within the calendar's current time range.
* Note that TRANSPARENT events are also tagged here.
*
* If comp_is_on_server is FALSE, it will try to resolve TZIDs using builtin
* timezones first, before querying the server, since the timezones may not
* have been added to the calendar on the server yet.
**/
void
tag_calendar_by_comp (ECalendar *ecal,
ECalComponent *comp,
ECalClient *client,
icaltimezone *display_zone,
gboolean clear_first,
gboolean comp_is_on_server,
gboolean can_recur_events_italic,
GCancellable *cancellable)
{
struct calendar_tag_closure c;
g_return_if_fail (E_IS_CALENDAR (ecal));
g_return_if_fail (E_IS_CAL_COMPONENT (comp));
/* If the ECalendar isn't visible, we just return. */
if (!gtk_widget_get_visible (GTK_WIDGET (ecal)))
return;
if (!prepare_tag (ecal, &c, display_zone, clear_first))
return;
c.skip_transparent_events = FALSE;
c.recur_events_italic = can_recur_events_italic && get_recur_events_italic ();
if (comp_is_on_server) {
struct calendar_tag_closure *closure = g_new0 (struct calendar_tag_closure, 1);
*closure = c;
e_cal_client_generate_instances_for_object (
client, e_cal_component_get_icalcomponent (comp),
c.start_time, c.end_time, cancellable, tag_calendar_cb, closure, g_free);
} else
e_cal_recur_generate_instances (
comp, c.start_time, c.end_time,
tag_calendar_cb, &c, resolve_tzid_cb,
client, c.zone);
}