2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public
13 * License along with this program; if not, write to the
14 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
15 * Boston, MA 02110-1301 USA
17 * Authors: Travis Reitter <treitter@gmail.com>
23 #include <glib/gi18n.h>
25 #include <hildon/hildon.h>
26 #include <rtm-glib/rtm-glib.h>
28 #include "milk-task-model.h"
29 #include "milk-auth.h"
32 milk_task_model_tree_model_init (GtkTreeModelIface *iface);
34 G_DEFINE_TYPE_EXTENDED (MilkTaskModel,
38 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
39 milk_task_model_tree_model_init));
41 /* less expensive than G_TYPE_INSTANCE_GET_PRIVATE */
42 #define MILK_TASK_MODEL_PRIVATE(o) ((MILK_TASK_MODEL ((o)))->priv)
44 /* FIXME: make this configurable at runtime, pref. as a gconf value */
45 /* time between syncing with the server, in ms */
46 #define MODEL_UPDATE_PERIOD 60000
48 struct _MilkTaskModelPrivate
62 task_is_finished (RtmTask *task)
64 return (rtm_task_get_completed_date (task) ||
65 rtm_task_get_deleted_date (task));
68 static GtkTreeModelFlags
69 milk_task_model_get_flags (GtkTreeModel *model)
71 return GTK_TREE_MODEL_LIST_ONLY;
75 milk_task_model_get_n_columns (GtkTreeModel *model)
77 return MILK_TASK_MODEL_N_COLUMNS;
81 milk_task_model_get_column_type (GtkTreeModel *model,
85 case MILK_TASK_MODEL_COLUMN_TASK:
89 g_warning (G_STRLOC ": invalid column: %d", column);
90 return G_TYPE_INVALID;
95 milk_task_model_get_iter (GtkTreeModel *model,
99 MilkTaskModelPrivate *priv;
101 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
102 g_return_val_if_fail (iter, FALSE);
103 g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE);
105 priv = MILK_TASK_MODEL_PRIVATE (model);
107 return gtk_tree_model_get_iter (
108 GTK_TREE_MODEL (priv->store), iter, path);
112 milk_task_model_get_path (GtkTreeModel *model,
115 MilkTaskModelPrivate *priv;
117 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), NULL);
118 g_return_val_if_fail (iter, NULL);
120 priv = MILK_TASK_MODEL_PRIVATE (model);
122 return gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), iter);
126 milk_task_model_get_value (GtkTreeModel *model,
131 MilkTaskModelPrivate *priv;
133 g_return_if_fail (MILK_IS_TASK_MODEL (model));
134 g_return_if_fail (iter);
135 g_return_if_fail (value);
137 priv = MILK_TASK_MODEL_PRIVATE (model);
139 gtk_tree_model_get_value (
140 GTK_TREE_MODEL (priv->store), iter, column, value);
144 milk_task_model_iter_next (GtkTreeModel *model,
147 MilkTaskModelPrivate *priv;
149 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
150 g_return_val_if_fail (iter, FALSE);
152 priv = MILK_TASK_MODEL_PRIVATE (model);
154 return gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), iter);
158 milk_task_model_iter_nth_child (GtkTreeModel *model,
163 MilkTaskModelPrivate *priv;
165 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
166 g_return_val_if_fail (iter, FALSE);
167 /* we're one-dimensional */
168 g_return_val_if_fail (!parent, FALSE);
169 g_return_val_if_fail (index >= 0, FALSE);
171 priv = MILK_TASK_MODEL_PRIVATE (model);
173 return gtk_tree_model_iter_nth_child (
174 GTK_TREE_MODEL (priv->store), iter, parent, index);
178 milk_task_model_iter_children (GtkTreeModel *model,
182 MilkTaskModelPrivate *priv;
184 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
185 g_return_val_if_fail (iter, FALSE);
186 /* we're one-dimensional */
187 g_return_val_if_fail (!parent, FALSE);
189 priv = MILK_TASK_MODEL_PRIVATE (model);
191 return gtk_tree_model_iter_children (
192 GTK_TREE_MODEL (priv->store), iter, parent);
196 milk_task_model_iter_has_child (GtkTreeModel *model,
199 MilkTaskModelPrivate *priv;
201 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
202 g_return_val_if_fail (iter, FALSE);
204 priv = MILK_TASK_MODEL_PRIVATE (model);
206 return gtk_tree_model_iter_has_child (
207 GTK_TREE_MODEL (priv->store), iter);
211 milk_task_model_iter_n_children (GtkTreeModel *model,
214 MilkTaskModelPrivate *priv;
216 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), -1);
217 g_return_val_if_fail (iter, -1);
219 priv = MILK_TASK_MODEL_PRIVATE (model);
221 /* we're one-dimensional */
225 return gtk_tree_model_iter_n_children (
226 GTK_TREE_MODEL (priv->store), iter);
230 milk_task_model_iter_parent (GtkTreeModel *model,
234 MilkTaskModelPrivate *priv;
236 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
237 g_return_val_if_fail (iter, FALSE);
238 g_return_val_if_fail (child, FALSE);
240 priv = MILK_TASK_MODEL_PRIVATE (model);
242 return gtk_tree_model_iter_parent (
243 GTK_TREE_MODEL (priv->store), iter, child);
247 model_store_find_task (MilkTaskModel *model,
249 GtkTreeIter *iter_in)
251 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
254 gboolean found = FALSE;
256 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store),
258 while (valid && !found) {
261 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
262 MILK_TASK_MODEL_COLUMN_TASK, &task,
265 if (!g_strcmp0 (rtm_task_get_id (task_in),
266 rtm_task_get_id (task))) {
271 g_object_unref (task);
273 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store),
281 update_model (MilkTaskModel *model)
283 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
286 GTimeVal current_time;
288 GError *error = NULL;
290 if (milk_auth_get_state (priv->auth) != MILK_AUTH_STATE_CONNECTED) {
294 g_get_current_time (¤t_time);
295 new_sync = g_time_val_to_iso8601 (¤t_time);
296 rtm_tasks = milk_auth_get_tasks (priv->auth, priv->last_sync, &error);
299 g_error (G_STRLOC ": failed to retrieve latest tasks: %s",
301 g_clear_error (&error);
303 g_free (priv->last_sync);
304 priv->last_sync = new_sync;
308 for (l = rtm_tasks; l; l = g_list_delete_link (l, l)) {
312 gboolean task_in_store;
314 rtm_task = RTM_TASK (l->data);
316 id = rtm_task_get_id (rtm_task);
317 g_hash_table_insert (priv->tasks, g_strdup (id),
318 g_object_ref (rtm_task));
320 task_in_store = model_store_find_task (model, rtm_task, &iter);
322 /* Task is deleted or completed */
323 if (task_is_finished (rtm_task)) {
325 gtk_list_store_remove (priv->store, &iter);
327 /* Task has been changed */
328 } else if (task_in_store) {
332 /* rtm-glib doesn't re-use task structs when they're
333 * updated, so we have to replace the changed */
335 GTK_TREE_MODEL (priv->store), &iter,
336 MILK_TASK_MODEL_COLUMN_TASK, &old_task,
341 MILK_TASK_MODEL_COLUMN_TASK, rtm_task,
344 path = gtk_tree_model_get_path (
345 GTK_TREE_MODEL (priv->store), &iter);
346 gtk_tree_model_row_changed (
347 GTK_TREE_MODEL (priv->store),
349 gtk_tree_path_free (path);
351 g_object_unref (old_task);
355 gtk_list_store_append (priv->store, &iter);
358 MILK_TASK_MODEL_COLUMN_TASK, rtm_task,
367 auth_notify_cb (MilkAuth *auth,
369 MilkTaskModel *model)
371 if (milk_auth_get_state (auth) == MILK_AUTH_STATE_CONNECTED) {
372 update_model (model);
377 row_changed_cb (GtkTreeModel *model,
382 gtk_tree_model_row_changed (GTK_TREE_MODEL (self), path, iter);
386 row_deleted_cb (GtkTreeModel *model,
390 gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
394 row_inserted_cb (GtkTreeModel *model,
399 gtk_tree_model_row_inserted (GTK_TREE_MODEL (self), path, iter);
403 rows_reordered_cb (GtkTreeModel *model,
409 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (self), path, NULL,
414 milk_task_model_set_auth (MilkTaskModel *model,
417 MilkTaskModelPrivate *priv;
419 g_return_if_fail (model);
420 g_return_if_fail (MILK_IS_TASK_MODEL (model));
421 g_return_if_fail (auth);
422 g_return_if_fail (MILK_IS_AUTH (auth));
424 priv = MILK_TASK_MODEL_PRIVATE (model);
427 g_object_unref (priv->auth);
429 priv->auth = g_object_ref (auth);
431 if (priv->update_id) {
432 g_source_remove (priv->update_id);
434 priv->update_id = g_timeout_add (MODEL_UPDATE_PERIOD,
435 (GSourceFunc) update_model, model);
437 gtk_list_store_clear (priv->store);
439 g_signal_connect (priv->auth, "notify::state",
440 G_CALLBACK (auth_notify_cb), model);
441 auth_notify_cb (priv->auth, NULL, model);
445 milk_task_model_get_property (GObject *object,
450 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
455 g_value_set_object (value, priv->auth);
459 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
465 milk_task_model_set_property (GObject *object,
473 milk_task_model_set_auth (MILK_TASK_MODEL (object),
474 g_value_get_object (value));
478 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
484 milk_task_model_dispose (GObject *object)
486 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
489 g_object_unref (priv->auth);
493 g_signal_handlers_disconnect_by_func (priv->store, row_changed_cb,
495 g_signal_handlers_disconnect_by_func (priv->store, row_deleted_cb,
497 g_signal_handlers_disconnect_by_func (priv->store, row_inserted_cb,
499 g_signal_handlers_disconnect_by_func (priv->store, rows_reordered_cb,
503 g_object_unref (priv->store);
507 if (priv->update_id) {
508 g_source_remove (priv->update_id);
513 g_hash_table_destroy (priv->tasks);
517 G_OBJECT_CLASS (milk_task_model_parent_class)->dispose (object);
521 milk_task_model_finalize (GObject *object)
523 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
525 g_free (priv->last_sync);
527 G_OBJECT_CLASS (milk_task_model_parent_class)->finalize (object);
531 milk_task_model_class_init (MilkTaskModelClass *klass)
533 GObjectClass *object_class = G_OBJECT_CLASS (klass);
535 g_type_class_add_private (klass, sizeof (MilkTaskModelPrivate));
537 object_class->get_property = milk_task_model_get_property;
538 object_class->set_property = milk_task_model_set_property;
539 object_class->dispose = milk_task_model_dispose;
540 object_class->finalize = milk_task_model_finalize;
542 g_object_class_install_property
547 "Authentication proxy",
548 "Remember The Milk authentication proxy.",
550 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
551 G_PARAM_STATIC_STRINGS));
555 milk_task_model_init (MilkTaskModel *self)
557 MilkTaskModelPrivate *priv;
559 self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (
560 self, MILK_TYPE_TASK_MODEL, MilkTaskModelPrivate);
562 priv->last_sync = NULL;
564 priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal,
565 g_free, g_object_unref);
567 priv->store = gtk_list_store_new (
568 MILK_TASK_MODEL_N_COLUMNS, RTM_TYPE_TASK);
570 g_signal_connect (priv->store, "row-changed",
571 G_CALLBACK (row_changed_cb), self);
573 g_signal_connect (priv->store, "row-deleted",
574 G_CALLBACK (row_deleted_cb), self);
576 g_signal_connect (priv->store, "row-inserted",
577 G_CALLBACK (row_inserted_cb), self);
579 g_signal_connect (priv->store, "rows-reordered",
580 G_CALLBACK (rows_reordered_cb), self);
584 milk_task_model_tree_model_init (GtkTreeModelIface *iface)
586 iface->get_flags = milk_task_model_get_flags;
587 iface->get_n_columns = milk_task_model_get_n_columns;
588 iface->get_column_type = milk_task_model_get_column_type;
589 iface->get_iter = milk_task_model_get_iter;
590 iface->get_path = milk_task_model_get_path;
591 iface->get_value = milk_task_model_get_value;
592 iface->iter_next = milk_task_model_iter_next;
593 iface->iter_children = milk_task_model_iter_children;
594 iface->iter_has_child = milk_task_model_iter_has_child;
595 iface->iter_n_children = milk_task_model_iter_n_children;
596 iface->iter_nth_child = milk_task_model_iter_nth_child;
597 iface->iter_parent = milk_task_model_iter_parent;
601 milk_task_model_new (MilkAuth *auth)
603 return g_object_new (MILK_TYPE_TASK_MODEL, "auth", auth, NULL);