Update the task model with tasks from the server.
[milk] / src / milk-task-model.c
1 /*
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.
6  *
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.
11  *
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
16  *
17  * Authors: Travis Reitter <treitter@gmail.com>
18  */
19
20 #include <config.h>
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtk.h>
25 #include <hildon/hildon.h>
26 #include <rtm-glib/rtm-glib.h>
27
28 #include "milk-task-model.h"
29 #include "milk-auth.h"
30 #include "milk-task.h"
31
32 static void
33 milk_task_model_tree_model_init (GtkTreeModelIface *iface);
34
35 G_DEFINE_TYPE_EXTENDED (MilkTaskModel,
36                         milk_task_model,
37                         G_TYPE_OBJECT,
38                         0,
39                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
40                                                milk_task_model_tree_model_init));
41
42 /* less expensive than G_TYPE_INSTANCE_GET_PRIVATE */
43 #define MILK_TASK_MODEL_PRIVATE(o) ((MILK_TASK_MODEL ((o)))->priv)
44
45 struct _MilkTaskModelPrivate
46 {
47         GtkListStore *store;
48         MilkAuth *auth;
49 };
50
51 enum {
52         PROP_AUTH = 1,
53 };
54
55 static GtkTreeModelFlags
56 milk_task_model_get_flags (GtkTreeModel *model)
57 {
58         return GTK_TREE_MODEL_LIST_ONLY;
59 }
60
61 static gint
62 milk_task_model_get_n_columns (GtkTreeModel *model)
63 {
64         return MILK_TASK_MODEL_N_COLUMNS;
65 }
66
67 static GType
68 milk_task_model_get_column_type (GtkTreeModel *model,
69                                  gint          column)
70 {
71         switch (column) {
72                 case MILK_TASK_MODEL_COLUMN_TASK:
73                         return MILK_TYPE_TASK;
74
75                 default:
76                         g_warning (G_STRLOC ": invalid column: %d", column);
77                         return G_TYPE_INVALID;
78         }
79 }
80
81 static gboolean
82 milk_task_model_get_iter (GtkTreeModel *model,
83                           GtkTreeIter  *iter,
84                           GtkTreePath  *path)
85 {
86         MilkTaskModelPrivate *priv;
87
88         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
89         g_return_val_if_fail (iter, FALSE);
90         g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE);
91
92         priv = MILK_TASK_MODEL_PRIVATE (model);
93
94         return gtk_tree_model_get_iter (
95                         GTK_TREE_MODEL (priv->store), iter, path);
96 }
97
98 static GtkTreePath*
99 milk_task_model_get_path (GtkTreeModel *model,
100                           GtkTreeIter  *iter)
101 {
102         MilkTaskModelPrivate *priv;
103
104         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), NULL);
105         g_return_val_if_fail (iter, NULL);
106
107         priv = MILK_TASK_MODEL_PRIVATE (model);
108
109         return gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), iter);
110 }
111
112 static void
113 milk_task_model_get_value (GtkTreeModel *model,
114                            GtkTreeIter  *iter,
115                            gint          column,
116                            GValue       *value)
117 {
118         MilkTaskModelPrivate *priv;
119
120         g_return_if_fail (MILK_IS_TASK_MODEL (model));
121         g_return_if_fail (iter);
122         g_return_if_fail (value);
123
124         priv = MILK_TASK_MODEL_PRIVATE (model);
125
126         gtk_tree_model_get_value (
127                         GTK_TREE_MODEL (priv->store), iter, column, value);
128 }
129
130 static gboolean
131 milk_task_model_iter_next (GtkTreeModel *model,
132                            GtkTreeIter  *iter)
133 {
134         MilkTaskModelPrivate *priv;
135
136         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
137         g_return_val_if_fail (iter, FALSE);
138
139         priv = MILK_TASK_MODEL_PRIVATE (model);
140
141         return gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), iter);
142 }
143
144 static gboolean
145 milk_task_model_iter_nth_child (GtkTreeModel *model,
146                                 GtkTreeIter  *iter,
147                                 GtkTreeIter  *parent,
148                                 gint          index)
149 {
150         MilkTaskModelPrivate *priv;
151
152         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
153         g_return_val_if_fail (iter, FALSE);
154         /* we're one-dimensional */
155         g_return_val_if_fail (!parent, FALSE);
156         g_return_val_if_fail (index >= 0, FALSE);
157
158         priv = MILK_TASK_MODEL_PRIVATE (model);
159
160         return gtk_tree_model_iter_nth_child (
161                         GTK_TREE_MODEL (priv->store), iter, parent, index);
162 }
163
164 static gboolean
165 milk_task_model_iter_children (GtkTreeModel *model,
166                                GtkTreeIter  *iter,
167                                GtkTreeIter  *parent)
168 {
169         MilkTaskModelPrivate *priv;
170
171         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
172         g_return_val_if_fail (iter, FALSE);
173         /* we're one-dimensional */
174         g_return_val_if_fail (!parent, FALSE);
175
176         priv = MILK_TASK_MODEL_PRIVATE (model);
177
178         return gtk_tree_model_iter_children (
179                         GTK_TREE_MODEL (priv->store), iter, parent);
180 }
181
182 static gboolean
183 milk_task_model_iter_has_child (GtkTreeModel *model,
184                                 GtkTreeIter  *iter)
185 {
186         MilkTaskModelPrivate *priv;
187
188         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
189         g_return_val_if_fail (iter, FALSE);
190
191         priv = MILK_TASK_MODEL_PRIVATE (model);
192
193         return gtk_tree_model_iter_has_child (
194                         GTK_TREE_MODEL (priv->store), iter);
195 }
196
197 static gint
198 milk_task_model_iter_n_children (GtkTreeModel *model,
199                                  GtkTreeIter  *iter)
200 {
201         MilkTaskModelPrivate *priv;
202
203         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), -1);
204         g_return_val_if_fail (iter, -1);
205
206         priv = MILK_TASK_MODEL_PRIVATE (model);
207
208         /* we're one-dimensional */
209         if (iter)
210                 return 0;
211
212         return gtk_tree_model_iter_n_children (
213                         GTK_TREE_MODEL (priv->store), iter);
214 }
215
216 static gboolean
217 milk_task_model_iter_parent (GtkTreeModel *model,
218                              GtkTreeIter  *iter,
219                              GtkTreeIter  *child)
220 {
221         MilkTaskModelPrivate *priv;
222
223         g_return_val_if_fail (MILK_IS_TASK_MODEL (model), FALSE);
224         g_return_val_if_fail (iter, FALSE);
225         g_return_val_if_fail (child, FALSE);
226
227         priv = MILK_TASK_MODEL_PRIVATE (model);
228
229         return gtk_tree_model_iter_parent (
230                         GTK_TREE_MODEL (priv->store), iter, child);
231 }
232
233 static void
234 populate_model (MilkTaskModel *model)
235 {
236         MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (model);
237         GList *rtm_tasks;
238         GList *l;
239         GtkTreeIter iter;
240
241         gtk_list_store_clear (priv->store);
242
243         /* FIXME: poll for new tasks periodically -- there's rtm-glib API to
244          * optimize just fetching the latest ones */
245         rtm_tasks = milk_auth_get_tasks (priv->auth);
246
247         /* Populate model */
248         for (l = rtm_tasks; l; l = g_list_delete_link (l, l)) {
249                 RtmTask *rtm_task;
250                 MilkTask *task;
251
252                 rtm_task = RTM_TASK (l->data);
253
254                 /* XXX: if possible, avoid fetching these in the first place */
255                 /* Skip tasks deleted or completed. */
256                 if (rtm_task_get_completed_date (rtm_task) ||
257                     rtm_task_get_deleted_date (rtm_task)) {
258                         continue;
259                 }
260
261                 task = milk_task_new (rtm_task_get_id (rtm_task),
262                                 rtm_task_get_name (rtm_task),
263                                 /* FIXME: switch priority from int to string */
264                                 g_ascii_strtod (rtm_task_get_priority
265                                                 (rtm_task), NULL));
266
267                 gtk_list_store_append (priv->store, &iter);
268                 gtk_list_store_set (
269                                 priv->store, &iter,
270                                 MILK_TASK_MODEL_COLUMN_TASK, task,
271                                 -1);
272         }
273 }
274
275 static void
276 auth_notify_cb (MilkAuth      *auth,
277                 GParamSpec    *spec,
278                 MilkTaskModel *model)
279 {
280         if (milk_auth_get_state (auth) == MILK_AUTH_STATE_CONNECTED) {
281                 populate_model (model);
282         }
283 }
284
285 static void
286 row_changed_cb (GtkTreeModel  *model,
287                 GtkTreePath   *path,
288                 GtkTreeIter   *iter,
289                 MilkTaskModel *self)
290 {
291         gtk_tree_model_row_changed (GTK_TREE_MODEL (self), path, iter);
292 }
293
294 static void
295 row_deleted_cb (GtkTreeModel  *model,
296                 GtkTreePath   *path,
297                 MilkTaskModel *self)
298 {
299         gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
300 }
301
302 static void
303 row_inserted_cb (GtkTreeModel  *model,
304                  GtkTreePath   *path,
305                  GtkTreeIter   *iter,
306                  MilkTaskModel *self)
307 {
308         gtk_tree_model_row_inserted (GTK_TREE_MODEL (self), path, iter);
309 }
310
311 static void
312 rows_reordered_cb (GtkTreeModel  *model,
313                    GtkTreePath   *path,
314                    GtkTreeIter   *iter,
315                    gint          *new_order,
316                    MilkTaskModel *self)
317 {
318         gtk_tree_model_rows_reordered (GTK_TREE_MODEL (self), path, NULL,
319                         new_order);
320 }
321
322 void
323 milk_task_model_set_auth (MilkTaskModel *model,
324                           MilkAuth      *auth)
325 {
326         MilkTaskModelPrivate *priv;
327
328         g_return_if_fail (model);
329         g_return_if_fail (MILK_IS_TASK_MODEL (model));
330         g_return_if_fail (auth);
331         g_return_if_fail (MILK_IS_AUTH (auth));
332
333         priv = MILK_TASK_MODEL_PRIVATE (model);
334
335         if (priv->auth) {
336                 g_object_unref (priv->auth);
337         }
338         priv->auth = g_object_ref (auth);
339
340         if (milk_auth_get_state (priv->auth) == MILK_AUTH_STATE_CONNECTED) {
341                 populate_model (model);
342         } else {
343                 g_signal_connect (priv->auth, "notify::state",
344                                 G_CALLBACK (auth_notify_cb), model);
345         }
346 }
347
348 static void
349 milk_task_model_get_property (GObject    *object,
350                               guint       property_id,
351                               GValue     *value,
352                               GParamSpec *pspec)
353 {
354         MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
355
356         switch (property_id)
357         {
358                 case PROP_AUTH:
359                         g_value_set_object (value, priv->auth);
360                 break;
361
362                 default:
363                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
364                                         pspec);
365         }
366 }
367
368 static void
369 milk_task_model_set_property (GObject      *object,
370                               guint         property_id,
371                               const GValue *value,
372                               GParamSpec   *pspec)
373 {
374         switch (property_id)
375         {
376                 case PROP_AUTH:
377                         milk_task_model_set_auth (MILK_TASK_MODEL (object),
378                                         g_value_get_object (value));
379                 break;
380
381                 default:
382                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
383                                         pspec);
384         }
385 }
386
387 static void
388 milk_task_model_dispose (GObject *object)
389 {
390         MilkTaskModelPrivate *priv = MILK_TASK_MODEL_PRIVATE (object);
391
392         if (priv->auth) {
393                 g_object_unref (priv->auth);
394                 priv->auth = NULL;
395         }
396
397         g_signal_handlers_disconnect_by_func (priv->store, row_changed_cb,
398                         object);
399         g_signal_handlers_disconnect_by_func (priv->store, row_deleted_cb,
400                         object);
401         g_signal_handlers_disconnect_by_func (priv->store, row_inserted_cb,
402                         object);
403         g_signal_handlers_disconnect_by_func (priv->store, rows_reordered_cb,
404                         object);
405
406         if (priv->store) {
407                 g_object_unref (priv->store);
408                 priv->store = NULL;
409         }
410
411         G_OBJECT_CLASS (milk_task_model_parent_class)->dispose (object);
412 }
413
414 static void
415 milk_task_model_class_init (MilkTaskModelClass *klass)
416 {
417         GObjectClass *object_class = G_OBJECT_CLASS (klass);
418
419         g_type_class_add_private (klass, sizeof (MilkTaskModelPrivate));
420
421         object_class->get_property = milk_task_model_get_property;
422         object_class->set_property = milk_task_model_set_property;
423         object_class->dispose = milk_task_model_dispose;
424
425         g_object_class_install_property
426                 (object_class,
427                  PROP_AUTH,
428                  g_param_spec_object
429                          ("auth",
430                           "Authentication proxy",
431                           "Remember The Milk authentication proxy.",
432                           MILK_TYPE_AUTH,
433                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
434                           G_PARAM_STATIC_STRINGS));
435 }
436
437 static void
438 milk_task_model_init (MilkTaskModel *self)
439 {
440         MilkTaskModelPrivate *priv;
441
442         self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (
443                         self, MILK_TYPE_TASK_MODEL, MilkTaskModelPrivate);
444
445         priv->store = gtk_list_store_new (
446                         MILK_TASK_MODEL_N_COLUMNS, MILK_TYPE_TASK);
447
448         g_signal_connect (priv->store, "row-changed",
449                         G_CALLBACK (row_changed_cb), self);
450
451         g_signal_connect (priv->store, "row-deleted",
452                         G_CALLBACK (row_deleted_cb), self);
453
454         g_signal_connect (priv->store, "row-inserted",
455                         G_CALLBACK (row_inserted_cb), self);
456
457         g_signal_connect (priv->store, "rows-reordered",
458                         G_CALLBACK (rows_reordered_cb), self);
459 }
460
461 static void
462 milk_task_model_tree_model_init (GtkTreeModelIface *iface)
463 {
464         iface->get_flags       = milk_task_model_get_flags;
465         iface->get_n_columns   = milk_task_model_get_n_columns;
466         iface->get_column_type = milk_task_model_get_column_type;
467         iface->get_iter        = milk_task_model_get_iter;
468         iface->get_path        = milk_task_model_get_path;
469         iface->get_value       = milk_task_model_get_value;
470         iface->iter_next       = milk_task_model_iter_next;
471         iface->iter_children   = milk_task_model_iter_children;
472         iface->iter_has_child  = milk_task_model_iter_has_child;
473         iface->iter_n_children = milk_task_model_iter_n_children;
474         iface->iter_nth_child  = milk_task_model_iter_nth_child;
475         iface->iter_parent     = milk_task_model_iter_parent;
476 }
477
478 MilkTaskModel*
479 milk_task_model_new (MilkAuth *auth)
480 {
481         return g_object_new (MILK_TYPE_TASK_MODEL, "auth", auth, NULL);
482 }