/* less expensive than G_TYPE_INSTANCE_GET_PRIVATE */
#define MILK_TASK_MODEL_PRIVATE(o) ((MILK_TASK_MODEL ((o)))->priv)
+/* FIXME: make this configurable at runtime, pref. as a gconf value */
+/* time between syncing with the server, in ms */
+#define MODEL_UPDATE_PERIOD 60000
+
struct _MilkTaskModelPrivate
{
+ GHashTable *tasks;
GtkListStore *store;
MilkAuth *auth;
+ guint update_id;
+ char *last_sync;
};
enum {
PROP_AUTH = 1,
};
+static gboolean
+task_is_finished (RtmTask *task)
+{
+ return (rtm_task_get_completed_date (task) ||
+ rtm_task_get_deleted_date (task));
+}
+
static GtkTreeModelFlags
milk_task_model_get_flags (GtkTreeModel *model)
{
GTK_TREE_MODEL (priv->store), iter, child);
}
-static void
-populate_model (MilkTaskModel *model)
+static gboolean
+model_store_find_task (MilkTaskModel *model,
+ RtmTask *task_in,
+ GtkTreeIter *iter_in)
+{
+ MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
+ gboolean valid;
+ GtkTreeIter iter;
+ gboolean found = FALSE;
+
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store),
+ &iter);
+ while (valid && !found) {
+ MilkTask *task;
+ char *task_id;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+ MILK_TASK_MODEL_COLUMN_TASK, &task,
+ -1);
+ g_object_get (task, "id", &task_id, NULL);
+
+ if (!g_strcmp0 (rtm_task_get_id (task_in), task_id)) {
+ *iter_in = iter;
+ found = TRUE;
+ }
+
+ g_object_unref (task);
+
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store),
+ &iter);
+ }
+
+ return found;
+}
+
+static gboolean
+update_model (MilkTaskModel *model)
{
MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
GList *rtm_tasks;
GList *l;
- GtkTreeIter iter;
+ GTimeVal current_time;
+ char *new_sync;
+ GError *error = NULL;
- gtk_list_store_clear (priv->store);
+ if (milk_auth_get_state (priv->auth) != MILK_AUTH_STATE_CONNECTED) {
+ return TRUE;
+ }
+
+ g_get_current_time (¤t_time);
+ new_sync = g_time_val_to_iso8601 (¤t_time);
+ rtm_tasks = milk_auth_get_tasks (priv->auth, priv->last_sync, &error);
- /* FIXME: poll for new tasks periodically -- there's rtm-glib API to
- * optimize just fetching the latest ones */
- rtm_tasks = milk_auth_get_tasks (priv->auth);
+ if (error) {
+ g_error (G_STRLOC ": failed to retrieve latest tasks: %s",
+ error->message);
+ g_clear_error (&error);
+ } else {
+ g_free (priv->last_sync);
+ priv->last_sync = new_sync;
+ }
/* Populate model */
for (l = rtm_tasks; l; l = g_list_delete_link (l, l)) {
+ GtkTreeIter iter;
RtmTask *rtm_task;
MilkTask *task;
+ const char *id;
+ gboolean task_in_store;
rtm_task = RTM_TASK (l->data);
- /* XXX: if possible, avoid fetching these in the first place */
- /* Skip tasks deleted or completed. */
- if (rtm_task_get_completed_date (rtm_task) ||
- rtm_task_get_deleted_date (rtm_task)) {
- continue;
+ id = rtm_task_get_id (rtm_task);
+ g_hash_table_insert (priv->tasks, g_strdup (id),
+ g_object_ref (rtm_task));
+
+ task_in_store = model_store_find_task (model, rtm_task, &iter);
+
+ /* Task is deleted or completed */
+ if (task_is_finished (rtm_task)) {
+ if (task_in_store) {
+ gtk_list_store_remove (priv->store, &iter);
+ }
+ /* Task has been changed */
+ } else if (task_in_store) {
+ GtkTreePath *path;
+
+ gtk_tree_model_get (
+ GTK_TREE_MODEL (priv->store), &iter,
+ MILK_TASK_MODEL_COLUMN_TASK, &task,
+ -1);
+
+ milk_task_set_title (task,
+ rtm_task_get_name (rtm_task));
+
+ path = gtk_tree_model_get_path (
+ GTK_TREE_MODEL (priv->store), &iter);
+ gtk_tree_model_row_changed (
+ GTK_TREE_MODEL (priv->store),
+ path, &iter);
+ gtk_tree_path_free (path);
+
+ g_object_unref (task);
+
+ /* Task is new */
+ } else {
+ /* FIXME: remove the MilkTask class; just use RtmTask
+ * directly */
+ task = milk_task_new (id, rtm_task_get_name (rtm_task),
+ /* FIXME: switch priority from int to
+ * string */
+ g_ascii_strtod (rtm_task_get_priority
+ (rtm_task), NULL));
+
+ gtk_list_store_append (priv->store, &iter);
+ gtk_list_store_set (
+ priv->store, &iter,
+ MILK_TASK_MODEL_COLUMN_TASK, task,
+ -1);
}
-
- task = milk_task_new (rtm_task_get_id (rtm_task),
- rtm_task_get_name (rtm_task),
- /* FIXME: switch priority from int to string */
- g_ascii_strtod (rtm_task_get_priority
- (rtm_task), NULL));
-
- gtk_list_store_append (priv->store, &iter);
- gtk_list_store_set (
- priv->store, &iter,
- MILK_TASK_MODEL_COLUMN_TASK, task,
- -1);
}
+
+ return TRUE;
}
static void
MilkTaskModel *model)
{
if (milk_auth_get_state (auth) == MILK_AUTH_STATE_CONNECTED) {
- populate_model (model);
+ update_model (model);
}
}
}
priv->auth = g_object_ref (auth);
- if (milk_auth_get_state (priv->auth) == MILK_AUTH_STATE_CONNECTED) {
- populate_model (model);
- } else {
- g_signal_connect (priv->auth, "notify::state",
- G_CALLBACK (auth_notify_cb), model);
+ if (priv->update_id) {
+ g_source_remove (priv->update_id);
}
+ priv->update_id = g_timeout_add (MODEL_UPDATE_PERIOD,
+ (GSourceFunc) update_model, model);
+
+ gtk_list_store_clear (priv->store);
+
+ g_signal_connect (priv->auth, "notify::state",
+ G_CALLBACK (auth_notify_cb), model);
+ auth_notify_cb (priv->auth, NULL, model);
}
static void
priv->store = NULL;
}
+ if (priv->update_id) {
+ g_source_remove (priv->update_id);
+ priv->update_id = 0;
+ }
+
+ if (priv->tasks) {
+ g_hash_table_destroy (priv->tasks);
+ priv->tasks = NULL;
+ }
+
G_OBJECT_CLASS (milk_task_model_parent_class)->dispose (object);
}
static void
+milk_task_model_finalize (GObject *object)
+{
+ MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
+
+ g_free (priv->last_sync);
+
+ G_OBJECT_CLASS (milk_task_model_parent_class)->finalize (object);
+}
+
+static void
milk_task_model_class_init (MilkTaskModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = milk_task_model_get_property;
object_class->set_property = milk_task_model_set_property;
object_class->dispose = milk_task_model_dispose;
+ object_class->finalize = milk_task_model_finalize;
g_object_class_install_property
(object_class,
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (
self, MILK_TYPE_TASK_MODEL, MilkTaskModelPrivate);
+ priv->last_sync = NULL;
+
+ priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
priv->store = gtk_list_store_new (
MILK_TASK_MODEL_N_COLUMNS, MILK_TYPE_TASK);