/*
* Evolution calendar - Data model for ETable
*
* 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:
* Rodrigo Moya <rodrigo@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "calendar-config.h"
#include "e-cal-model-tasks.h"
#include "e-cell-date-edit-text.h"
#include "misc.h"
#define E_CAL_MODEL_TASKS_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_CAL_MODEL_TASKS, ECalModelTasksPrivate))
struct _ECalModelTasksPrivate {
gboolean highlight_due_today;
gchar *color_due_today;
gboolean highlight_overdue;
gchar *color_overdue;
};
static gint ecmt_column_count (ETableModel *etm);
static gpointer ecmt_value_at (ETableModel *etm, gint col, gint row);
static void ecmt_set_value_at (ETableModel *etm, gint col, gint row, gconstpointer value);
static gboolean ecmt_is_cell_editable (ETableModel *etm, gint col, gint row);
static gpointer ecmt_duplicate_value (ETableModel *etm, gint col, gconstpointer value);
static void ecmt_free_value (ETableModel *etm, gint col, gpointer value);
static gpointer ecmt_initialize_value (ETableModel *etm, gint col);
static gboolean ecmt_value_is_empty (ETableModel *etm, gint col, gconstpointer value);
static gchar *ecmt_value_to_string (ETableModel *etm, gint col, gconstpointer value);
static const gchar *ecmt_get_color_for_component (ECalModel *model, ECalModelComponent *comp_data);
static void ecmt_fill_component_from_model (ECalModel *model, ECalModelComponent *comp_data,
ETableModel *source_model, gint row);
static void commit_component_changes (ECalModelComponent *comp_data);
G_DEFINE_TYPE (ECalModelTasks, e_cal_model_tasks, E_TYPE_CAL_MODEL)
enum {
PROP_0,
PROP_HIGHLIGHT_DUE_TODAY,
PROP_COLOR_DUE_TODAY,
PROP_HIGHLIGHT_OVERDUE,
PROP_COLOR_OVERDUE
};
static void
cal_model_tasks_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_HIGHLIGHT_DUE_TODAY:
e_cal_model_tasks_set_highlight_due_today (
E_CAL_MODEL_TASKS (object),
g_value_get_boolean (value));
return;
case PROP_COLOR_DUE_TODAY:
e_cal_model_tasks_set_color_due_today (
E_CAL_MODEL_TASKS (object),
g_value_get_string (value));
return;
case PROP_HIGHLIGHT_OVERDUE:
e_cal_model_tasks_set_highlight_overdue (
E_CAL_MODEL_TASKS (object),
g_value_get_boolean (value));
return;
case PROP_COLOR_OVERDUE:
e_cal_model_tasks_set_color_overdue (
E_CAL_MODEL_TASKS (object),
g_value_get_string (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
cal_model_tasks_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_HIGHLIGHT_DUE_TODAY:
g_value_set_boolean (
value,
e_cal_model_tasks_get_highlight_due_today (
E_CAL_MODEL_TASKS (object)));
return;
case PROP_COLOR_DUE_TODAY:
g_value_set_string (
value,
e_cal_model_tasks_get_color_due_today (
E_CAL_MODEL_TASKS (object)));
return;
case PROP_HIGHLIGHT_OVERDUE:
g_value_set_boolean (
value,
e_cal_model_tasks_get_highlight_overdue (
E_CAL_MODEL_TASKS (object)));
return;
case PROP_COLOR_OVERDUE:
g_value_set_string (
value,
e_cal_model_tasks_get_color_overdue (
E_CAL_MODEL_TASKS (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
cal_model_tasks_finalize (GObject *object)
{
ECalModelTasksPrivate *priv;
priv = E_CAL_MODEL_TASKS_GET_PRIVATE (object);
g_free (priv->color_due_today);
g_free (priv->color_overdue);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_cal_model_tasks_parent_class)->finalize (object);
}
static void
e_cal_model_tasks_class_init (ECalModelTasksClass *class)
{
GObjectClass *object_class;
ETableModelClass *table_model_class;
ECalModelClass *cal_model_class;
g_type_class_add_private (class, sizeof (ECalModelTasksPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = cal_model_tasks_set_property;
object_class->get_property = cal_model_tasks_get_property;
object_class->finalize = cal_model_tasks_finalize;
table_model_class = E_TABLE_MODEL_CLASS (class);
table_model_class->column_count = ecmt_column_count;
table_model_class->value_at = ecmt_value_at;
table_model_class->set_value_at = ecmt_set_value_at;
table_model_class->is_cell_editable = ecmt_is_cell_editable;
table_model_class->duplicate_value = ecmt_duplicate_value;
table_model_class->free_value = ecmt_free_value;
table_model_class->initialize_value = ecmt_initialize_value;
table_model_class->value_is_empty = ecmt_value_is_empty;
table_model_class->value_to_string = ecmt_value_to_string;
cal_model_class = E_CAL_MODEL_CLASS (class);
cal_model_class->get_color_for_component = ecmt_get_color_for_component;
cal_model_class->fill_component_from_model = ecmt_fill_component_from_model;
g_object_class_install_property (
object_class,
PROP_HIGHLIGHT_DUE_TODAY,
g_param_spec_boolean (
"highlight-due-today",
"Highlight Due Today",
NULL,
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_COLOR_DUE_TODAY,
g_param_spec_string (
"color-due-today",
"Color Due Today",
NULL,
"#1e90ff",
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_HIGHLIGHT_OVERDUE,
g_param_spec_boolean (
"highlight-overdue",
"Highlight Overdue",
NULL,
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_COLOR_OVERDUE,
g_param_spec_string (
"color-overdue",
"Color Overdue",
NULL,
"#ff0000",
G_PARAM_READWRITE));
}
static void
e_cal_model_tasks_init (ECalModelTasks *model)
{
model->priv = E_CAL_MODEL_TASKS_GET_PRIVATE (model);
model->priv->highlight_due_today = TRUE;
model->priv->highlight_overdue = TRUE;
e_cal_model_set_component_kind (
E_CAL_MODEL (model), ICAL_VTODO_COMPONENT);
}
/* ETableModel methods */
static gint
ecmt_column_count (ETableModel *etm)
{
return E_CAL_MODEL_TASKS_FIELD_LAST;
}
/* This makes sure a task is marked as complete.
* It makes sure the "Date Completed" property is set. If the completed_date
* is not -1, then that is used, otherwise if the "Date Completed" property
* is not already set it is set to the current time.
* It makes sure the percent is set to 100, and that the status is "Completed".
* Note that this doesn't update the component on the server. */
static void
ensure_task_complete (ECalModelComponent *comp_data,
time_t completed_date)
{
icalproperty *prop;
gboolean set_completed = TRUE;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
/* Date Completed. */
if (completed_date == -1) {
if (prop)
set_completed = FALSE;
else
completed_date = time (NULL);
}
if (set_completed) {
icaltimezone *utc_zone;
struct icaltimetype new_completed;
/* COMPLETED is stored in UTC. */
utc_zone = icaltimezone_get_utc_timezone ();
new_completed = icaltime_from_timet_with_zone (completed_date,
FALSE,
utc_zone);
if (prop)
icalproperty_set_completed (prop, new_completed);
else {
prop = icalproperty_new_completed (new_completed);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
}
/* Percent. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (!prop)
icalcomponent_add_property (comp_data->icalcomp, icalproperty_new_percentcomplete (100));
else
icalproperty_set_percentcomplete (prop, 100);
/* Status. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop)
icalproperty_set_status (prop, ICAL_STATUS_COMPLETED);
else
icalcomponent_add_property (comp_data->icalcomp, icalproperty_new_status (ICAL_STATUS_COMPLETED));
}
static void
ensure_task_partially_complete (ECalModelComponent *comp_data)
{
icalproperty *prop;
/* Date Completed. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
/* Percent. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (!prop)
icalcomponent_add_property (comp_data->icalcomp, icalproperty_new_percentcomplete (50));
else if (icalproperty_get_percentcomplete (prop) == 0 || icalproperty_get_percentcomplete (prop) == 100)
icalproperty_set_percentcomplete (prop, 50);
/* Status. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop)
icalproperty_set_status (prop, ICAL_STATUS_INPROCESS);
else
icalcomponent_add_property (comp_data->icalcomp, icalproperty_new_status (ICAL_STATUS_INPROCESS));
}
/* This makes sure a task is marked as incomplete. It clears the
* "Date Completed" property. If the percent is set to 100 it removes it,
* and if the status is "Completed" it sets it to "Needs Action".
* Note that this doesn't update the component on the client. */
static void
ensure_task_not_complete (ECalModelComponent *comp_data)
{
icalproperty *prop;
/* Date Completed. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
/* Percent. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
/* Status. */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop)
icalproperty_set_status (prop, ICAL_STATUS_NEEDSACTION);
}
static ECellDateEditValue *
get_completed (ECalModelComponent *comp_data)
{
struct icaltimetype tt_completed;
if (!comp_data->completed) {
icaltimezone *zone;
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (!prop)
return NULL;
tt_completed = icalproperty_get_completed (prop);
if (!icaltime_is_valid_time (tt_completed) || icaltime_is_null_time (tt_completed))
return NULL;
comp_data->completed = g_new0 (ECellDateEditValue, 1);
comp_data->completed->tt = tt_completed;
if (icaltime_get_tzid (tt_completed)
&& e_cal_client_get_timezone_sync (comp_data->client, icaltime_get_tzid (tt_completed), &zone, NULL, NULL))
comp_data->completed->zone = zone;
else
comp_data->completed->zone = NULL;
}
return comp_data->completed;
}
static ECellDateEditValue *
get_due (ECalModelComponent *comp_data)
{
struct icaltimetype tt_due;
if (!comp_data->due) {
icaltimezone *zone;
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DUE_PROPERTY);
if (!prop)
return NULL;
tt_due = icalproperty_get_due (prop);
if (!icaltime_is_valid_time (tt_due) || icaltime_is_null_time (tt_due))
return NULL;
comp_data->due = g_new0 (ECellDateEditValue, 1);
comp_data->due->tt = tt_due;
if (icaltime_get_tzid (tt_due)
&& e_cal_client_get_timezone_sync (comp_data->client, icaltime_get_tzid (tt_due), &zone, NULL, NULL))
comp_data->due->zone = zone;
else
comp_data->due->zone = NULL;
}
return comp_data->due;
}
static gpointer
get_geo (ECalModelComponent *comp_data)
{
icalproperty *prop;
struct icalgeotype geo;
static gchar buf[32];
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_GEO_PROPERTY);
if (prop) {
geo = icalproperty_get_geo (prop);
g_snprintf (buf, sizeof (buf), "%g %s, %g %s",
fabs (geo.lat),
geo.lat >= 0.0 ? "N" : "S",
fabs (geo.lon),
geo.lon >= 0.0 ? "E" : "W");
return buf;
}
return (gpointer) "";
}
static gint
get_percent (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (prop)
return icalproperty_get_percentcomplete (prop);
return 0;
}
static gpointer
get_priority (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PRIORITY_PROPERTY);
if (prop)
return (gpointer) e_cal_util_priority_to_string (icalproperty_get_priority (prop));
return (gpointer) "";
}
static gboolean
is_status_canceled (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
return prop && icalproperty_get_status (prop) == ICAL_STATUS_CANCELLED;
}
static gpointer
get_status (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop) {
switch (icalproperty_get_status (prop)) {
case ICAL_STATUS_NONE:
return (gpointer) "";
case ICAL_STATUS_NEEDSACTION:
return (gpointer) _("Not Started");
case ICAL_STATUS_INPROCESS:
return (gpointer) _("In Progress");
case ICAL_STATUS_COMPLETED:
return (gpointer) _("Completed");
case ICAL_STATUS_CANCELLED:
return (gpointer) _("Canceled");
default:
return (gpointer) "";
}
}
return (gpointer) "";
}
static gpointer
get_url (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_URL_PROPERTY);
if (prop)
return (gpointer) icalproperty_get_url (prop);
return (gpointer) "";
}
static gboolean
is_complete (ECalModelComponent *comp_data)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (prop)
return TRUE;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (prop && icalproperty_get_percentcomplete (prop) == 100)
return TRUE;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop && icalproperty_get_status (prop) == ICAL_STATUS_COMPLETED)
return TRUE;
return FALSE;
}
typedef enum {
E_CAL_MODEL_TASKS_DUE_NEVER,
E_CAL_MODEL_TASKS_DUE_FUTURE,
E_CAL_MODEL_TASKS_DUE_TODAY,
E_CAL_MODEL_TASKS_DUE_OVERDUE,
E_CAL_MODEL_TASKS_DUE_COMPLETE
} ECalModelTasksDueStatus;
static ECalModelTasksDueStatus
get_due_status (ECalModelTasks *model,
ECalModelComponent *comp_data)
{
icalproperty *prop;
/* First, do we have a due date? */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DUE_PROPERTY);
if (!prop)
return E_CAL_MODEL_TASKS_DUE_NEVER;
else {
struct icaltimetype now_tt, due_tt;
icaltimezone *zone;
/* Second, is it already completed? */
if (is_complete (comp_data))
return E_CAL_MODEL_TASKS_DUE_COMPLETE;
/* Third, are we overdue as of right now? */
due_tt = icalproperty_get_due (prop);
if (due_tt.is_date) {
gint cmp;
now_tt = icaltime_current_time_with_zone (e_cal_model_get_timezone (E_CAL_MODEL (model)));
cmp = icaltime_compare_date_only (due_tt, now_tt);
if (cmp < 0)
return E_CAL_MODEL_TASKS_DUE_OVERDUE;
else if (cmp == 0)
return E_CAL_MODEL_TASKS_DUE_TODAY;
else
return E_CAL_MODEL_TASKS_DUE_FUTURE;
} else {
icalparameter *param;
const gchar *tzid;
if (!(param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER)))
return E_CAL_MODEL_TASKS_DUE_FUTURE;
/* Get the current time in the same timezone as the DUE date.*/
tzid = icalparameter_get_tzid (param);
if (!e_cal_client_get_timezone_sync (comp_data->client, tzid, &zone, NULL, NULL))
return E_CAL_MODEL_TASKS_DUE_FUTURE;
now_tt = icaltime_current_time_with_zone (zone);
if (icaltime_compare (due_tt, now_tt) <= 0)
return E_CAL_MODEL_TASKS_DUE_OVERDUE;
else
if (icaltime_compare_date_only (due_tt, now_tt) == 0)
return E_CAL_MODEL_TASKS_DUE_TODAY;
else
return E_CAL_MODEL_TASKS_DUE_FUTURE;
}
}
}
static gboolean
is_overdue (ECalModelTasks *model,
ECalModelComponent *comp_data)
{
switch (get_due_status (model, comp_data)) {
case E_CAL_MODEL_TASKS_DUE_NEVER:
case E_CAL_MODEL_TASKS_DUE_FUTURE:
case E_CAL_MODEL_TASKS_DUE_COMPLETE:
return FALSE;
case E_CAL_MODEL_TASKS_DUE_TODAY:
case E_CAL_MODEL_TASKS_DUE_OVERDUE:
return TRUE;
}
return FALSE;
}
static gpointer
ecmt_value_at (ETableModel *etm,
gint col,
gint row)
{
ECalModelComponent *comp_data;
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), NULL);
g_return_val_if_fail (col >= 0 && (col < E_CAL_MODEL_TASKS_FIELD_LAST || col == E_CAL_MODEL_TASKS_FIELD_STRIKEOUT), NULL);
g_return_val_if_fail (row >= 0 && row < e_table_model_row_count (etm), NULL);
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->value_at (etm, col, row);
comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row);
if (!comp_data)
return (gpointer) "";
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
return get_completed (comp_data);
case E_CAL_MODEL_TASKS_FIELD_STRIKEOUT :
return GINT_TO_POINTER (is_status_canceled (comp_data) || is_complete (comp_data));
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
return GINT_TO_POINTER (is_complete (comp_data));
case E_CAL_MODEL_TASKS_FIELD_DUE :
return get_due (comp_data);
case E_CAL_MODEL_TASKS_FIELD_GEO :
return get_geo (comp_data);
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
return GINT_TO_POINTER (is_overdue (model, comp_data));
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
return GINT_TO_POINTER (get_percent (comp_data));
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
return get_priority (comp_data);
case E_CAL_MODEL_TASKS_FIELD_STATUS :
return get_status (comp_data);
case E_CAL_MODEL_TASKS_FIELD_URL :
return get_url (comp_data);
}
return (gpointer) "";
}
static void
set_completed (ECalModelTasks *model,
ECalModelComponent *comp_data,
gconstpointer value)
{
ECellDateEditValue *dv = (ECellDateEditValue *) value;
if (!dv)
ensure_task_not_complete (comp_data);
else {
time_t t;
if (dv->tt.is_date) {
/* if it's a date, it will be floating,
* but completed needs a date time value */
dv->tt.is_date = FALSE;
t = icaltime_as_timet_with_zone (dv->tt, e_cal_model_get_timezone (E_CAL_MODEL (model)));
} else {
/* we assume that COMPLETED is entered in the current timezone,
* even though it gets stored in UTC */
t = icaltime_as_timet_with_zone (dv->tt, dv->zone);
}
ensure_task_complete (comp_data, t);
}
}
static void
set_complete (ECalModelComponent *comp_data,
gconstpointer value)
{
gint state = GPOINTER_TO_INT (value);
if (state)
ensure_task_complete (comp_data, -1);
else
ensure_task_not_complete (comp_data);
}
static void
set_due (ECalModel *model,
ECalModelComponent *comp_data,
gconstpointer value)
{
e_cal_model_update_comp_time (model, comp_data, value, ICAL_DUE_PROPERTY, icalproperty_set_due, icalproperty_new_due);
}
/* FIXME: We need to set the "transient_for" property for the dialog, but the
* model doesn't know anything about the windows.
*/
static void
show_geo_warning (void)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"%s", _("The geographical position must be entered "
"in the format: \n\n45.436845,125.862501"));
gtk_widget_show (dialog);
}
static void
set_geo (ECalModelComponent *comp_data,
const gchar *value)
{
gdouble latitude, longitude;
gint matched;
struct icalgeotype geo;
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_GEO_PROPERTY);
if (string_is_empty (value)) {
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
} else {
matched = sscanf (value, "%lg , %lg", &latitude, &longitude);
if (matched != 2)
show_geo_warning ();
geo.lat = latitude;
geo.lon = longitude;
if (prop)
icalproperty_set_geo (prop, geo);
else {
prop = icalproperty_new_geo (geo);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
}
}
static void
set_status (ECalModelComponent *comp_data,
const gchar *value)
{
icalproperty_status status;
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
/* an empty string is the same as 'None' */
if (!value[0])
return;
/* Translators: "None" for task's status */
if (!e_util_utf8_strcasecmp (value, C_("cal-task-status", "None")))
return;
else if (!e_util_utf8_strcasecmp (value, _("Not Started")))
status = ICAL_STATUS_NEEDSACTION;
else if (!e_util_utf8_strcasecmp (value, _("In Progress")))
status = ICAL_STATUS_INPROCESS;
else if (!e_util_utf8_strcasecmp (value, _("Completed")))
status = ICAL_STATUS_COMPLETED;
else if (!e_util_utf8_strcasecmp (value, _("Canceled")))
status = ICAL_STATUS_CANCELLED;
else {
g_warning ("Invalid status: %s\n", value);
return;
}
if (prop)
icalproperty_set_status (prop, status);
else {
prop = icalproperty_new_status (status);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
switch (status) {
case ICAL_STATUS_NEEDSACTION:
ensure_task_not_complete (comp_data);
break;
case ICAL_STATUS_INPROCESS:
ensure_task_partially_complete (comp_data);
break;
case ICAL_STATUS_CANCELLED:
ensure_task_not_complete (comp_data);
/* do this again, because the previous function changed status to NEEDSACTION */
icalproperty_set_status (prop, status);
break;
case ICAL_STATUS_COMPLETED:
ensure_task_complete (comp_data, -1);
break;
default:
break;
}
}
static void
set_percent (ECalModelComponent *comp_data,
gconstpointer value)
{
icalproperty *prop;
gint percent = GPOINTER_TO_INT (value);
g_return_if_fail (percent >= -1);
g_return_if_fail (percent <= 100);
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
/* A value of -1 means it isn't set */
if (percent == -1) {
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
ensure_task_not_complete (comp_data);
} else {
if (prop)
icalproperty_set_percentcomplete (prop, percent);
else {
prop = icalproperty_new_percentcomplete (percent);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
if (percent == 100)
ensure_task_complete (comp_data, -1);
else {
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
if (percent > 0)
set_status (comp_data, _("In Progress"));
}
}
}
static void
set_priority (ECalModelComponent *comp_data,
const gchar *value)
{
icalproperty *prop;
gint priority;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PRIORITY_PROPERTY);
priority = e_cal_util_priority_from_string (value);
if (priority == -1) {
g_warning ("Invalid priority");
priority = 0;
}
if (prop)
icalproperty_set_priority (prop, priority);
else {
prop = icalproperty_new_priority (priority);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
}
static void
set_url (ECalModelComponent *comp_data,
const gchar *value)
{
icalproperty *prop;
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_URL_PROPERTY);
if (string_is_empty (value)) {
if (prop) {
icalcomponent_remove_property (comp_data->icalcomp, prop);
icalproperty_free (prop);
}
} else {
if (prop)
icalproperty_set_url (prop, value);
else {
prop = icalproperty_new_url (value);
icalcomponent_add_property (comp_data->icalcomp, prop);
}
}
}
static void
ecmt_set_value_at (ETableModel *etm,
gint col,
gint row,
gconstpointer value)
{
ECalModelComponent *comp_data;
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
g_return_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST);
g_return_if_fail (row >= 0 && row < e_table_model_row_count (etm));
if (col < E_CAL_MODEL_FIELD_LAST) {
E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->set_value_at (etm, col, row, value);
return;
}
comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row);
if (!comp_data)
return;
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
set_completed (model, comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
set_complete (comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_DUE :
set_due ((ECalModel *) model, comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_GEO :
set_geo (comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
set_percent (comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
set_priority (comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_STATUS :
set_status (comp_data, value);
break;
case E_CAL_MODEL_TASKS_FIELD_URL :
set_url (comp_data, value);
break;
}
commit_component_changes (comp_data);
}
static gboolean
ecmt_is_cell_editable (ETableModel *etm,
gint col,
gint row)
{
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), FALSE);
g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST, FALSE);
g_return_val_if_fail (row >= -1 || (row >= 0 && row < e_table_model_row_count (etm)), FALSE);
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->is_cell_editable (etm, col, row);
if (!e_cal_model_test_row_editable (E_CAL_MODEL (etm), row))
return FALSE;
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_DUE :
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
return TRUE;
}
return FALSE;
}
static gpointer
ecmt_duplicate_value (ETableModel *etm,
gint col,
gconstpointer value)
{
g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST, NULL);
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->duplicate_value (etm, col, value);
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
return g_strdup (value);
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_DUE :
if (value) {
ECellDateEditValue *dv, *orig_dv;
orig_dv = (ECellDateEditValue *) value;
dv = g_new0 (ECellDateEditValue, 1);
*dv = *orig_dv;
return dv;
}
break;
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
return (gpointer) value;
}
return NULL;
}
static void
ecmt_free_value (ETableModel *etm,
gint col,
gpointer value)
{
g_return_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST);
if (col < E_CAL_MODEL_FIELD_LAST) {
E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->free_value (etm, col, value);
return;
}
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_DUE :
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
if (value)
g_free (value);
break;
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
break;
}
}
static gpointer
ecmt_initialize_value (ETableModel *etm,
gint col)
{
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), NULL);
g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST, NULL);
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->initialize_value (etm, col);
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
return g_strdup ("");
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_DUE :
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
return NULL;
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
return GINT_TO_POINTER (-1);
}
return NULL;
}
static gboolean
ecmt_value_is_empty (ETableModel *etm,
gint col,
gconstpointer value)
{
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), TRUE);
g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST, TRUE);
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->value_is_empty (etm, col, value);
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
return string_is_empty (value);
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_DUE :
return value ? FALSE : TRUE;
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
return (GPOINTER_TO_INT (value) < 0) ? TRUE : FALSE;
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
return TRUE;
}
return TRUE;
}
static gchar *
ecmt_value_to_string (ETableModel *etm,
gint col,
gconstpointer value)
{
ECalModelTasks *model = (ECalModelTasks *) etm;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), g_strdup (""));
g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_TASKS_FIELD_LAST, g_strdup (""));
if (col < E_CAL_MODEL_FIELD_LAST)
return E_TABLE_MODEL_CLASS (e_cal_model_tasks_parent_class)->value_to_string (etm, col, value);
switch (col) {
case E_CAL_MODEL_TASKS_FIELD_GEO :
case E_CAL_MODEL_TASKS_FIELD_PRIORITY :
case E_CAL_MODEL_TASKS_FIELD_STATUS :
case E_CAL_MODEL_TASKS_FIELD_URL :
return g_strdup (value);
case E_CAL_MODEL_TASKS_FIELD_COMPLETED :
case E_CAL_MODEL_TASKS_FIELD_DUE :
return e_cal_model_date_value_to_string (E_CAL_MODEL (model), value);
case E_CAL_MODEL_TASKS_FIELD_COMPLETE :
case E_CAL_MODEL_TASKS_FIELD_OVERDUE :
return g_strdup (value ? _("Yes") : _("No"));
case E_CAL_MODEL_TASKS_FIELD_PERCENT :
if (GPOINTER_TO_INT (value) < 0)
return g_strdup ("N/A");
else
return g_strdup_printf ("%i%%", GPOINTER_TO_INT (value));
}
return g_strdup ("");
}
/* ECalModel class methods */
static const gchar *
ecmt_get_color_for_component (ECalModel *model,
ECalModelComponent *comp_data)
{
ECalModelTasks *tasks;
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), NULL);
g_return_val_if_fail (comp_data != NULL, NULL);
tasks = E_CAL_MODEL_TASKS (model);
/* XXX ECalModel's get_color_for_component() method should really
* get a GdkColor instead of a color specification string. */
switch (get_due_status (tasks, comp_data)) {
case E_CAL_MODEL_TASKS_DUE_TODAY:
if (!e_cal_model_tasks_get_highlight_due_today (tasks))
break;
return e_cal_model_tasks_get_color_due_today (tasks);
case E_CAL_MODEL_TASKS_DUE_OVERDUE:
if (!e_cal_model_tasks_get_highlight_overdue (tasks))
break;
return e_cal_model_tasks_get_color_overdue (tasks);
case E_CAL_MODEL_TASKS_DUE_NEVER:
case E_CAL_MODEL_TASKS_DUE_FUTURE:
case E_CAL_MODEL_TASKS_DUE_COMPLETE:
break;
}
return E_CAL_MODEL_CLASS (e_cal_model_tasks_parent_class)->
get_color_for_component (model, comp_data);
}
static void
ecmt_fill_component_from_model (ECalModel *model,
ECalModelComponent *comp_data,
ETableModel *source_model,
gint row)
{
gpointer value;
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
g_return_if_fail (comp_data != NULL);
g_return_if_fail (E_IS_TABLE_MODEL (source_model));
/* This just makes sure if anything indicates completion, all
* three fields do or if percent is 0, status is sane */
value = e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_COMPLETED, row);
set_completed ((ECalModelTasks *) model, comp_data, value);
if (!value) {
value = e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_PERCENT, row);
set_percent (comp_data, value);
if (GPOINTER_TO_INT (value) != 100 && GPOINTER_TO_INT (value) != 0)
set_status (comp_data, e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_STATUS, row));
}
set_due (model, comp_data,
e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_DUE, row));
set_geo (comp_data,
e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_GEO, row));
set_priority (comp_data,
e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_PRIORITY, row));
set_url (comp_data,
e_table_model_value_at (source_model, E_CAL_MODEL_TASKS_FIELD_URL, row));
}
ECalModel *
e_cal_model_tasks_new (ESourceRegistry *registry)
{
g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
return g_object_new (
E_TYPE_CAL_MODEL_TASKS,
"registry", registry, NULL);
}
gboolean
e_cal_model_tasks_get_highlight_due_today (ECalModelTasks *model)
{
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), FALSE);
return model->priv->highlight_due_today;
}
void
e_cal_model_tasks_set_highlight_due_today (ECalModelTasks *model,
gboolean highlight)
{
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
if ((highlight ? 1 : 0) == (model->priv->highlight_due_today ? 1 : 0))
return;
model->priv->highlight_due_today = highlight;
g_object_notify (G_OBJECT (model), "highlight-due-today");
}
const gchar *
e_cal_model_tasks_get_color_due_today (ECalModelTasks *model)
{
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), NULL);
return model->priv->color_due_today;
}
void
e_cal_model_tasks_set_color_due_today (ECalModelTasks *model,
const gchar *color_due_today)
{
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
g_return_if_fail (color_due_today != NULL);
g_free (model->priv->color_due_today);
model->priv->color_due_today = g_strdup (color_due_today);
g_object_notify (G_OBJECT (model), "color-due-today");
}
gboolean
e_cal_model_tasks_get_highlight_overdue (ECalModelTasks *model)
{
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), FALSE);
return model->priv->highlight_overdue;
}
void
e_cal_model_tasks_set_highlight_overdue (ECalModelTasks *model,
gboolean highlight)
{
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
if ((highlight ? 1 : 0) == (model->priv->highlight_overdue ? 1 : 0))
return;
model->priv->highlight_overdue = highlight;
g_object_notify (G_OBJECT (model), "highlight-overdue");
}
const gchar *
e_cal_model_tasks_get_color_overdue (ECalModelTasks *model)
{
g_return_val_if_fail (E_IS_CAL_MODEL_TASKS (model), NULL);
return model->priv->color_overdue;
}
void
e_cal_model_tasks_set_color_overdue (ECalModelTasks *model,
const gchar *color_overdue)
{
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
g_return_if_fail (color_overdue != NULL);
g_free (model->priv->color_overdue);
model->priv->color_overdue = g_strdup (color_overdue);
g_object_notify (G_OBJECT (model), "color-overdue");
}
/**
* e_cal_model_tasks_mark_comp_complete
* @model: Currently not used...
* @comp_data: Component of our interest
*
* Marks component as complete and commits changes to the calendar backend.
**/
void e_cal_model_tasks_mark_comp_complete (ECalModelTasks *model, ECalModelComponent *comp_data)
{
g_return_if_fail (model != NULL);
g_return_if_fail (comp_data != NULL);
/* we will receive changes when committed, so don't do this */
/*e_table_model_pre_change (E_TABLE_MODEL (model));*/
ensure_task_complete (comp_data, -1);
/*e_table_model_row_changed (E_TABLE_MODEL (model), model_row);*/
commit_component_changes (comp_data);
}
/**
* e_cal_model_tasks_mark_comp_incomplete
* @model: Currently not used...
* @comp_data: Component of our interest
*
* Marks component as incomplete and commits changes to the calendar backend.
**/
void e_cal_model_tasks_mark_comp_incomplete (ECalModelTasks *model, ECalModelComponent *comp_data)
{
icalproperty *prop,*prop1;
g_return_if_fail (model != NULL);
g_return_if_fail (comp_data != NULL);
/* we will receive changes when committed, so don't do this */
/*e_table_model_pre_change (E_TABLE_MODEL (model));*/
/* Status */
prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_STATUS_PROPERTY);
if (prop)
icalproperty_set_status (prop, ICAL_STATUS_NEEDSACTION);
else
icalcomponent_add_property (comp_data->icalcomp, icalproperty_new_status (ICAL_STATUS_NEEDSACTION));
/*complete property*/
prop1 = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COMPLETED_PROPERTY);
if (prop1) {
icalcomponent_remove_property (comp_data->icalcomp, prop1);
icalproperty_free (prop1);
}
/* Percent. */
prop1 = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY);
if (prop1) {
icalcomponent_remove_property (comp_data->icalcomp, prop1);
icalproperty_free (prop1);
}
/*e_table_model_row_changed (E_TABLE_MODEL (model), model_row);*/
commit_component_changes (comp_data);
}
/**
* commit_component_changes
* @comp_data: Component of our interest, which has been changed.
*
* Commits changes to the backend calendar of the component.
**/
static void
commit_component_changes (ECalModelComponent *comp_data)
{
GError *error = NULL;
g_return_if_fail (comp_data != NULL);
/* FIXME ask about mod type */
if (!e_cal_client_modify_object_sync (comp_data->client, comp_data->icalcomp, CALOBJ_MOD_ALL, NULL, &error)) {
g_warning (G_STRLOC ": Could not modify the object! %s", error ? error->message : "Unknown error");
/* FIXME Show error dialog */
if (error)
g_error_free (error);
}
}
/**
* e_cal_model_tasks_update_due_tasks
*/
void
e_cal_model_tasks_update_due_tasks (ECalModelTasks *model)
{
gint row, row_count;
ECalModelComponent *comp_data;
ECalModelTasksDueStatus status;
g_return_if_fail (E_IS_CAL_MODEL_TASKS (model));
row_count = e_table_model_row_count (E_TABLE_MODEL (model));
for (row = 0; row < row_count; row++)
{
comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row);
status = get_due_status (E_CAL_MODEL_TASKS (model), comp_data);
if ((status == E_CAL_MODEL_TASKS_DUE_TODAY) || (status == E_CAL_MODEL_TASKS_DUE_OVERDUE))
{
e_table_model_pre_change (E_TABLE_MODEL (model));
e_table_model_row_changed (E_TABLE_MODEL (model), row);
}
}
}