422 lines
9.9 KiB
C
422 lines
9.9 KiB
C
/*
|
||
* Evolution calendar - Widget utilities
|
||
*
|
||
* 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:
|
||
* Federico Mena-Quintero <federico@ximian.com>
|
||
*
|
||
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
||
*
|
||
*/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
#include <config.h>
|
||
#endif
|
||
|
||
#include <ctype.h>
|
||
#include <string.h>
|
||
#include <libical/ical.h>
|
||
#include <glib/gi18n.h>
|
||
#include <libedataserver/e-time-utils.h>
|
||
#include <libecal/e-cal-time-util.h>
|
||
#include "widgets/misc/e-dateedit.h"
|
||
#include "../itip-utils.h"
|
||
#include <shell/e-shell.h>
|
||
#include "comp-editor-util.h"
|
||
|
||
|
||
|
||
/**
|
||
* comp_editor_dates:
|
||
* @dates: A structure to be filled out with dates of a component
|
||
* @comp: The component to extract the dates from
|
||
*
|
||
* Extracts the dates from the calendar component into the
|
||
* CompEditorPageDates structure. Call comp_editor_free_dates() to free the
|
||
* results.
|
||
**/
|
||
void
|
||
comp_editor_dates (CompEditorPageDates *dates, ECalComponent *comp)
|
||
{
|
||
ECalComponentDateTime dt;
|
||
|
||
dates->start = NULL;
|
||
dates->end = NULL;
|
||
dates->due = NULL;
|
||
dates->complete = NULL;
|
||
|
||
/* Note that the ECalComponentDateTime's returned contain allocated
|
||
icaltimetype and tzid values, so we just take over ownership of
|
||
those. */
|
||
e_cal_component_get_dtstart (comp, &dt);
|
||
if (dt.value) {
|
||
dates->start = g_new (ECalComponentDateTime, 1);
|
||
*dates->start = dt;
|
||
}
|
||
|
||
e_cal_component_get_dtend (comp, &dt);
|
||
if (dt.value) {
|
||
dates->end = g_new (ECalComponentDateTime, 1);
|
||
*dates->end = dt;
|
||
}
|
||
|
||
e_cal_component_get_due (comp, &dt);
|
||
if (dt.value) {
|
||
dates->due = g_new (ECalComponentDateTime, 1);
|
||
*dates->due = dt;
|
||
}
|
||
|
||
e_cal_component_get_completed (comp, &dates->complete);
|
||
}
|
||
|
||
/* This frees the dates in the CompEditorPageDates struct. But it doesn't free
|
||
* the struct (as that is usually static).
|
||
*/
|
||
void
|
||
comp_editor_free_dates (CompEditorPageDates *dates)
|
||
{
|
||
/* Note that e_cal_component_free_datetime() only frees the fields in
|
||
the struct. It doesn't free the struct itself, so we do that. */
|
||
if (dates->start) {
|
||
e_cal_component_free_datetime (dates->start);
|
||
g_free (dates->start);
|
||
}
|
||
|
||
if (dates->end) {
|
||
e_cal_component_free_datetime (dates->end);
|
||
g_free (dates->end);
|
||
}
|
||
|
||
if (dates->due) {
|
||
e_cal_component_free_datetime (dates->due);
|
||
g_free (dates->due);
|
||
}
|
||
|
||
if (dates->complete)
|
||
e_cal_component_free_icaltimetype (dates->complete);
|
||
}
|
||
|
||
/**
|
||
* comp_editor_new_date_edit:
|
||
* @show_date: Whether to show a date picker in the widget.
|
||
* @show_time: Whether to show a time picker in the widget.
|
||
* @make_time_insensitive: Whether the time field is made insensitive rather
|
||
* than hiding it. This is useful if you want to preserve the layout of the
|
||
* widgets.
|
||
*
|
||
* Creates a new #EDateEdit widget, configured using the calendar's preferences.
|
||
*
|
||
* Return value: A newly-created #EDateEdit widget.
|
||
**/
|
||
GtkWidget *
|
||
comp_editor_new_date_edit (gboolean show_date,
|
||
gboolean show_time,
|
||
gboolean make_time_insensitive)
|
||
{
|
||
EDateEdit *dedit;
|
||
|
||
dedit = E_DATE_EDIT (e_date_edit_new ());
|
||
|
||
e_date_edit_set_show_date (dedit, show_date);
|
||
e_date_edit_set_show_time (dedit, show_time);
|
||
#if 0
|
||
e_date_edit_set_make_time_insensitive (dedit, make_time_insensitive);
|
||
#else
|
||
e_date_edit_set_make_time_insensitive (dedit, FALSE);
|
||
#endif
|
||
|
||
return GTK_WIDGET (dedit);
|
||
}
|
||
|
||
/* Returns the current time, for EDateEdit widgets and ECalendar items in the
|
||
dialogs.
|
||
FIXME: Should probably use the timezone from somewhere in the component
|
||
rather than the current timezone. */
|
||
struct tm
|
||
comp_editor_get_current_time (EDateEdit *date_edit,
|
||
CompEditor *editor)
|
||
{
|
||
icaltimezone *zone;
|
||
struct icaltimetype tt;
|
||
struct tm tmp_tm = { 0 };
|
||
|
||
/* Get the current timezone. */
|
||
zone = comp_editor_get_timezone (editor);
|
||
|
||
tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
|
||
|
||
/* Now copy it to the struct tm and return it. */
|
||
tmp_tm.tm_year = tt.year - 1900;
|
||
tmp_tm.tm_mon = tt.month - 1;
|
||
tmp_tm.tm_mday = tt.day;
|
||
tmp_tm.tm_hour = tt.hour;
|
||
tmp_tm.tm_min = tt.minute;
|
||
tmp_tm.tm_sec = tt.second;
|
||
tmp_tm.tm_isdst = -1;
|
||
|
||
return tmp_tm;
|
||
}
|
||
|
||
/**
|
||
* comp_editor_strip_categories:
|
||
* @categories: A string of category names entered by the user.
|
||
*
|
||
* Takes a string of the form "categ, categ, categ, ..." and removes the
|
||
* whitespace between categories to result in "categ,categ,categ,..."
|
||
*
|
||
* Return value: The category names stripped of surrounding whitespace
|
||
* and separated with commas.
|
||
**/
|
||
gchar *
|
||
comp_editor_strip_categories (const gchar *categories)
|
||
{
|
||
gchar *new_categories;
|
||
const gchar *start, *end;
|
||
const gchar *p;
|
||
gchar *new_p;
|
||
|
||
if (!categories)
|
||
return NULL;
|
||
|
||
new_categories = g_new (char, strlen (categories) + 1);
|
||
|
||
start = end = NULL;
|
||
new_p = new_categories;
|
||
|
||
for (p = categories; *p; p = g_utf8_next_char (p)) {
|
||
gunichar c;
|
||
|
||
c = g_utf8_get_char (p);
|
||
|
||
if (g_unichar_isspace (c))
|
||
continue;
|
||
else if (c == ',') {
|
||
gint len;
|
||
|
||
if (!start)
|
||
continue;
|
||
|
||
g_return_val_if_fail (start <= end, NULL);
|
||
|
||
len = end - start + 1;
|
||
strncpy (new_p, start, len);
|
||
new_p[len] = ',';
|
||
new_p += len + 1;
|
||
|
||
start = end = NULL;
|
||
} else {
|
||
if (!start) {
|
||
start = p;
|
||
end = p;
|
||
} else
|
||
end = g_utf8_next_char (p) - 1;
|
||
}
|
||
}
|
||
|
||
if (start) {
|
||
gint len;
|
||
|
||
g_return_val_if_fail (start <= end, NULL);
|
||
|
||
len = end - start + 1;
|
||
strncpy (new_p, start, len);
|
||
new_p += len;
|
||
}
|
||
|
||
*new_p = '\0';
|
||
|
||
return new_categories;
|
||
}
|
||
|
||
static GSList *
|
||
manage_new_attendees (const GSList *lst, const gchar *eml, gboolean add)
|
||
{
|
||
GSList *copy = NULL;
|
||
const GSList *l;
|
||
gboolean found = FALSE;
|
||
|
||
g_return_val_if_fail (eml != NULL, NULL);
|
||
|
||
for (l = lst; l; l = l->next) {
|
||
const gchar *eml2 = l->data;
|
||
|
||
if (!eml2)
|
||
continue;
|
||
|
||
if (g_ascii_strcasecmp (eml, eml2) == 0) {
|
||
found = TRUE;
|
||
if (add)
|
||
copy = g_slist_append (copy, g_strdup (eml2));
|
||
} else {
|
||
copy = g_slist_append (copy, g_strdup (eml2));
|
||
}
|
||
}
|
||
|
||
if (!found && add) {
|
||
copy = g_slist_append (copy, g_strdup (eml));
|
||
}
|
||
|
||
return copy;
|
||
}
|
||
|
||
static void
|
||
free_slist_strs (gpointer data)
|
||
{
|
||
GSList *lst = data;
|
||
|
||
if (lst) {
|
||
g_slist_foreach (lst, (GFunc) g_free, NULL);
|
||
g_slist_free (lst);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* comp_editor_manage_new_attendees:
|
||
* @comp: The component.
|
||
* @ma: An attendee.
|
||
* @add: %TRUE to add attendee's email to new-attendees, %FALSE to remove
|
||
* from it.
|
||
*
|
||
* Manages the 'new-attendees' string of new attendees of the component.
|
||
*
|
||
* <note>
|
||
* <para>
|
||
* The list is just string of emails separated by ';'
|
||
* </para>
|
||
* </note>
|
||
**/
|
||
void
|
||
comp_editor_manage_new_attendees (ECalComponent *comp,
|
||
EMeetingAttendee *ma,
|
||
gboolean add)
|
||
{
|
||
const gchar *eml;
|
||
|
||
g_return_if_fail (comp != NULL);
|
||
g_return_if_fail (ma != NULL);
|
||
|
||
eml = e_meeting_attendee_get_address (ma);
|
||
if (eml)
|
||
eml = itip_strip_mailto (eml);
|
||
g_return_if_fail (eml != NULL);
|
||
|
||
g_object_set_data_full (
|
||
G_OBJECT (comp), "new-attendees",
|
||
manage_new_attendees (
|
||
g_object_get_data (G_OBJECT (comp), "new-attendees"),
|
||
eml, add), free_slist_strs);
|
||
}
|
||
|
||
/**
|
||
* comp_editor_copy_new_attendees:
|
||
* @des: Component, to copy to.
|
||
* @src: Component, to copy from.
|
||
*
|
||
* Copies "new-attendees" information from @src to @des component.
|
||
**/
|
||
void
|
||
comp_editor_copy_new_attendees (ECalComponent *des, ECalComponent *src)
|
||
{
|
||
GSList *copy = NULL, *l;
|
||
|
||
g_return_if_fail (src != NULL);
|
||
g_return_if_fail (des != NULL);
|
||
|
||
for (l = g_object_get_data (G_OBJECT (src), "new-attendees"); l; l = l->next) {
|
||
copy = g_slist_append (copy, g_strdup (l->data));
|
||
}
|
||
|
||
g_object_set_data_full (G_OBJECT (des), "new-attendees", copy, free_slist_strs);
|
||
}
|
||
|
||
/**
|
||
* comp_editor_have_in_new_attendees:
|
||
* @comp: Component with the "new-attendees" possibly set.
|
||
* @ma: Meeting attendee to check.
|
||
*
|
||
* Returns: Whether @ma is present in the list of new attendees of the comp.
|
||
**/
|
||
gboolean
|
||
comp_editor_have_in_new_attendees (ECalComponent *comp, EMeetingAttendee *ma)
|
||
{
|
||
const gchar *eml;
|
||
|
||
g_return_val_if_fail (comp != NULL, FALSE);
|
||
g_return_val_if_fail (ma != NULL, FALSE);
|
||
|
||
eml = e_meeting_attendee_get_address (ma);
|
||
if (eml)
|
||
eml = itip_strip_mailto (eml);
|
||
g_return_val_if_fail (eml != NULL, FALSE);
|
||
|
||
return comp_editor_have_in_new_attendees_lst (
|
||
g_object_get_data (G_OBJECT (comp), "new-attendees"), eml);
|
||
}
|
||
|
||
/**
|
||
* comp_editor_have_in_new_attendees_lst:
|
||
*
|
||
* Same as comp_editor_have_in_new_attendees() only parameters are
|
||
* direct GSList and string.
|
||
**/
|
||
gboolean
|
||
comp_editor_have_in_new_attendees_lst (const GSList *new_attendees,
|
||
const gchar *eml)
|
||
{
|
||
const GSList *l;
|
||
|
||
if (!eml)
|
||
return FALSE;
|
||
|
||
for (l = new_attendees; l; l = l->next) {
|
||
if (l->data && g_ascii_strcasecmp (eml, l->data) == 0)
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* comp_editor_test_time_in_the_past:
|
||
* @time_tt: Time to check.
|
||
* @parent: Parent window for a question dialog.
|
||
* @tag: Question message tag to use.
|
||
* Returns whether given time is in the past.
|
||
*
|
||
* Tests the given @time_tt whether occurs in the past,
|
||
* and if so, returns TRUE.
|
||
**/
|
||
gboolean
|
||
comp_editor_test_time_in_the_past (const struct icaltimetype time_tt)
|
||
{
|
||
struct icaltimetype now_tt;
|
||
gboolean is_past;
|
||
|
||
if (icaltime_is_null_time (time_tt))
|
||
return FALSE;
|
||
|
||
if (time_tt.is_date) {
|
||
now_tt = icaltime_today ();
|
||
is_past = icaltime_compare_date_only (time_tt, now_tt) < 0;
|
||
} else {
|
||
now_tt = icaltime_current_time_with_zone (time_tt.zone);
|
||
now_tt.zone = time_tt.zone;
|
||
is_past = icaltime_compare (time_tt, now_tt) < 0;
|
||
}
|
||
|
||
return is_past;
|
||
}
|