/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* itip-model.c
*
* Copyright (C) 2001 Ximian, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: JP Rosevear
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
#include <libgnomevfs/gnome-vfs.h>
#include <gal/e-table/e-cell-text.h>
#include <gal/e-table/e-cell-popup.h>
#include <gal/e-table/e-cell-combo.h>
#include <e-book.h>
#include <e-card-types.h>
#include <e-card-cursor.h>
#include <e-card.h>
#include <e-card-simple.h>
#include <cal-util/cal-component.h>
#include <cal-util/cal-util.h>
#include <cal-util/timeutil.h>
#include "calendar-config.h"
#include "itip-utils.h"
#include "e-meeting-attendee.h"
#include "e-meeting-model.h"
enum columns {
ITIP_ADDRESS_COL,
ITIP_MEMBER_COL,
ITIP_TYPE_COL,
ITIP_ROLE_COL,
ITIP_RSVP_COL,
ITIP_DELTO_COL,
ITIP_DELFROM_COL,
ITIP_STATUS_COL,
ITIP_CN_COL,
ITIP_LANGUAGE_COL,
ITIP_COLUMN_COUNT
};
struct _EMeetingModelPrivate
{
GPtrArray *attendees;
CalClient *client;
EBook *ebook;
gboolean book_loaded;
gboolean book_load_wait;
GList *refresh_callbacks;
GList *refresh_data;
gint refresh_count;
gboolean refreshing;
};
#define BUF_SIZE 1024
typedef struct _EMeetingModelAttendeeRefreshData EMeetingModelAttendeeRefreshData;
struct _EMeetingModelAttendeeRefreshData {
char buffer[BUF_SIZE];
GString *string;
EMeetingAttendee *ia;
};
typedef struct _EMeetingModelRefreshData EMeetingModelRefreshData;
struct _EMeetingModelRefreshData {
EMeetingModel *im;
EMeetingModelAttendeeRefreshData attendee_data;
};
static void class_init (EMeetingModelClass *klass);
static void init (EMeetingModel *model);
static void destroy (GtkObject *obj);
static void attendee_changed_cb (EMeetingAttendee *ia, gpointer data);
static ETableModelClass *parent_class = NULL;
GtkType
e_meeting_model_get_type (void)
{
static GtkType type = 0;
if (type == 0)
{
static const GtkTypeInfo info =
{
"EMeetingModel",
sizeof (EMeetingModel),
sizeof (EMeetingModelClass),
(GtkClassInitFunc) class_init,
(GtkObjectInitFunc) init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
type = gtk_type_unique (e_table_model_get_type (), &info);
}
return type;
}
static void
book_open_cb (EBook *book, EBookStatus status, gpointer data)
{
EMeetingModel *im = E_MEETING_MODEL (data);
EMeetingModelPrivate *priv;
priv = im->priv;
if (status == E_BOOK_STATUS_SUCCESS)
priv->book_loaded = TRUE;
else
g_warning ("Book not loaded");
if (priv->book_load_wait) {
priv->book_load_wait = FALSE;
gtk_main_quit ();
}
}
static void
start_addressbook_server (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
gchar *uri, *path;
priv = im->priv;
priv->ebook = e_book_new ();
path = g_concat_dir_and_file (g_get_home_dir (),
"evolution/local/Contacts/addressbook.db");
uri = g_strdup_printf ("file://%s", path);
g_free (path);
e_book_load_uri (priv->ebook, uri, book_open_cb, im);
g_free (uri);
}
static EMeetingAttendee *
find_match (EMeetingModel *im, const char *address, int *pos)
{
EMeetingModelPrivate *priv;
EMeetingAttendee *ia;
const gchar *ia_address;
int i;
priv = im->priv;
if (address == NULL)
return NULL;
/* Make sure we can add the new delegatee person */
for (i = 0; i < priv->attendees->len; i++) {
ia = g_ptr_array_index (priv->attendees, i);
ia_address = e_meeting_attendee_get_address (ia);
if (ia_address != NULL && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) {
if (pos != NULL)
*pos = i;
return ia;
}
}
return NULL;
}
static icalparameter_cutype
text_to_type (const char *type)
{
if (!g_strcasecmp (type, _("Individual")))
return ICAL_CUTYPE_INDIVIDUAL;
else if (!g_strcasecmp (type, _("Group")))
return ICAL_CUTYPE_GROUP;
else if (!g_strcasecmp (type, _("Resource")))
return ICAL_CUTYPE_RESOURCE;
else if (!g_strcasecmp (type, _("Room")))
return ICAL_CUTYPE_ROOM;
else
return ICAL_CUTYPE_NONE;
}
static char *
type_to_text (icalparameter_cutype type)
{
switch (type) {
case ICAL_CUTYPE_INDIVIDUAL:
return _("Individual");
case ICAL_CUTYPE_GROUP:
return _("Group");
case ICAL_CUTYPE_RESOURCE:
return _("Resource");
case ICAL_CUTYPE_ROOM:
return _("Room");
default:
return _("Unknown");
}
return NULL;
}
static icalparameter_role
text_to_role (const char *role)
{
if (!g_strcasecmp (role, _("Chair")))
return ICAL_ROLE_CHAIR;
else if (!g_strcasecmp (role, _("Required Participant")))
return ICAL_ROLE_REQPARTICIPANT;
else if (!g_strcasecmp (role, _("Optional Participant")))
return ICAL_ROLE_OPTPARTICIPANT;
else if (!g_strcasecmp (role, _("Non-Participant")))
return ICAL_ROLE_NONPARTICIPANT;
else
return ICAL_ROLE_NONE;
}
static char *
role_to_text (icalparameter_role role)
{
switch (role) {
case ICAL_ROLE_CHAIR:
return _("Chair");
case ICAL_ROLE_REQPARTICIPANT:
return _("Required Participant");
case ICAL_ROLE_OPTPARTICIPANT:
return _("Optional Participant");
case ICAL_ROLE_NONPARTICIPANT:
return _("Non-Participant");
default:
return _("Unknown");
}
return NULL;
}
static gboolean
text_to_boolean (const char *role)
{
if (!g_strcasecmp (role, _("Yes")))
return TRUE;
else
return FALSE;
}
static char *
boolean_to_text (gboolean b)
{
if (b)
return _("Yes");
else
return _("No");
}
static icalparameter_partstat
text_to_partstat (const char *partstat)
{
if (!g_strcasecmp (partstat, _("Needs Action")))
return ICAL_PARTSTAT_NEEDSACTION;
else if (!g_strcasecmp (partstat, _("Accepted")))
return ICAL_PARTSTAT_ACCEPTED;
else if (!g_strcasecmp (partstat, _("Declined")))
return ICAL_PARTSTAT_DECLINED;
else if (!g_strcasecmp (partstat, _("Tentative")))
return ICAL_PARTSTAT_TENTATIVE;
else if (!g_strcasecmp (partstat, _("Delegated")))
return ICAL_PARTSTAT_DELEGATED;
else if (!g_strcasecmp (partstat, _("Completed")))
return ICAL_PARTSTAT_COMPLETED;
else if (!g_strcasecmp (partstat, _("In Process")))
return ICAL_PARTSTAT_INPROCESS;
else
return ICAL_PARTSTAT_NONE;
}
static char *
partstat_to_text (icalparameter_partstat partstat)
{
switch (partstat) {
case ICAL_PARTSTAT_NEEDSACTION:
return _("Needs Action");
case ICAL_PARTSTAT_ACCEPTED:
return _("Accepted");
case ICAL_PARTSTAT_DECLINED:
return _("Declined");
case ICAL_PARTSTAT_TENTATIVE:
return _("Tentative");
case ICAL_PARTSTAT_DELEGATED:
return _("Delegated");
case ICAL_PARTSTAT_COMPLETED:
return _("Completed");
case ICAL_PARTSTAT_INPROCESS:
return _("In Process");
case ICAL_PARTSTAT_NONE:
default:
return _("Unknown");
}
return NULL;
}
static int
column_count (ETableModel *etm)
{
return ITIP_COLUMN_COUNT;
}
static int
row_count (ETableModel *etm)
{
EMeetingModel *im;
EMeetingModelPrivate *priv;
im = E_MEETING_MODEL (etm);
priv = im->priv;
return (priv->attendees->len);
}
static void
append_row (ETableModel *etm, ETableModel *source, int row)
{
EMeetingModel *im;
EMeetingModelPrivate *priv;
EMeetingAttendee *ia;
char *address;
im = E_MEETING_MODEL (etm);
priv = im->priv;
address = (char *) e_table_model_value_at (source, ITIP_ADDRESS_COL, row);
if (find_match (im, address, NULL) != NULL) {
// duplicate_error ();
return;
}
ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", address));
e_meeting_attendee_set_member (ia, g_strdup (e_table_model_value_at (source, ITIP_MEMBER_COL, row)));
e_meeting_attendee_set_cutype (ia, text_to_type (e_table_model_value_at (source, ITIP_TYPE_COL, row)));
e_meeting_attendee_set_role (ia, text_to_role (e_table_model_value_at (source, ITIP_ROLE_COL, row)));
e_meeting_attendee_set_rsvp (ia, text_to_boolean (e_table_model_value_at (source, ITIP_RSVP_COL, row)));
e_meeting_attendee_set_delto (ia, g_strdup (e_table_model_value_at (source, ITIP_DELTO_COL, row)));
e_meeting_attendee_set_delfrom (ia, g_strdup (e_table_model_value_at (source, ITIP_DELFROM_COL, row)));
e_meeting_attendee_set_status (ia, text_to_partstat (e_table_model_value_at (source, ITIP_STATUS_COL, row)));
e_meeting_attendee_set_cn (ia, g_strdup (e_table_model_value_at (source, ITIP_CN_COL, row)));
e_meeting_attendee_set_language (ia, g_strdup (e_table_model_value_at (source, ITIP_LANGUAGE_COL, row)));
e_meeting_model_add_attendee (E_MEETING_MODEL (etm), ia);
}
static void *
value_at (ETableModel *etm, int col, int row)
{
EMeetingModel *im;
EMeetingModelPrivate *priv;
EMeetingAttendee *ia;
im = E_MEETING_MODEL (etm);
priv = im->priv;
ia = g_ptr_array_index (priv->attendees, row);
switch (col) {
case ITIP_ADDRESS_COL:
return (void *)itip_strip_mailto (e_meeting_attendee_get_address (ia));
case ITIP_MEMBER_COL:
return (void *)e_meeting_attendee_get_member (ia);
case ITIP_TYPE_COL:
return type_to_text (e_meeting_attendee_get_cutype (ia));
case ITIP_ROLE_COL:
return role_to_text (e_meeting_attendee_get_role (ia));
case ITIP_RSVP_COL:
return boolean_to_text (e_meeting_attendee_get_rsvp (ia));
case ITIP_DELTO_COL:
return (void *)itip_strip_mailto (e_meeting_attendee_get_delto (ia));
case ITIP_DELFROM_COL:
return (void *)itip_strip_mailto (e_meeting_attendee_get_delfrom (ia));
case ITIP_STATUS_COL:
return partstat_to_text (e_meeting_attendee_get_status (ia));
case ITIP_CN_COL:
return (void *)e_meeting_attendee_get_cn (ia);
case ITIP_LANGUAGE_COL:
return (void *)e_meeting_attendee_get_language (ia);
}
return NULL;
}
static void
set_value_at (ETableModel *etm, int col, int row, const void *val)
{
EMeetingModel *im;
EMeetingModelPrivate *priv;
EMeetingAttendee *ia;
im = E_MEETING_MODEL (etm);
priv = im->priv;
ia = g_ptr_array_index (priv->attendees, row);
switch (col) {
case ITIP_ADDRESS_COL:
e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", (char *) val));
break;
case ITIP_MEMBER_COL:
e_meeting_attendee_set_member (ia, g_strdup (val));
break;
case ITIP_TYPE_COL:
e_meeting_attendee_set_cutype (ia, text_to_type (val));
break;
case ITIP_ROLE_COL:
e_meeting_attendee_set_role (ia, text_to_role (val));
break;
case ITIP_RSVP_COL:
e_meeting_attendee_set_rsvp (ia, text_to_boolean (val));
break;
case ITIP_DELTO_COL:
e_meeting_attendee_set_delto (ia, g_strdup (val));
break;
case ITIP_DELFROM_COL:
e_meeting_attendee_set_delfrom (ia, g_strdup (val));
break;
case ITIP_STATUS_COL:
e_meeting_attendee_set_status (ia, text_to_partstat (val));
break;
case ITIP_CN_COL:
e_meeting_attendee_set_cn (ia, g_strdup (val));
break;
case ITIP_LANGUAGE_COL:
e_meeting_attendee_set_language (ia, g_strdup (val));
break;
}
}
static gboolean
is_cell_editable (ETableModel *etm, int col, int row)
{
switch (col) {
case ITIP_DELTO_COL:
case ITIP_DELFROM_COL:
return FALSE;
default:
}
return TRUE;
}
static void *
duplicate_value (ETableModel *etm, int col, const void *val)
{
return g_strdup (val);
}
static void
free_value (ETableModel *etm, int col, void *val)
{
g_free (val);
}
static void *
init_value (ETableModel *etm, int col)
{
switch (col) {
case ITIP_ADDRESS_COL:
return g_strdup ("");
case ITIP_MEMBER_COL:
return g_strdup ("");
case ITIP_TYPE_COL:
return g_strdup (_("Individual"));
case ITIP_ROLE_COL:
return g_strdup (_("Required Participant"));
case ITIP_RSVP_COL:
return g_strdup (_("Yes"));
case ITIP_DELTO_COL:
return g_strdup ("");
case ITIP_DELFROM_COL:
return g_strdup ("");
case ITIP_STATUS_COL:
return g_strdup (_("Needs Action"));
case ITIP_CN_COL:
return g_strdup ("");
case ITIP_LANGUAGE_COL:
return g_strdup ("en");
}
return g_strdup ("");
}
static gboolean
value_is_empty (ETableModel *etm, int col, const void *val)
{
switch (col) {
case ITIP_ADDRESS_COL:
case ITIP_MEMBER_COL:
case ITIP_DELTO_COL:
case ITIP_DELFROM_COL:
case ITIP_CN_COL:
if (val && !g_strcasecmp (val, ""))
return TRUE;
else
return FALSE;
default:
}
return TRUE;
}
static char *
value_to_string (ETableModel *etm, int col, const void *val)
{
return g_strdup (val);
}
static void
class_init (EMeetingModelClass *klass)
{
GtkObjectClass *object_class;
ETableModelClass *etm_class;
object_class = GTK_OBJECT_CLASS (klass);
etm_class = E_TABLE_MODEL_CLASS (klass);
parent_class = gtk_type_class (E_TABLE_MODEL_TYPE);
object_class->destroy = destroy;
etm_class->column_count = column_count;
etm_class->row_count = row_count;
etm_class->value_at = value_at;
etm_class->set_value_at = set_value_at;
etm_class->is_cell_editable = is_cell_editable;
etm_class->append_row = append_row;
etm_class->duplicate_value = duplicate_value;
etm_class->free_value = free_value;
etm_class->initialize_value = init_value;
etm_class->value_is_empty = value_is_empty;
etm_class->value_to_string = value_to_string;
}
static void
init (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
priv = g_new0 (EMeetingModelPrivate, 1);
im->priv = priv;
priv->attendees = g_ptr_array_new ();
priv->client = NULL;
priv->ebook = NULL;
priv->book_loaded = FALSE;
priv->book_load_wait = FALSE;
priv->refreshing = FALSE;
start_addressbook_server (im);
}
static void
destroy (GtkObject *obj)
{
EMeetingModel *model = E_MEETING_MODEL (obj);
EMeetingModelPrivate *priv;
gint i;
priv = model->priv;
for (i = 0; i < priv->attendees->len; i++)
gtk_object_unref (GTK_OBJECT (g_ptr_array_index(priv->attendees, i)));
g_ptr_array_free (priv->attendees, FALSE);
if (priv->client != NULL)
gtk_object_unref (GTK_OBJECT (priv->client));
if (priv->ebook != NULL)
gtk_object_unref (GTK_OBJECT (priv->ebook));
g_free (priv);
}
GtkObject *
e_meeting_model_new (void)
{
return gtk_type_new (E_TYPE_MEETING_MODEL);
}
CalClient *
e_meeting_model_get_cal_client (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
priv = im->priv;
return priv->client;
}
void
e_meeting_model_set_cal_client (EMeetingModel *im, CalClient *client)
{
EMeetingModelPrivate *priv;
priv = im->priv;
if (priv->client != NULL)
gtk_object_unref (GTK_OBJECT (priv->client));
if (client != NULL)
gtk_object_ref (GTK_OBJECT (client));
priv->client = client;
}
static ETableScrolled *
build_etable (ETableModel *model, const gchar *spec_file, const gchar *state_file)
{
GtkWidget *etable;
ETable *real_table;
ETableExtras *extras;
GList *strings;
ECell *popup_cell, *cell;
extras = e_table_extras_new ();
/* For type */
cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
popup_cell = e_cell_combo_new ();
e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
gtk_object_unref (GTK_OBJECT (cell));
strings = NULL;
strings = g_list_append (strings, _("Individual"));
strings = g_list_append (strings, _("Group"));
strings = g_list_append (strings, _("Resource"));
strings = g_list_append (strings, _("Room"));
strings = g_list_append (strings, _("Unknown"));
e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings);
e_table_extras_add_cell (extras, "typeedit", popup_cell);
/* For role */
cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
popup_cell = e_cell_combo_new ();
e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
gtk_object_unref (GTK_OBJECT (cell));
strings = NULL;
strings = g_list_append (strings, _("Chair"));
strings = g_list_append (strings, _("Required Participant"));
strings = g_list_append (strings, _("Optional Participant"));
strings = g_list_append (strings, _("Non-Participant"));
strings = g_list_append (strings, _("Unknown"));
e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings);
e_table_extras_add_cell (extras, "roleedit", popup_cell);
/* For rsvp */
cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
popup_cell = e_cell_combo_new ();
e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
gtk_object_unref (GTK_OBJECT (cell));
strings = NULL;
strings = g_list_append (strings, _("Yes"));
strings = g_list_append (strings, _("No"));
e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings);
e_table_extras_add_cell (extras, "rsvpedit", popup_cell);
/* For status */
cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
popup_cell = e_cell_combo_new ();
e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
gtk_object_unref (GTK_OBJECT (cell));
strings = NULL;
strings = g_list_append (strings, _("Needs Action"));
strings = g_list_append (strings, _("Accepted"));
strings = g_list_append (strings, _("Declined"));
strings = g_list_append (strings, _("Tentative"));
strings = g_list_append (strings, _("Delegated"));
e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings);
e_table_extras_add_cell (extras, "statusedit", popup_cell);
etable = e_table_scrolled_new_from_spec_file (model, extras, spec_file, NULL);
real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (etable));
e_scroll_frame_set_policy (E_SCROLL_FRAME (etable), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
e_scroll_frame_set_scrollbar_spacing (E_SCROLL_FRAME (etable), 0);
e_table_load_state (real_table, state_file);
#if 0
gtk_signal_connect (GTK_OBJECT (real_table),
"right_click", GTK_SIGNAL_FUNC (right_click_cb), mpage);
gtk_signal_connect (GTK_OBJECT (real_table->sort_info),
"sort_info_changed", GTK_SIGNAL_FUNC (sort_info_changed_cb), mts);
#endif
gtk_object_unref (GTK_OBJECT (extras));
return E_TABLE_SCROLLED (etable);
}
void
e_meeting_model_add_attendee (EMeetingModel *im, EMeetingAttendee *ia)
{
EMeetingModelPrivate *priv;
priv = im->priv;
gtk_object_ref (GTK_OBJECT (ia));
g_ptr_array_add (priv->attendees, ia);
gtk_signal_connect (GTK_OBJECT (ia), "changed",
GTK_SIGNAL_FUNC (attendee_changed_cb), im);
e_table_model_row_inserted (E_TABLE_MODEL (im), row_count (E_TABLE_MODEL (im)) - 1);
}
EMeetingAttendee *
e_meeting_model_add_attendee_with_defaults (EMeetingModel *im)
{
EMeetingAttendee *ia;
char *str;
ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
e_meeting_attendee_set_address (ia, init_value (E_TABLE_MODEL (im), ITIP_ADDRESS_COL));
e_meeting_attendee_set_member (ia, init_value (E_TABLE_MODEL (im), ITIP_MEMBER_COL));
str = init_value (E_TABLE_MODEL (im), ITIP_TYPE_COL);
e_meeting_attendee_set_cutype (ia, text_to_type (str));
g_free (str);
str = init_value (E_TABLE_MODEL (im), ITIP_ROLE_COL);
e_meeting_attendee_set_role (ia, text_to_role (str));
g_free (str);
str = init_value (E_TABLE_MODEL (im), ITIP_RSVP_COL);
e_meeting_attendee_set_role (ia, text_to_boolean (str));
g_free (str);
e_meeting_attendee_set_delto (ia, init_value (E_TABLE_MODEL (im), ITIP_DELTO_COL));
e_meeting_attendee_set_delfrom (ia, init_value (E_TABLE_MODEL (im), ITIP_DELFROM_COL));
str = init_value (E_TABLE_MODEL (im), ITIP_STATUS_COL);
e_meeting_attendee_set_status (ia, text_to_partstat (str));
g_free (str);
e_meeting_attendee_set_cn (ia, init_value (E_TABLE_MODEL (im), ITIP_CN_COL));
e_meeting_attendee_set_language (ia, init_value (E_TABLE_MODEL (im), ITIP_LANGUAGE_COL));
e_meeting_model_add_attendee (im, ia);
return ia;
}
void
e_meeting_model_remove_attendee (EMeetingModel *im, EMeetingAttendee *ia)
{
EMeetingModelPrivate *priv;
gint i, row = -1;
priv = im->priv;
for (i = 0; i < priv->attendees->len; i++) {
if (ia == g_ptr_array_index (priv->attendees, i)) {
row = i;
break;
}
}
if (row != -1) {
g_ptr_array_remove_index (priv->attendees, row);
gtk_object_unref (GTK_OBJECT (ia));
e_table_model_row_deleted (E_TABLE_MODEL (im), row);
}
}
void
e_meeting_model_remove_all_attendees (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
gint i;
priv = im->priv;
for (i = 0; i < priv->attendees->len; i++) {
EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i);
gtk_object_unref (GTK_OBJECT (ia));
}
e_table_model_rows_deleted (E_TABLE_MODEL (im), 0, priv->attendees->len);
g_ptr_array_set_size (priv->attendees, 0);
}
EMeetingAttendee *
e_meeting_model_find_attendee (EMeetingModel *im, const gchar *address, gint *row)
{
EMeetingModelPrivate *priv;
EMeetingAttendee *ia;
int i;
priv = im->priv;
if (address == NULL)
return NULL;
for (i = 0; i < priv->attendees->len; i++) {
const gchar *ia_address;
ia = g_ptr_array_index (priv->attendees, i);
ia_address = e_meeting_attendee_get_address (ia);
if (ia_address && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) {
if (row != NULL)
*row = i;
return ia;
}
}
return NULL;
}
EMeetingAttendee *
e_meeting_model_find_attendee_at_row (EMeetingModel *im, gint row)
{
EMeetingModelPrivate *priv;
priv = im->priv;
return g_ptr_array_index (priv->attendees, row);
}
gint
e_meeting_model_count_attendees (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
priv = im->priv;
return priv->attendees->len;
}
const GPtrArray *
e_meeting_model_get_attendees (EMeetingModel *im)
{
EMeetingModelPrivate *priv;
priv = im->priv;
return priv->attendees;
}
static icaltimezone *
find_zone (icalproperty *ip, icalcomponent *tz_top_level)
{
icalparameter *param;
icalcomponent *sub_comp;
const char *tzid;
icalcompiter iter;
if (tz_top_level == NULL)
return NULL;
param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER);
tzid = icalparameter_get_tzid (param);
iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT);
while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
icalcomponent *clone;
const char *tz_tzid;
tz_tzid = icalproperty_get_tzid (sub_comp);
if (!strcmp (tzid, tz_tzid)) {
icaltimezone *zone;
zone = icaltimezone_new ();
clone = icalcomponent_new_clone (sub_comp);
icaltimezone_set_component (zone, clone);
return zone;
}
icalcompiter_next (&iter);
}
return NULL;
}
static struct icaltimetype
convert_time (struct icaltimetype itt, icaltimezone *from, icaltimezone *to)
{
if (from == NULL)
from = icaltimezone_get_utc_timezone ();
icaltimezone_convert_time (&itt, from, to);
return itt;
}
static void
process_free_busy_comp (EMeetingAttendee *ia, icalcomponent *fb_comp, icalcomponent *tz_top_level)
{
icalproperty *ip;
icaltimezone *view_zone;
view_zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ());
ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY);
if (ip != NULL) {
struct icaltimetype dtstart;
icaltimezone *ds_zone = NULL;
dtstart = icalproperty_get_dtstart (ip);
if (!dtstart.is_utc) {
ds_zone = find_zone (ip, tz_top_level);
if (ds_zone != NULL)
dtstart = convert_time (dtstart, ds_zone, view_zone);
}
e_meeting_attendee_set_start_busy_range (ia,
dtstart.year,
dtstart.month,
dtstart.day,
dtstart.hour,
dtstart.minute);
}
ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY);
if (ip != NULL) {
struct icaltimetype dtend;
icaltimezone *de_zone = NULL;
dtend = icalproperty_get_dtend (ip);
if (!dtend.is_utc) {
de_zone = find_zone (ip, tz_top_level);
if (de_zone != NULL)
dtend = convert_time (dtend, de_zone, view_zone);
}
e_meeting_attendee_set_end_busy_range (ia,
dtend.year,
dtend.month,
dtend.day,
dtend.hour,
dtend.minute);
}
ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
while (ip != NULL) {
icalparameter *param;
struct icalperiodtype fb;
EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST;
icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY;
fb = icalproperty_get_freebusy (ip);
param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER);
if (param != NULL)
fbtype = icalparameter_get_fbtype (param);
switch (fbtype) {
case ICAL_FBTYPE_BUSY:
busy_type = E_MEETING_FREE_BUSY_BUSY;
break;
case ICAL_FBTYPE_BUSYUNAVAILABLE:
busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE;
break;
case ICAL_FBTYPE_BUSYTENTATIVE:
busy_type = E_MEETING_FREE_BUSY_TENTATIVE;
break;
default:
}
if (busy_type != E_MEETING_FREE_BUSY_LAST) {
fb.start = convert_time (fb.start, NULL, view_zone);
fb.end = convert_time (fb.end, NULL, view_zone);
e_meeting_attendee_add_busy_period (ia,
fb.start.year,
fb.start.month,
fb.start.day,
fb.start.hour,
fb.start.minute,
fb.end.year,
fb.end.month,
fb.end.day,
fb.end.hour,
fb.end.minute,
busy_type);
}
ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
}
}
static void
process_free_busy (EMeetingModel *im, EMeetingAttendee *ia, char *text)
{
EMeetingModelPrivate *priv;
icalcomponent *main_comp;
icalcomponent_kind kind = ICAL_NO_COMPONENT;
priv = im->priv;
main_comp = icalparser_parse_string (text);
if (main_comp == NULL)
return;
kind = icalcomponent_isa (main_comp);
if (kind == ICAL_VCALENDAR_COMPONENT) {
icalcompiter iter;
icalcomponent *tz_top_level, *sub_comp;
tz_top_level = cal_util_new_top_level ();
iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT);
while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
icalcomponent *clone;
clone = icalcomponent_new_clone (sub_comp);
icalcomponent_add_component (tz_top_level, clone);
icalcompiter_next (&iter);
}
iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT);
while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
process_free_busy_comp (ia, sub_comp, tz_top_level);
icalcompiter_next (&iter);
}
icalcomponent_free (tz_top_level);
} else if (kind == ICAL_VFREEBUSY_COMPONENT) {
process_free_busy_comp (ia, main_comp, NULL);
} else {
return;
}
icalcomponent_free (main_comp);
}
static void
async_close (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer data)
{
EMeetingModelRefreshData *r_data = data;
EMeetingModelPrivate *priv;
process_free_busy (r_data->im, r_data->attendee_data.ia, r_data->attendee_data.string->str);
priv = r_data->im->priv;
priv->refresh_count--;
if (priv->refresh_count == 0) {
GList *l, *m;
for (l = priv->refresh_callbacks, m = priv->refresh_data; l != NULL; l = l->next, m = m->next) {
EMeetingModelRefreshCallback cb = l->data;
cb (m->data);
}
priv->refreshing = FALSE;
}
}
static void
async_read (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer buffer,
GnomeVFSFileSize requested,
GnomeVFSFileSize read,
gpointer data)
{
EMeetingModelRefreshData *r_data = data;
GnomeVFSFileSize buf_size = BUF_SIZE - 1;
if (result != GNOME_VFS_OK) {
gnome_vfs_async_close (handle, async_close, r_data);
return;
}
((char *)buffer)[read] = '\0';
r_data->attendee_data.string = g_string_append (r_data->attendee_data.string, buffer);
if (read < requested) {
gnome_vfs_async_close (handle, async_close, r_data);
return;
}
gnome_vfs_async_read (handle, r_data->attendee_data.buffer, buf_size, async_read, r_data);
}
static void
async_open (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer data)
{
EMeetingModelRefreshData *r_data = data;
GnomeVFSFileSize buf_size = BUF_SIZE - 1;
gnome_vfs_async_read (handle, r_data->attendee_data.buffer, buf_size, async_read, r_data);
}
static void
cursor_cb (EBook *book, EBookStatus status, ECardCursor *cursor, gpointer data)
{
EMeetingModel *im = E_MEETING_MODEL (data);
EMeetingModelPrivate *priv;
int length, i, j;
if (status != E_BOOK_STATUS_SUCCESS)
return;
priv = im->priv;
length = e_card_cursor_get_length (cursor);
priv->refresh_count = 0;
for (i = 0; i < length; i ++) {
GnomeVFSAsyncHandle *handle;
ECard *card = e_card_cursor_get_nth (cursor, i);
EMeetingModelRefreshData *r_data = g_new0 (EMeetingModelRefreshData, 1);
EMeetingAttendee *ia = NULL;
if (card->fburl == NULL)
continue;
for (j = 0; j < priv->attendees->len; j++) {
ia = g_ptr_array_index (priv->attendees, j);
if (e_card_email_match_string (card, itip_strip_mailto (e_meeting_attendee_get_address (ia))))
break;
}
if (ia == NULL)
continue;
r_data->im = im;
r_data->attendee_data.string = g_string_new (NULL);
r_data->attendee_data.ia = ia;
priv->refresh_count++;
/* Read in free/busy data from the url */
gnome_vfs_async_open (&handle, card->fburl, GNOME_VFS_OPEN_READ, async_open, r_data);
}
}
void
e_meeting_model_refresh_busy_periods (EMeetingModel *im, EMeetingModelRefreshCallback call_back, gpointer data)
{
EMeetingModelPrivate *priv;
GPtrArray *not_found;
GString *string;
int i;
priv = im->priv;
priv->refresh_callbacks = g_list_append (priv->refresh_callbacks, call_back);
priv->refresh_data = g_list_append (priv->refresh_data, data);
if (priv->refreshing)
return;
priv->refreshing = TRUE;
/* To track what we don't find on the server */
not_found = g_ptr_array_new ();
g_ptr_array_set_size (not_found, priv->attendees->len);
for (i = 0; i < priv->attendees->len; i++)
g_ptr_array_index (not_found, i) = g_ptr_array_index (priv->attendees, i);
/* Check the server for free busy data */
if (priv->client) {
GList *fb_data, *users = NULL, *l;
time_t start, end, now = time (NULL);
start = now - 60 * 60 * 24;
end = time_add_week (now, 6);
for (i = 0; i < priv->attendees->len; i++) {
EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i);
const char *user;
user = itip_strip_mailto (e_meeting_attendee_get_address (ia));
users = g_list_append (users, g_strdup (user));
}
fb_data = cal_client_get_free_busy (priv->client, users, start, end);
g_list_foreach (users, (GFunc)g_free, NULL);
g_list_free (users);
for (l = fb_data; l != NULL; l = l->next) {
CalComponent *comp = l->data;
EMeetingAttendee *ia = NULL;
CalComponentOrganizer org;
/* Process the data for any attendees found */
cal_component_get_organizer (comp, &org);
for (i = 0; i < priv->attendees->len; i++) {
ia = g_ptr_array_index (priv->attendees, i);
if (org.value && !strcmp (org.value, e_meeting_attendee_get_address (ia))) {
g_ptr_array_remove_fast (not_found, ia);
break;
}
ia = NULL;
}
if (ia != NULL)
process_free_busy (im, ia, cal_component_get_as_string (comp));
}
}
/* Look for fburl's of attendee with no free busy info on server */
if (!priv->book_loaded) {
priv->book_load_wait = TRUE;
gtk_main ();
}
string = g_string_new ("(or ");
for (i = 0; i < not_found->len; i++) {
EMeetingAttendee *ia = g_ptr_array_index (not_found, i);
char *query;
if (!e_meeting_attendee_is_set_address (ia))
continue;
e_meeting_attendee_clear_busy_periods (ia);
query = g_strdup_printf ("(contains \"email\" \"%s\")", itip_strip_mailto (e_meeting_attendee_get_address (ia)));
g_string_append (string, query);
g_free (query);
}
g_string_append_c (string, ')');
e_book_get_cursor (priv->ebook, string->str, cursor_cb, im);
g_ptr_array_free (not_found, FALSE);
g_string_free (string, TRUE);
}
ETableScrolled *
e_meeting_model_etable_from_model (EMeetingModel *im, const gchar *spec_file, const gchar *state_file)
{
g_return_val_if_fail (im != NULL, NULL);
g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL);
return build_etable (E_TABLE_MODEL (im), spec_file, state_file);
}
static void
attendee_changed_cb (EMeetingAttendee *ia, gpointer data)
{
EMeetingModel *im = E_MEETING_MODEL (data);
EMeetingModelPrivate *priv;
gint row = -1, i;
priv = im->priv;
for (i = 0; i < priv->attendees->len; i++) {
if (ia == g_ptr_array_index (priv->attendees, i)) {
row = 1;
break;
}
}
if (row == -1)
return;
e_table_model_row_changed (E_TABLE_MODEL (im), row);
}