Logo Search packages:      
Sourcecode: tasks version File versions  Download package

koto-task-store.c

/*
 * Copyright (C) 2007 OpenedHand Ltd
 *
 * 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., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <config.h>
#include <string.h>
#include <libecal/e-cal.h>
#include "koto-task.h"
#include "koto-task-store.h"
#include "koto-actions.h"
#include "ical-util.h"

G_DEFINE_TYPE (KotoTaskStore, koto_task_store, GTK_TYPE_LIST_STORE);

#define GET_PRIVATE(o) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), KOTO_TYPE_TASK_STORE, KotoTaskStorePrivate))

typedef struct _KotoTaskStorePrivate KotoTaskStorePrivate;

struct _KotoTaskStorePrivate
{
  ECal *cal;
  ECalView *view;
  guint sig_added, sig_modified, sig_removed;
  GHashTable *uid_hash;
};

enum {
  PROP_0,
  PROP_CALVIEW,
};

/*
 * Private methods
 */

static gint
sorter_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
  int res;
  gboolean done_a, done_b;
  int weight_a, weight_b;
  char *summary_a, *summary_b;

  gtk_tree_model_get (model, a,
                      COLUMN_DONE, &done_a,
                      COLUMN_WEIGHT, &weight_a,
                      COLUMN_SUMMARY, &summary_a,
                      -1);
  gtk_tree_model_get (model, b,
                      COLUMN_DONE, &done_b,
                      COLUMN_WEIGHT, &weight_b,
                      COLUMN_SUMMARY, &summary_b,
                      -1);

  if (done_a != done_b) {
    res = done_a < done_b ? -1 : 1;
    goto done;
  }
  
  if (weight_a != weight_b) {
    res = weight_a < weight_b ? -1 : 1;
    goto done;
  }
  
  res = g_utf8_collate (summary_a ?: "", summary_b ?: "");
  
 done:
  g_free (summary_a);
  g_free (summary_b);

  return res;
}

static int
get_weight (int priority, struct icaltimetype due) {

  struct icaltimetype today;

  if (priority == PRIORITY_NONE)
    priority = PRIORITY_MEDIUM;

  if (icaltime_is_null_time (due)) {
    return priority;
  }

  today = icaltime_today ();

  /* If we're due in the past */
  if (icaltime_compare_date_only (due, today) < 0)
    return priority - 10;

  /* If it's due today */
  if (icaltime_compare_date_only(due, today) == 0)
    return priority - 5;

  /* If it's due in the next three days */
  icaltime_adjust(&today, 3, 0, 0, 0);
  if (icaltime_compare_date_only(due, today) <= 0)
    return priority - 2;

  /* If its due later than a fortnight away */
  icaltime_adjust(&today, -3 + 14, 0, 0, 0);
  if (icaltime_compare_date_only(due, today) > 0)
    return priority + 2;

  return priority;
}

/*
 * Utility method called by objects_added and objects_modified that inserts a
 * new row (@insert is #TRUE) or updates a row (@insert is #FALSE) in @store
 * pointed to by @iter with data extracted from @ical.
 */
static void
update_row (KotoTaskStore *store, icalcomponent *ical, gboolean insert, GtkTreeIter *iter)
{
  icalproperty_status status;
  icaltimetype due_time;
  GDate *due_date = NULL;
  KotoTask *task;

  g_assert (KOTO_IS_TASK_STORE (store));
  g_assert (ical);
  g_assert (iter);
  
  status = icalcomponent_get_status (ical);
  due_time = icalcomponent_get_due (ical);
  if (!icaltime_is_null_time (due_time)) {
    due_date = g_date_new ();
    g_date_set_time_t (due_date,
                       icaltime_as_timet_with_zone (due_time,
                                                    icaltimezone_get_utc_timezone ()));
  }
  
  task = koto_task_new (icalcomponent_new_clone (ical));

  if (insert) {
    gtk_list_store_insert_with_values (GTK_LIST_STORE (store), iter, 0,
                                       COLUMN_TASK, task,
                                       COLUMN_DONE, status == ICAL_STATUS_COMPLETED,
                                       COLUMN_PRIORITY, ical_util_get_priority (ical),
                                       COLUMN_WEIGHT, get_weight (ical_util_get_priority (ical), due_time),
                                       COLUMN_DUE, due_date,
                                       COLUMN_SUMMARY, icalcomponent_get_summary (ical),
                                       COLUMN_URL, ical_util_get_url (ical),
                                       -1);
  } else {
    gtk_list_store_set (GTK_LIST_STORE (store), iter,
                        COLUMN_TASK, task,
                        COLUMN_DONE, status == ICAL_STATUS_COMPLETED,
                        COLUMN_PRIORITY, ical_util_get_priority (ical),
                        COLUMN_WEIGHT, get_weight (ical_util_get_priority (ical), due_time),
                        COLUMN_DUE, due_date,
                        COLUMN_SUMMARY, icalcomponent_get_summary (ical),
                        COLUMN_URL, ical_util_get_url (ical),
                        -1);
  }
  
  if (due_date)
    g_date_free (due_date);
  koto_task_unref (task);
}

/*
 * Callback when objects in the calendar view are added.
 */
static void
on_objects_added (ECalView *view, GList *objects, KotoTaskStore *store)
{
  KotoTaskStorePrivate *priv = GET_PRIVATE (store);

  for (;objects; objects = g_list_next (objects)) {
    icalcomponent *ical;
    GtkTreeIter iter;

    ical = objects->data;

    update_row (store, ical, TRUE, &iter);

    g_hash_table_insert (priv->uid_hash,
                         g_strdup (icalcomponent_get_uid (ical)),
                         gtk_tree_iter_copy (&iter));
    
  }
}

/*
 * Callback when objects in the calendar view are modified.
 */
static void
on_objects_modified (ECalView *view, GList *objects, KotoTaskStore *store)
{
  KotoTaskStorePrivate *priv = GET_PRIVATE (store);

  for (;objects; objects = g_list_next (objects)) {
    icalcomponent *ical;
    GtkTreeIter *iter;

    ical = objects->data;

    iter = g_hash_table_lookup (priv->uid_hash, icalcomponent_get_uid (ical));

    update_row (store, ical, FALSE, iter);
  }
}

/*
 * Callback when objects in the calendar view are removed.
 */
static void
on_objects_removed (ECalView *view, GList *uids, KotoTaskStore *store) {
  KotoTaskStorePrivate *priv = GET_PRIVATE (store);

  for (; uids; uids = g_list_next (uids)) {
    GtkTreeIter *iter;
    const char *uid;
#if HAVE_ECALCOMPONENTID
    ECalComponentId *id = uids->data;
    /* TODO: check uid/rid to handle recurrant tasks? */
    uid = id->uid;
#else
    uid = uids->data;
#endif

    iter = g_hash_table_lookup (priv->uid_hash, uid);
    if (iter) {
      gtk_list_store_remove (GTK_LIST_STORE (store), iter);
      g_hash_table_remove (priv->uid_hash, uid);
    } else {
      g_warning ("Cannot find iter for removed UID %s", uid);
    }
  }
}

/*
 * Object methods
 */

static void
koto_task_store_get_property (GObject *object, guint property_id,
                              GValue *value, GParamSpec *pspec)
{
  KotoTaskStorePrivate *priv = GET_PRIVATE (object);

  switch (property_id) {
  case PROP_CALVIEW:
    g_value_set_object (value, priv->view);
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static void
koto_task_store_set_property (GObject *object, guint property_id,
                              const GValue *value, GParamSpec *pspec)
{
  switch (property_id) {
  case PROP_CALVIEW:
    koto_task_store_set_view (KOTO_TASK_STORE (object),
                               g_value_get_object (value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static void
koto_task_store_dispose (GObject *object)
{
  koto_task_store_set_view (KOTO_TASK_STORE (object), NULL);

  if (G_OBJECT_CLASS (koto_task_store_parent_class)->dispose)
    G_OBJECT_CLASS (koto_task_store_parent_class)->dispose (object);
}

static void
koto_task_store_finalize (GObject *object)
{
  KotoTaskStorePrivate *priv = GET_PRIVATE (object);

  g_hash_table_destroy (priv->uid_hash);
  
  G_OBJECT_CLASS (koto_task_store_parent_class)->finalize (object);
}

static void
koto_task_store_class_init (KotoTaskStoreClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  g_type_class_add_private (klass, sizeof (KotoTaskStorePrivate));

  object_class->get_property = koto_task_store_get_property;
  object_class->set_property = koto_task_store_set_property;
  object_class->dispose = koto_task_store_dispose;
  object_class->finalize = koto_task_store_finalize;

  g_object_class_install_property (object_class, PROP_CALVIEW,
                                   g_param_spec_object ("calview", "calview", NULL,
                                                        E_TYPE_CAL_VIEW,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK));
}

static void
koto_task_store_init (KotoTaskStore *self)
{
  KotoTaskStorePrivate *priv = GET_PRIVATE (self);

  const GType column_types[] = {
    KOTO_TYPE_TASK, /* icalcomponent */
    G_TYPE_BOOLEAN, /* done */
    G_TYPE_INT, /* weight */
    G_TYPE_INT, /* priority */
    G_TYPE_DATE, /* due */
    G_TYPE_STRING, /* summary */
    G_TYPE_STRING, /* URL */
  };

  gtk_list_store_set_column_types (GTK_LIST_STORE (self),
                                   G_N_ELEMENTS (column_types),
                                   (GType *) column_types);

  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (self),
                                           sorter_cb, NULL, NULL);
  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
                                        GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
                                        GTK_SORT_ASCENDING);

  priv->uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                          g_free, (GDestroyNotify)gtk_tree_iter_free);
}


/*
 * Public methods
 */

GtkTreeModel*
koto_task_store_new (ECalView *view)
{
  return g_object_new (KOTO_TYPE_TASK_STORE,
                       "calview", view,
                       NULL);
}

void
koto_task_store_set_view (KotoTaskStore *store, ECalView *view)
{
  KotoTaskStorePrivate *priv;

  g_return_if_fail (KOTO_IS_TASK_STORE (store));

  priv = GET_PRIVATE (store);

  if (priv->view) {
    g_signal_handler_disconnect (priv->view, priv->sig_added);
    g_signal_handler_disconnect (priv->view, priv->sig_modified);
    g_signal_handler_disconnect (priv->view, priv->sig_removed);
    priv->sig_added = priv->sig_modified = priv->sig_removed = 0;
    
    g_object_unref (priv->view);
    g_object_unref (priv->cal);
    priv->view = NULL;
    priv->cal = NULL;
  }
  
  if (view) {
    priv->view = g_object_ref (view);
    priv->cal = g_object_get_data (G_OBJECT (view), "koto-ecal");
    if (priv->cal)
      g_object_ref (priv->cal);
    priv->sig_added = g_signal_connect (priv->view, "objects-added", G_CALLBACK (on_objects_added), store);
    priv->sig_modified = g_signal_connect (priv->view, "objects-modified", G_CALLBACK (on_objects_modified), store);
    priv->sig_removed = g_signal_connect (priv->view, "objects-removed", G_CALLBACK (on_objects_removed), store);
  }
}

void
koto_task_store_set_done (KotoTaskStore *store, GtkTreeIter *iter, gboolean done, KotoUndoContext *undo)
{
  KotoTaskStorePrivate *priv;
  KotoTask *task;
  icalcomponent *old_comp;

  g_return_if_fail (KOTO_IS_TASK_STORE (store));
  g_return_if_fail (iter);

  priv = GET_PRIVATE (store);
  
  gtk_tree_model_get (GTK_TREE_MODEL (store), iter, COLUMN_TASK, &task, -1);
  
  old_comp = icalcomponent_new_clone (task->comp);
  
  icalcomponent_set_status (task->comp, done ? ICAL_STATUS_COMPLETED : ICAL_STATUS_NONE);
  
  gtk_list_store_set (GTK_LIST_STORE (store), iter, COLUMN_DONE, done, -1);

  koto_action_modify_task (priv->cal, task, old_comp, undo);

  koto_task_unref (task);
}

gboolean
koto_task_store_get_iter_for_uid (KotoTaskStore *store, const char *uid, GtkTreeIter *iter)
{
  KotoTaskStorePrivate *priv;
  GtkTreeIter *it;

  g_return_val_if_fail (KOTO_IS_TASK_STORE (store), FALSE);
  g_return_val_if_fail (uid, FALSE);
  g_return_val_if_fail (iter, FALSE);

  priv = GET_PRIVATE (store);

  if (g_hash_table_lookup_extended (priv->uid_hash, uid, NULL, (gpointer*)(void*)&it)) {
    *iter = *it;
    return TRUE;
  } else {
    return FALSE;
  }
}

Generated by  Doxygen 1.6.0   Back to index