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"
30 #include "milk-cache.h"
33 milk_task_model_tree_model_init (GtkTreeModelIface *iface);
35 milk_task_model_tree_sortable_init (GtkTreeSortableIface *iface);
37 G_DEFINE_TYPE_WITH_CODE (MilkTaskModel,
40 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
41 milk_task_model_tree_model_init)
42 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
43 milk_task_model_tree_sortable_init));
45 /* less expensive than G_TYPE_INSTANCE_GET_PRIVATE */
46 #define MILK_TASK_MODEL_PRIVATE(o) ((MILK_TASK_MODEL ((o)))->priv)
48 struct _MilkTaskModelPrivate
60 static GtkTreeModelFlags
61 milk_task_model_get_flags (GtkTreeModel *model)
63 return GTK_TREE_MODEL_LIST_ONLY;
67 milk_task_model_get_n_columns (GtkTreeModel *model)
69 return MILK_TASK_MODEL_N_COLUMNS;
73 milk_task_model_get_column_type (GtkTreeModel *model,
77 case MILK_TASK_MODEL_COLUMN_TASK:
81 g_warning (G_STRLOC ": invalid column: %d", column);
82 return G_TYPE_INVALID;
87 milk_task_model_get_iter (GtkTreeModel *model,
91 MilkTaskModelPrivate *priv;
93 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
94 g_return_val_if_fail (iter, FALSE);
95 g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE);
97 priv = MILK_TASK_MODEL_PRIVATE (model);
99 return gtk_tree_model_get_iter (
100 GTK_TREE_MODEL (priv->store), iter, path);
104 milk_task_model_get_path (GtkTreeModel *model,
107 MilkTaskModelPrivate *priv;
109 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), NULL);
110 g_return_val_if_fail (iter, NULL);
112 priv = MILK_TASK_MODEL_PRIVATE (model);
114 return gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), iter);
118 milk_task_model_get_value (GtkTreeModel *model,
123 MilkTaskModelPrivate *priv;
125 g_return_if_fail (MILK_IS_TASK_MODEL (model));
126 g_return_if_fail (iter);
127 g_return_if_fail (value);
129 priv = MILK_TASK_MODEL_PRIVATE (model);
131 gtk_tree_model_get_value (
132 GTK_TREE_MODEL (priv->store), iter, column, value);
136 milk_task_model_iter_next (GtkTreeModel *model,
139 MilkTaskModelPrivate *priv;
141 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
142 g_return_val_if_fail (iter, FALSE);
144 priv = MILK_TASK_MODEL_PRIVATE (model);
146 return gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), iter);
150 milk_task_model_iter_nth_child (GtkTreeModel *model,
155 MilkTaskModelPrivate *priv;
157 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
158 g_return_val_if_fail (iter, FALSE);
159 /* we're one-dimensional */
160 g_return_val_if_fail (!parent, FALSE);
161 g_return_val_if_fail (index >= 0, FALSE);
163 priv = MILK_TASK_MODEL_PRIVATE (model);
165 return gtk_tree_model_iter_nth_child (
166 GTK_TREE_MODEL (priv->store), iter, parent, index);
170 milk_task_model_iter_children (GtkTreeModel *model,
174 MilkTaskModelPrivate *priv;
176 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
177 g_return_val_if_fail (iter, FALSE);
178 /* we're one-dimensional */
179 g_return_val_if_fail (!parent, FALSE);
181 priv = MILK_TASK_MODEL_PRIVATE (model);
183 return gtk_tree_model_iter_children (
184 GTK_TREE_MODEL (priv->store), iter, parent);
188 milk_task_model_iter_has_child (GtkTreeModel *model,
191 MilkTaskModelPrivate *priv;
193 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
194 g_return_val_if_fail (iter, FALSE);
196 priv = MILK_TASK_MODEL_PRIVATE (model);
198 return gtk_tree_model_iter_has_child (
199 GTK_TREE_MODEL (priv->store), iter);
203 milk_task_model_iter_n_children (GtkTreeModel *model,
206 MilkTaskModelPrivate *priv;
208 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), -1);
209 g_return_val_if_fail (iter, -1);
211 priv = MILK_TASK_MODEL_PRIVATE (model);
213 /* we're one-dimensional */
217 return gtk_tree_model_iter_n_children (
218 GTK_TREE_MODEL (priv->store), iter);
222 milk_task_model_iter_parent (GtkTreeModel *model,
226 MilkTaskModelPrivate *priv;
228 g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
229 g_return_val_if_fail (iter, FALSE);
230 g_return_val_if_fail (child, FALSE);
232 priv = MILK_TASK_MODEL_PRIVATE (model);
234 return gtk_tree_model_iter_parent (
235 GTK_TREE_MODEL (priv->store), iter, child);
239 milk_task_model_get_sort_column_id (GtkTreeSortable *sortable,
243 MilkTaskModel *model;
244 MilkTaskModelPrivate *priv;
246 g_return_if_fail (MILK_IS_TASK_MODEL (sortable));
248 model = MILK_TASK_MODEL (model);
249 priv = MILK_TASK_MODEL_PRIVATE (model);
251 return gtk_tree_sortable_get_sort_column_id (
252 GTK_TREE_SORTABLE (priv->store), column_id, order);
256 milk_task_model_set_sort_column_id (GtkTreeSortable *sortable,
260 MilkTaskModel *model;
261 MilkTaskModelPrivate *priv;
263 g_return_if_fail (MILK_IS_TASK_MODEL (sortable));
265 model = MILK_TASK_MODEL (model);
266 priv = MILK_TASK_MODEL_PRIVATE (model);
268 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store),
273 milk_task_model_set_default_sort_func (GtkTreeSortable *sortable,
274 GtkTreeIterCompareFunc callback,
276 GDestroyNotify destroy_data)
278 MilkTaskModel *model;
279 MilkTaskModelPrivate *priv;
281 g_return_if_fail (MILK_IS_TASK_MODEL (sortable));
283 model = MILK_TASK_MODEL (model);
284 priv = MILK_TASK_MODEL_PRIVATE (model);
286 gtk_tree_sortable_set_default_sort_func (
287 GTK_TREE_SORTABLE (priv->store), callback, user_data,
291 typedef gchar* (*RtmTaskAttrFunc) (RtmTask*);
294 model_store_find_task_by_attr (MilkTaskModel *model,
296 RtmTaskAttrFunc attr_func,
297 GtkTreeIter *iter_in)
299 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
302 gboolean found = FALSE;
304 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store),
306 while (valid && !found) {
309 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
310 MILK_TASK_MODEL_COLUMN_TASK, &task,
313 if (!g_strcmp0 (attr_func (task_in),
319 g_object_unref (task);
321 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store),
329 model_store_find_task (MilkTaskModel *model,
331 GtkTreeIter *iter_in)
333 return model_store_find_task_by_attr (model, task_in, rtm_task_get_id,
338 model_store_find_local_only_task (MilkTaskModel *model,
340 GtkTreeIter *iter_in)
342 return model_store_find_task_by_attr (model, task_in,rtm_task_get_name,
347 row_changed_cb (GtkTreeModel *model,
352 gtk_tree_model_row_changed (GTK_TREE_MODEL (self), path, iter);
356 row_deleted_cb (GtkTreeModel *model,
360 gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
364 row_inserted_cb (GtkTreeModel *model,
369 gtk_tree_model_row_inserted (GTK_TREE_MODEL (self), path, iter);
373 rows_reordered_cb (GtkTreeModel *model,
379 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (self), path, NULL,
384 model_priority_sort_func (GtkTreeModel *model,
390 RtmTask *task_a, *task_b;
391 const char *priority_str_a, *priority_str_b;
392 gint64 priority_a, priority_b;
393 const char *name_a, *name_b;
395 gtk_tree_model_get (model, a,
396 MILK_TASK_MODEL_COLUMN_TASK, &task_a,
399 gtk_tree_model_get (model, b,
400 MILK_TASK_MODEL_COLUMN_TASK, &task_b,
403 priority_str_a = rtm_task_get_priority (task_a);
404 priority_str_b = rtm_task_get_priority (task_b);
406 /* these will be assigned 0 if they're not the well-known priorites
408 priority_a = g_ascii_strtoll (priority_str_a, NULL, 10);
409 priority_b = g_ascii_strtoll (priority_str_b, NULL, 10);
411 priority_a = (priority_a <= 0) ? G_MAXINT : priority_a;
412 priority_b = (priority_b <= 0) ? G_MAXINT : priority_b;
414 retval = (priority_a - priority_b);
418 /* secondary sort, by name */
419 name_a = rtm_task_get_name (task_a);
420 name_b = rtm_task_get_name (task_b);
422 return g_utf8_collate (name_a, name_b);
426 cache_cleared_cb (MilkCache *cache,
427 MilkTaskModel *model)
429 MilkTaskModelPrivate *priv;
430 priv = MILK_TASK_MODEL_PRIVATE (model);
432 gtk_list_store_clear (priv->store);
436 cache_task_added_cb (MilkCache *cache,
438 MilkTaskModel *model)
440 MilkTaskModelPrivate *priv;
443 gboolean task_in_store;
445 priv = MILK_TASK_MODEL_PRIVATE (model);
447 /* local-only tasks don't have a set task ID */
448 id = rtm_task_get_id (task);
450 /* clear out any entries for the task created before we knew its
452 g_hash_table_remove (priv->tasks, rtm_task_get_name (task));
454 id = rtm_task_get_name (task);
456 g_return_if_fail (id);
458 g_hash_table_insert (priv->tasks, g_strdup (id),
459 g_object_ref (task));
461 task_in_store = model_store_find_task (model, task, &iter);
463 gtk_list_store_append (priv->store, &iter);
464 gtk_list_store_set (priv->store, &iter,
465 MILK_TASK_MODEL_COLUMN_TASK, task, -1);
469 cache_task_changed_cb (MilkCache *cache,
471 MilkTaskModel *model)
473 MilkTaskModelPrivate *priv;
476 gboolean task_in_store;
480 priv = MILK_TASK_MODEL_PRIVATE (model);
482 id = rtm_task_get_id (task);
483 g_hash_table_insert (priv->tasks, g_strdup (id),
484 g_object_ref (task));
486 /* try to find a local-only version of this task first, to upgrade it to
488 task_in_store = model_store_find_local_only_task (model, task, &iter);
489 if (!task_in_store) {
490 /* FIXME: cut this */
491 g_debug ("task (supposedly) was already known remotely");
493 task_in_store = model_store_find_task (model, task, &iter);
495 /* FIXME: cut this */
496 g_debug ("task was *NOT* known remotely");
499 /* rtm-glib doesn't re-use task structs when they're updated, so we have
500 * to replace the changed */
501 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
502 MILK_TASK_MODEL_COLUMN_TASK, &old_task, -1);
504 gtk_list_store_set (priv->store, &iter,
505 MILK_TASK_MODEL_COLUMN_TASK, task, -1);
507 path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
508 gtk_tree_model_row_changed (GTK_TREE_MODEL (priv->store), path, &iter);
509 gtk_tree_path_free (path);
511 g_object_unref (old_task);
515 cache_task_finished_cb (MilkCache *cache,
517 MilkTaskModel *model)
519 MilkTaskModelPrivate *priv;
522 gboolean task_in_store;
524 priv = MILK_TASK_MODEL_PRIVATE (model);
526 id = rtm_task_get_id (task);
527 g_hash_table_insert (priv->tasks, g_strdup (id),
528 g_object_ref (task));
530 task_in_store = model_store_find_task (model, task, &iter);
533 gtk_list_store_remove (priv->store, &iter);
537 set_cache (MilkTaskModel *model,
540 MilkTaskModelPrivate *priv;
543 g_return_if_fail (MILK_IS_TASK_MODEL (model));
544 g_return_if_fail (MILK_IS_CACHE (cache));
546 priv = MILK_TASK_MODEL_PRIVATE (model);
549 g_object_unref (priv->cache);
551 priv->cache = g_object_ref (cache);
553 gtk_list_store_clear (priv->store);
555 g_signal_connect (cache, "cleared", G_CALLBACK (cache_cleared_cb),
557 g_signal_connect (cache, "task-added", G_CALLBACK (cache_task_added_cb),
559 g_signal_connect (cache, "task-changed",
560 G_CALLBACK (cache_task_changed_cb), model);
561 g_signal_connect (cache, "task-finished",
562 G_CALLBACK (cache_task_finished_cb), model);
564 /* do the initial fill from the cache */
565 tasks = milk_cache_get_active_tasks (cache);
566 for (l = tasks; l; l = l->next) {
567 cache_task_added_cb (cache, l->data, model);
573 milk_task_model_get_property (GObject *object,
578 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
583 g_value_set_object (value, priv->cache);
587 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
593 milk_task_model_set_property (GObject *object,
601 set_cache (MILK_TASK_MODEL (object),
602 g_value_get_object (value));
606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
612 milk_task_model_dispose (GObject *object)
614 MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
616 g_signal_handlers_disconnect_by_func (priv->cache, cache_cleared_cb,
618 g_signal_handlers_disconnect_by_func (priv->cache, cache_task_added_cb,
620 g_signal_handlers_disconnect_by_func (priv->cache,
621 cache_task_changed_cb, object);
622 g_signal_handlers_disconnect_by_func (priv->cache,
623 cache_task_finished_cb, object);
626 g_object_unref (priv->cache);
630 g_signal_handlers_disconnect_by_func (priv->store, row_changed_cb,
632 g_signal_handlers_disconnect_by_func (priv->store, row_deleted_cb,
634 g_signal_handlers_disconnect_by_func (priv->store, row_inserted_cb,
636 g_signal_handlers_disconnect_by_func (priv->store, rows_reordered_cb,
640 g_object_unref (priv->store);
645 g_hash_table_destroy (priv->tasks);
649 G_OBJECT_CLASS (milk_task_model_parent_class)->dispose (object);
653 milk_task_model_class_init (MilkTaskModelClass *klass)
655 GObjectClass *object_class = G_OBJECT_CLASS (klass);
657 g_type_class_add_private (klass, sizeof (MilkTaskModelPrivate));
659 object_class->get_property = milk_task_model_get_property;
660 object_class->set_property = milk_task_model_set_property;
661 object_class->dispose = milk_task_model_dispose;
663 g_object_class_install_property
669 "Remember The Milk tasks cache.",
671 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
672 G_PARAM_STATIC_STRINGS));
676 milk_task_model_init (MilkTaskModel *self)
678 MilkTaskModelPrivate *priv;
680 self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (
681 self, MILK_TYPE_TASK_MODEL, MilkTaskModelPrivate);
683 priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal,
684 g_free, g_object_unref);
686 priv->store = gtk_list_store_new (
687 MILK_TASK_MODEL_N_COLUMNS, RTM_TYPE_TASK);
689 gtk_tree_sortable_set_default_sort_func (
690 GTK_TREE_SORTABLE (priv->store),
691 model_priority_sort_func, g_object_ref (self),
694 gtk_tree_sortable_set_sort_column_id (
695 GTK_TREE_SORTABLE (priv->store),
696 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
699 g_signal_connect (priv->store, "row-changed",
700 G_CALLBACK (row_changed_cb), self);
702 g_signal_connect (priv->store, "row-deleted",
703 G_CALLBACK (row_deleted_cb), self);
705 g_signal_connect (priv->store, "row-inserted",
706 G_CALLBACK (row_inserted_cb), self);
708 g_signal_connect (priv->store, "rows-reordered",
709 G_CALLBACK (rows_reordered_cb), self);
713 milk_task_model_tree_model_init (GtkTreeModelIface *iface)
715 iface->get_flags = milk_task_model_get_flags;
716 iface->get_n_columns = milk_task_model_get_n_columns;
717 iface->get_column_type = milk_task_model_get_column_type;
718 iface->get_iter = milk_task_model_get_iter;
719 iface->get_path = milk_task_model_get_path;
720 iface->get_value = milk_task_model_get_value;
721 iface->iter_next = milk_task_model_iter_next;
722 iface->iter_children = milk_task_model_iter_children;
723 iface->iter_has_child = milk_task_model_iter_has_child;
724 iface->iter_n_children = milk_task_model_iter_n_children;
725 iface->iter_nth_child = milk_task_model_iter_nth_child;
726 iface->iter_parent = milk_task_model_iter_parent;
730 milk_task_model_tree_sortable_init (GtkTreeSortableIface *iface)
732 iface->get_sort_column_id = milk_task_model_get_sort_column_id;
733 iface->set_sort_column_id = milk_task_model_set_sort_column_id;
734 iface->set_default_sort_func = milk_task_model_set_default_sort_func;
738 milk_task_model_new ()
740 return g_object_new (MILK_TYPE_TASK_MODEL,
741 "cache", milk_cache_get_default (), NULL);