root/trunk/libtinymailui-gtk/tny-gtk-folder-store-tree-model.c

Revision 3832 (checked in by jdapena, 6 days ago)

Fix big typo: folder list store inherits from list store, not tree store.

Line 
1 /* libtinymailui-gtk - The Tiny Mail UI library for Gtk+
2  * Copyright (C) 2006-2007 Philip Van Hoof <pvanhoof@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with self library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20
21 /**
22  * TnyGtkFolderStoreTreeModel:
23  *
24  * A #GtkTreeModel for #TnyFolderStore instances that'll become #TnyFolderStoreObserver
25  * and #TnyFolderObserver (in case the #TnyFolderStore instance is a #TnyFolder
26  * too) of each added #TnyFolderStore and each of its children en grandchildren
27  * (recursively).
28  *
29  * It will detect changes in the instance's tree structure this way and it will
30  * adapt itself to a new situation automatically. It also contains columns that
31  * contain certain popular numbers (like the unread and the total counts of
32  * #TnyFolder instances).
33  *
34  * Note that a #TnyGtkFolderStoreTreeModel is a #TnyList too. You can use the
35  * #TnyList API on instances of this type too.
36  *
37  * Note that you must make sure that you unreference #TnyFolderStore instances
38  * that you get out of the instance column of this type using the #GtkTreeModel
39  * API gtk_tree_model_get().
40  *
41  * free-function: g_object_unref
42  **/
43
44 #include <config.h>
45 #include <string.h>
46
47 #include <glib.h>
48 #include <gtk/gtk.h>
49 #include <glib/gi18n-lib.h>
50
51 #include <tny-list.h>
52 #include <tny-iterator.h>
53 #include <tny-folder.h>
54 #include <tny-folder-store.h>
55 #include <tny-simple-list.h>
56 #include <tny-merge-folder.h>
57
58 #include <tny-store-account.h>
59 #include <tny-folder-store-change.h>
60 #include <tny-folder-store-observer.h>
61 #include <tny-folder-change.h>
62 #include <tny-folder-observer.h>
63
64 #include <tny-gtk-folder-store-tree-model.h>
65
66 #include "tny-gtk-folder-store-tree-model-iterator-priv.h"
67
68 #define PATH_SEPARATOR " "
69
70 static GObjectClass *parent_class = NULL;
71
72 typedef void (*treeaddfunc) (GtkTreeStore *tree_store, GtkTreeIter *iter, GtkTreeIter *parent);
73
74
75 static void
76 add_folder_observer_weak (TnyGtkFolderStoreTreeModel *self, TnyFolder *folder)
77 {
78         if (TNY_IS_FOLDER (folder)) {
79                 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (self));
80                 self->fol_obs = g_list_prepend (self->fol_obs, folder);
81         }
82 }
83
84 static void
85 add_folder_store_observer_weak (TnyGtkFolderStoreTreeModel *self, TnyFolderStore *store)
86 {
87         if (TNY_IS_FOLDER_STORE (store)) {
88                 tny_folder_store_add_observer (store, TNY_FOLDER_STORE_OBSERVER (self));
89                 self->store_obs = g_list_prepend (self->store_obs, store);
90         }
91 }
92
93 static void
94 remove_folder_observer_weak (TnyGtkFolderStoreTreeModel *self, TnyFolder *folder, gboolean final)
95 {
96         if (TNY_IS_FOLDER (folder)) {
97                 if (!final)
98                         self->fol_obs = g_list_remove (self->fol_obs, folder);
99                 tny_folder_remove_observer (folder, (TnyFolderObserver *) self);
100         }
101 }
102
103 static void
104 remove_folder_store_observer_weak (TnyGtkFolderStoreTreeModel *self, TnyFolderStore *store, gboolean final)
105 {
106         if (TNY_IS_FOLDER_STORE (store)) {
107                 if (!final)
108                         self->store_obs = g_list_remove (self->store_obs, store);
109                 tny_folder_store_remove_observer (store, (TnyFolderStoreObserver *) self);
110         }
111 }
112
113 static void
114 recurse_folders_sync (TnyGtkFolderStoreTreeModel *self,
115                       TnyFolderStore *store,
116                       const gchar *parent_name,
117                       GtkTreeIter *parent_tree_iter)
118 {
119         TnyIterator *iter;
120         TnyList *folders = tny_simple_list_new ();
121
122         /* TODO add error checking and reporting here */
123         tny_folder_store_get_folders (store, folders, self->query, TRUE, NULL);
124         iter = tny_list_create_iterator (folders);
125
126         if (parent_name == NULL)
127                 parent_name = "";
128
129         while (!tny_iterator_is_done (iter))
130         {
131                 GtkTreeModel *mmodel = (GtkTreeModel *) self;
132                 GtkTreeStore *model = GTK_TREE_STORE (self);
133                 GObject *instance = G_OBJECT (tny_iterator_get_current (iter));
134                 GtkTreeIter tree_iter;
135                 TnyFolder *folder = NULL;
136                 TnyFolderStore *folder_store = NULL;
137                 GtkTreeIter miter;
138                 gboolean found = FALSE;
139                 GObject *mark_for_removal = NULL;
140
141                 if (instance && (TNY_IS_FOLDER (instance) || TNY_IS_MERGE_FOLDER (instance)))
142                         folder = TNY_FOLDER (instance);
143
144                 if (instance && (TNY_IS_FOLDER_STORE (instance)))
145                         folder_store = TNY_FOLDER_STORE (instance);
146
147                 /* We check whether we have this folder already in the tree, or
148                  * whether it's a brand new one. If it's a new one, we'll add
149                  * it, of course */
150
151                 if (gtk_tree_model_iter_children (mmodel, &miter, parent_tree_iter))
152                   do
153                   {
154                         GObject *citem = NULL;
155                         TnyIterator *niter = NULL;
156                        
157                         gtk_tree_model_get (mmodel, &miter,
158                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
159                                 &citem, -1);
160
161                         if (citem == instance)
162                         {
163                                 found = TRUE;
164                                 if (citem)
165                                         g_object_unref (citem);
166                                 break;
167                         }
168
169                         /* We search whether this folder that we have in the
170                          * model, still exists in the actual list. Because if
171                          * not, it probably got removed remotely (and we need
172                          * to get rid of it in the model now) */
173
174                         niter = tny_list_create_iterator (folders);
175                         while (!tny_iterator_is_done (niter))
176                         {
177                                 TnyFolder *ifound = TNY_FOLDER (tny_iterator_get_current (niter));
178                                 if (citem == (GObject *) ifound) {
179                                         if (mark_for_removal)
180                                                 g_object_unref (mark_for_removal);
181                                         mark_for_removal = g_object_ref (ifound);
182                                 }
183                                 g_object_unref (ifound);
184                                 tny_iterator_next (niter);
185                         }
186                         g_object_unref (niter);
187
188                         if (citem)
189                                 g_object_unref (citem);
190
191                   } while (gtk_tree_model_iter_next (mmodel, &miter));
192
193                 /* It was not found, so let's start adding it to the model! */
194
195                 if (!found)
196                 {
197                         gchar *name;
198                         gtk_tree_store_append (model, &tree_iter, parent_tree_iter);
199
200                         /* Making self both a folder-store as a folder observer
201                          * of this folder. This way we'll get notified when
202                          * both a removal and a creation happens. Also when a
203                          * rename happens: that's a removal and a creation. */
204
205                         if (folder)
206                         {
207                                 /* This adds a reference count to folder too. When it gets removed, that
208                                    reference count is decreased automatically by the gtktreestore infra-
209                                    structure. */
210
211                                 add_folder_observer_weak (self, folder);
212
213                                 if (self->flags & TNY_GTK_FOLDER_STORE_TREE_MODEL_FLAG_SHOW_PATH) {
214                                         if ((parent_name == NULL) || *parent_name == '\0') {
215                                                 name = g_strdup (tny_folder_get_name (folder));
216                                         } else {
217                                                 name = g_strconcat (parent_name,
218                                                                     PATH_SEPARATOR,
219                                                                     tny_folder_get_name (folder),
220                                                                     NULL);
221                                         }
222                                 } else {
223                                         name = g_strdup (tny_folder_get_name (folder));
224                                 }
225
226                                 gtk_tree_store_set  (model, &tree_iter,
227                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
228                                         name,
229                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN,
230                                         tny_folder_get_unread_count (TNY_FOLDER (folder)),
231                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN,
232                                         tny_folder_get_all_count (TNY_FOLDER (folder)),
233                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
234                                         tny_folder_get_folder_type (TNY_FOLDER (folder)),
235                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
236                                         folder, -1);
237
238                         } else {
239                                 name = g_strdup (tny_folder_get_name (folder));
240                         }
241
242                         /* it's a store by itself, so keep on recursing */
243                         if (folder_store) {
244                                 add_folder_store_observer_weak (self, folder_store);
245                                 recurse_folders_sync (self, folder_store, name, &tree_iter);
246                         }
247
248                         g_free (name);
249
250                         /* We're a folder, we'll request a status, since we've
251                          * set self to be a folder observers of folder, we'll
252                          * receive the status update. This makes it possible to
253                          * instantly get the unread and total counts, of course.
254                          *
255                          * Note that the initial value is read from the cache in
256                          * case the account is set offline, in TnyCamelFolder.
257                          * In case the account is online a STAT for POP or a
258                          * STATUS for IMAP is asked during the poke-status impl.
259                          *
260                          * This means that no priv->folder must be loaded, no
261                          * memory peak will happen, few data must be transmitted
262                          * in case we're online. Which is perfect! */
263
264                         if (folder)
265                                 tny_folder_poke_status (TNY_FOLDER (folder));
266
267                         if (mark_for_removal) {
268                                 g_object_unref (mark_for_removal);
269                                 mark_for_removal = NULL;
270                         }
271
272                 } else {
273                         if (mark_for_removal) {
274                                 g_object_unref (mark_for_removal);
275                                 mark_for_removal = NULL;
276                         }
277                 }
278
279                 g_object_unref (instance);
280
281                 tny_iterator_next (iter);
282         }
283
284         g_object_unref (iter);
285         g_object_unref (folders);
286 }
287
288
289 static const gchar*
290 get_root_name (TnyFolderStore *folder_store)
291 {
292         const gchar *root_name;
293         if (TNY_IS_ACCOUNT (folder_store))
294                 root_name = tny_account_get_name (TNY_ACCOUNT (folder_store));
295         else
296                 root_name = _("Folder bag");
297         return root_name;
298 }
299
300
301 static void
302 get_folders_cb (TnyFolderStore *fstore, gboolean cancelled, TnyList *list, GError *err, gpointer user_data)
303 {
304         TnyGtkFolderStoreTreeModel *self = (TnyGtkFolderStoreTreeModel *) user_data;
305         GtkTreeModel *model = GTK_TREE_MODEL (self);
306         GtkTreeIter iter;
307         GtkTreeIter name_iter;
308         gboolean found = FALSE;
309
310         g_object_unref (list);
311
312         /* Note that the very first time, this code will pull all folder info.
313          * Note that when it runs, you'll see LIST commands happen on the IMAP
314          * socket. Especially the first time. You'll also see STATUS commands
315          * happen. This is, indeed, normal behaviour when this component is used.*/
316
317         /* But first, let's find-back that damn account so that we have the
318          * actual iter behind it in the model. */
319
320         if (gtk_tree_model_get_iter_first (model, &iter))
321           do
322           {
323                 GObject *citem;
324
325                 gtk_tree_model_get (model, &iter,
326                         TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
327                         &citem, -1);
328
329                 if (citem == (GObject *) fstore)
330                 {
331                         name_iter = iter;
332                         found = TRUE;
333                         if (citem)
334                                 g_object_unref (citem);
335                         break;
336                 }
337
338                 g_object_unref (citem);
339
340           } while (gtk_tree_model_iter_next (model, &iter));
341
342         /* We found it, so we'll now just recursively start asking for all its
343          * folders and subfolders. The recurse_folders_sync can indeed cope with
344          * folders that already exist (it wont add them a second time). */
345
346         if (found)
347                 recurse_folders_sync (self, fstore, NULL, &name_iter);
348
349         g_object_unref (self);
350
351         return;
352 }
353
354 static void
355 tny_gtk_folder_store_tree_model_on_constatus_changed (TnyAccount *account, TnyConnectionStatus status, TnyGtkFolderStoreTreeModel *self)
356 {
357         TnyList *list = NULL;
358
359         if (!self || !TNY_IS_GTK_FOLDER_STORE_TREE_MODEL (self))
360                 return;
361
362         /* This callback handler deals with connection status changes. In case
363          * we got connected, we can expect that, at least sometimes, new folders
364          * might have arrived. We'll need to scan for those, so we'll recursively
365          * start asking the account about its folders. */
366
367         if (status == TNY_CONNECTION_STATUS_RECONNECTING || status == TNY_CONNECTION_STATUS_DISCONNECTED)
368                 return;
369
370         list = tny_simple_list_new ();
371         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account),
372                 list, self->query, TRUE, get_folders_cb, NULL, g_object_ref (self));
373
374         return;
375 }
376
377
378
379 static void
380 tny_gtk_folder_store_tree_model_on_changed (TnyAccount *account, TnyGtkFolderStoreTreeModel *self)
381 {
382         GtkTreeModel *model = GTK_TREE_MODEL (self);
383         GtkTreeIter iter;
384         GtkTreeIter name_iter;
385         gboolean found = FALSE;
386
387         if (gtk_tree_model_get_iter_first (model, &iter))
388           do
389           {
390                 GObject *citem = NULL;
391
392                 gtk_tree_model_get (model, &iter,
393                         TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
394                         &citem, -1);
395
396                 if (citem == (GObject *) account)
397                 {
398                         name_iter = iter;
399                         found = TRUE;
400                         if (citem)
401                                 g_object_unref (citem);
402                         break;
403                 }
404
405                 if (citem)
406                         g_object_unref (citem);
407
408           } while (gtk_tree_model_iter_next (model, &iter));
409
410         if (found) {
411                 gtk_tree_store_set (GTK_TREE_STORE (model), &name_iter,
412                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
413                         get_root_name (TNY_FOLDER_STORE (account)), -1);
414         }
415 }
416
417 typedef struct
418 {
419         GObject *instance;
420         guint handler_id;
421 } SignalSlot;
422
423 typedef struct
424 {
425         TnyGtkFolderStoreTreeModel *self;
426         TnyAccount *account;
427 } AccNotYetReadyInfo;
428
429 static void
430 notify_signal_slots (gpointer user_data, GObject *instance)
431 {
432         TnyGtkFolderStoreTreeModel *self = (TnyGtkFolderStoreTreeModel *) user_data;
433         int i=0;
434
435         for (i=0; i < self->signals->len; i++) {
436                 SignalSlot *slot = (SignalSlot *) self->signals->pdata[i];
437                 if (slot->instance == instance)
438                         slot->instance = NULL;
439         }
440 }
441
442 static gboolean
443 account_was_not_yet_ready_idle (gpointer user_data)
444 {
445         AccNotYetReadyInfo *info = (AccNotYetReadyInfo *) user_data;
446         gboolean repeat = TRUE;
447
448         if (tny_account_is_ready (info->account))
449         {
450                 SignalSlot *slot;
451
452                 slot = g_slice_new (SignalSlot);
453                 slot->instance = (GObject *) info->account;
454                 slot->handler_id = g_signal_connect (info->account, "connection-status-changed",
455                         G_CALLBACK (tny_gtk_folder_store_tree_model_on_constatus_changed), info->self);
456                 g_object_weak_ref (G_OBJECT (info->account), notify_signal_slots, info->self);
457                 g_ptr_array_add (info->self->signals, slot);
458
459                 slot = g_slice_new (SignalSlot);
460                 slot->instance = (GObject *) info->account;
461                 slot->handler_id = g_signal_connect (info->account, "changed",
462                         G_CALLBACK (tny_gtk_folder_store_tree_model_on_changed), info->self);
463                 g_object_weak_ref (G_OBJECT (info->account), notify_signal_slots, info->self);
464                 g_ptr_array_add (info->self->signals, slot);
465
466                 tny_gtk_folder_store_tree_model_on_constatus_changed (info->account,
467                         tny_account_get_connection_status (info->account), info->self);
468
469                 tny_gtk_folder_store_tree_model_on_changed (info->account,
470                         info->self);
471
472                 repeat = FALSE;
473         }
474
475         return repeat;
476 }
477
478 static void
479 account_was_not_yet_ready_destroy (gpointer user_data)
480 {
481         AccNotYetReadyInfo *info = (AccNotYetReadyInfo *) user_data;
482
483         g_object_unref (info->account);
484         g_object_unref (info->self);
485
486         g_slice_free (AccNotYetReadyInfo, user_data);
487 }
488
489
490 static void
491 tny_gtk_folder_store_tree_model_add_i (TnyGtkFolderStoreTreeModel *self, TnyFolderStore *folder_store, treeaddfunc func, const gchar *root_name)
492 {
493         GtkTreeStore *model = GTK_TREE_STORE (self);
494         TnyList *folders = tny_simple_list_new ();
495         GtkTreeIter name_iter;
496
497         func (model, &name_iter, NULL);
498
499         gtk_tree_store_set (model, &name_iter,
500                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, root_name,
501                 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, 0,
502                 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, 0,
503                 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, TNY_FOLDER_TYPE_ROOT,
504                 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
505                 folder_store, -1);
506
507         /* In case we added a store account, it's possible that the account
508          * will have "the account just got connected" events happening. Accounts
509          * that just got connected might have new folders for us to know about.
510          * That's why we'll handle connection changes. */
511
512         if (TNY_IS_STORE_ACCOUNT (folder_store)) {
513                 if (!tny_account_is_ready (TNY_ACCOUNT (folder_store)))
514                 {
515                         AccNotYetReadyInfo *info = g_slice_new (AccNotYetReadyInfo);
516
517                         info->account = TNY_ACCOUNT (g_object_ref (folder_store));
518                         info->self = TNY_GTK_FOLDER_STORE_TREE_MODEL (g_object_ref (self));
519
520                         g_timeout_add_full (G_PRIORITY_HIGH, 100,
521                                 account_was_not_yet_ready_idle,
522                                 info, account_was_not_yet_ready_destroy);
523
524                 } else {
525                         SignalSlot *slot;
526
527                         slot = g_slice_new (SignalSlot);
528
529                         slot->instance = (GObject *)folder_store;
530                         slot->handler_id = g_signal_connect (folder_store, "connection-status-changed",
531                                 G_CALLBACK (tny_gtk_folder_store_tree_model_on_constatus_changed), self);
532                         g_object_weak_ref (G_OBJECT (folder_store), notify_signal_slots, self);
533                         g_ptr_array_add (self->signals, slot);
534
535                         slot = g_slice_new (SignalSlot);
536                         slot->instance = (GObject *)folder_store;
537                         slot->handler_id = g_signal_connect (folder_store, "changed",
538                                 G_CALLBACK (tny_gtk_folder_store_tree_model_on_changed), self);
539                         g_object_weak_ref (G_OBJECT (folder_store), notify_signal_slots, self);
540                         g_ptr_array_add (self->signals, slot);
541                 }
542         }
543
544         /* Being online at startup is being punished by this: it'll have to
545          * wait for the connection operation to finish before this queued item
546          * will get its turn. TNY TODO: figure out how we can avoid this and
547          * already return the offline folders (if we had those from a session
548          * before this one) */
549
550         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (folder_store),
551                 folders, self->query, TRUE,  get_folders_cb, NULL, g_object_ref (self));
552
553         /* recurse_folders_sync (self, TNY_FOLDER_STORE (folder_store), &name_iter);  */
554
555
556         /* Add an observer for the root folder store, so that we can observe
557          * the actual account too. */
558
559         add_folder_store_observer_weak (self, folder_store);
560
561         /* g_object_unref (G_OBJECT (folders)); */
562
563         return;
564 }
565
566
567 /**
568  * tny_gtk_folder_store_tree_model_new:
569  * @query: the #TnyFolderStoreQuery that will be used to retrieve the child folders of each #TnyFolderStore
570  *
571  * Create a new #GtkTreeModel for showing #TnyFolderStore instances, with default flags
572  *
573  * returns: (caller-owns): a new #GtkTreeModel for #TnyFolderStore instances
574  * since: 1.0
575  * audience: application-developer
576  **/
577 GtkTreeModel*
578 tny_gtk_folder_store_tree_model_new (TnyFolderStoreQuery *query)
579 {
580         return tny_gtk_folder_store_tree_model_new_with_flags (query, 0);
581 }
582
583 /**
584  * tny_gtk_folder_store_tree_model_new:
585  * @query: the #TnyFolderStoreQuery that will be used to retrieve the child folders of each #TnyFolderStore
586  * @flags: #TnyGtkFolderStoreTreeModelFlags for setting the store
587  *
588  * Create a new #GtkTreeModel for showing #TnyFolderStore instances
589  *
590  * returns: (caller-owns): a new #GtkTreeModel for #TnyFolderStore instances
591  * since: 1.0
592  * audience: application-developer
593  **/
594 GtkTreeModel*
595 tny_gtk_folder_store_tree_model_new_with_flags (TnyFolderStoreQuery *query,
596                                                 TnyGtkFolderStoreTreeModelFlags flags)
597 {
598         TnyGtkFolderStoreTreeModel *self = g_object_new (TNY_TYPE_GTK_FOLDER_STORE_TREE_MODEL, NULL);
599
600         if (query)
601                 self->query = g_object_ref (query);
602
603         self->flags = flags;
604
605         return GTK_TREE_MODEL (self);
606 }
607
608
609
610 static void
611 tny_gtk_folder_store_tree_model_finalize (GObject *object)
612 {
613         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*) object;
614         int i = 0;
615
616         for (i = 0; i < me->signals->len; i++) {
617                 SignalSlot *slot = (SignalSlot *) me->signals->pdata [i];
618                 if (slot->instance) {
619                         g_signal_handler_disconnect (slot->instance, slot->handler_id);
620                         g_object_weak_unref (G_OBJECT (slot->instance), notify_signal_slots, object);
621                 }
622                 g_slice_free (SignalSlot, slot);
623         }
624
625         g_ptr_array_free (me->signals, TRUE);
626         me->signals = NULL;
627
628 /* Experimentally removed this. With weak referencing this ain't needed ...
629
630         while (copy) {
631                 remove_folder_observer_weak (me, (TnyFolder *) copy->data, TRUE);
632                 copy = g_list_next (copy);
633         }
634
635         copy = me->store_obs;
636         while (copy) {
637                 remove_folder_store_observer_weak (me, (TnyFolderStore *) copy->data, TRUE);
638                 copy = g_list_next (copy);
639         }
640 */
641
642         if (me->fol_obs)
643                 g_list_free (me->fol_obs);
644         me->fol_obs = NULL;
645
646         if (me->store_obs)
647                 g_list_free (me->store_obs);
648         me->store_obs = NULL;
649
650         g_mutex_lock (me->iterator_lock);
651         if (me->first) {
652                 if (me->first_needs_unref)
653                         g_list_foreach (me->first, (GFunc)g_object_unref, NULL);
654                 me->first_needs_unref = FALSE;
655                 g_list_free (me->first);
656         }
657         me->first = NULL;
658         g_mutex_unlock (me->iterator_lock);
659
660
661         g_mutex_free (me->iterator_lock);
662         me->iterator_lock = NULL;
663
664         if (me->query)
665                 g_object_unref (me->query);
666
667         (*parent_class->finalize) (object);
668 }
669
670 static void
671 tny_gtk_folder_store_tree_model_class_init (TnyGtkFolderStoreTreeModelClass *class)
672 {
673         GObjectClass *object_class;
674
675         parent_class = g_type_class_peek_parent (class);
676         object_class = (GObjectClass*) class;
677
678         object_class->finalize = tny_gtk_folder_store_tree_model_finalize;
679
680         return;
681 }
682
683 static void
684 tny_gtk_folder_store_tree_model_instance_init (GTypeInstance *instance, gpointer g_class)
685 {
686         GtkTreeStore *store = (GtkTreeStore*) instance;
687         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*) instance;
688         static GType types[] = { G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INT, G_TYPE_OBJECT };
689
690         me->signals = g_ptr_array_new ();
691         me->fol_obs = NULL;
692         me->store_obs = NULL;
693         me->iterator_lock = g_mutex_new ();
694         me->first_needs_unref = FALSE;
695         me->first = NULL;
696
697         me->flags = 0;
698
699         gtk_tree_store_set_column_types (store,
700                 TNY_GTK_FOLDER_STORE_TREE_MODEL_N_COLUMNS, types);
701
702         return;
703 }
704
705
706
707 static TnyIterator*
708 tny_gtk_folder_store_tree_model_create_iterator (TnyList *self)
709 {
710         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
711
712         /* Return a new iterator */
713
714         return TNY_ITERATOR (_tny_gtk_folder_store_tree_model_iterator_new (me));
715 }
716
717
718 /**
719  * tny_gtk_folder_store_tree_model_prepend:
720  * @self: a #TnyGtkFolderStoreTreeModel
721  * @item: a #TnyFolderStore to add
722  * @root_name: The node's root name
723  *
724  * Prepends an item to the model
725  *
726  * since: 1.0
727  * audience: application-developer
728  **/
729 void
730 tny_gtk_folder_store_tree_model_prepend (TnyGtkFolderStoreTreeModel *self, TnyFolderStore* item, const gchar *root_name)
731 {
732         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
733
734         g_mutex_lock (me->iterator_lock);
735
736         /* Prepend something to the list */
737         me->first = g_list_prepend (me->first, item);
738
739         tny_gtk_folder_store_tree_model_add_i (me, TNY_FOLDER_STORE (item),
740                 gtk_tree_store_prepend, root_name);
741
742         g_mutex_unlock (me->iterator_lock);
743 }
744
745
746 static void
747 tny_gtk_folder_store_tree_model_prepend_i (TnyList *self, GObject* item)
748 {
749         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
750
751         g_mutex_lock (me->iterator_lock);
752
753         /* Prepend something to the list */
754         me->first = g_list_prepend (me->first, item);
755
756         tny_gtk_folder_store_tree_model_add_i (me, TNY_FOLDER_STORE (item),
757                 gtk_tree_store_prepend, get_root_name (TNY_FOLDER_STORE (item)));
758
759         g_mutex_unlock (me->iterator_lock);
760 }
761
762 /**
763  * tny_gtk_folder_store_tree_model_append:
764  * @self: a #TnyGtkFolderStoreTreeModel
765  * @item: a #TnyFolderStore to add
766  * @root_name: The node's root name
767  *
768  * Appends an item to the model
769  *
770  * since: 1.0
771  * audience: application-developer
772  **/
773 void
774 tny_gtk_folder_store_tree_model_append (TnyGtkFolderStoreTreeModel *self, TnyFolderStore* item, const gchar *root_name)
775 {
776         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
777
778         g_mutex_lock (me->iterator_lock);
779
780         /* Append something to the list */
781         me->first = g_list_append (me->first, item);
782
783         tny_gtk_folder_store_tree_model_add_i (me, TNY_FOLDER_STORE (item),
784                 gtk_tree_store_append, root_name);
785
786         g_mutex_unlock (me->iterator_lock);
787 }
788
789 static void
790 tny_gtk_folder_store_tree_model_append_i (TnyList *self, GObject* item)
791 {
792         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
793
794         g_mutex_lock (me->iterator_lock);
795
796         /* Append something to the list */
797         me->first = g_list_append (me->first, item);
798
799         tny_gtk_folder_store_tree_model_add_i (me, TNY_FOLDER_STORE (item),
800                 gtk_tree_store_append, get_root_name (TNY_FOLDER_STORE (item)));
801
802         g_mutex_unlock (me->iterator_lock);
803 }
804
805 static guint
806 tny_gtk_folder_store_tree_model_get_length (TnyList *self)
807 {
808         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
809         guint retval = 0;
810
811         g_mutex_lock (me->iterator_lock);
812
813         retval = me->first?g_list_length (me->first):0;
814
815         g_mutex_unlock (me->iterator_lock);
816
817         return retval;
818 }
819
820 static void
821 tny_gtk_folder_store_tree_model_remove (TnyList *self, GObject* item)
822 {
823         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
824         GtkTreeModel *model = GTK_TREE_MODEL (me);
825         GtkTreeIter iter;
826
827         g_return_if_fail (G_IS_OBJECT (item));
828         g_return_if_fail (G_IS_OBJECT (me));
829
830         /* Remove something from the list */
831
832         g_mutex_lock (me->iterator_lock);
833
834         me->first = g_list_remove (me->first, (gconstpointer)item);
835
836         /* This doesn't have to be recursive as only the first-level folders are
837            actually really part of the list. */
838
839         if (gtk_tree_model_get_iter_first (model, &iter))
840           do
841           {
842                 GObject *citem = NULL;
843                 gtk_tree_model_get (model, &iter,
844                         TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
845                         &citem, -1);
846                 if (citem == item)
847                 {
848                         /* This removes a reference count */
849                         gtk_tree_store_remove (GTK_TREE_STORE (me), &iter);
850                         if (citem)
851                                 g_object_unref (citem);
852                         break;
853                 }
854                 if (citem)
855                         g_object_unref (citem);
856
857           } while (gtk_tree_model_iter_next (model, &iter));
858
859         g_mutex_unlock (me->iterator_lock);
860 }
861
862
863 static TnyList*
864 tny_gtk_folder_store_tree_model_copy_the_list (TnyList *self)
865 {
866         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
867         TnyGtkFolderStoreTreeModel *copy = g_object_new (TNY_TYPE_GTK_FOLDER_STORE_TREE_MODEL, NULL);
868         GList *list_copy = NULL;
869
870         /* This only copies the TnyList pieces. The result is not a correct or good
871            TnyHeaderListModel. But it will be a correct TnyList instance. It's the
872            only thing the user of this method expects (that is the contract of it).
873
874            The new list will point to the same instances, of course. It's only a
875            copy of the list-nodes of course. */
876
877         g_mutex_lock (me->iterator_lock);
878         list_copy = g_list_copy (me->first);
879         g_list_foreach (list_copy, (GFunc)g_object_ref, NULL);
880         copy->first_needs_unref = TRUE;
881         copy->first = list_copy;
882         g_mutex_unlock (me->iterator_lock);
883
884         return TNY_LIST (copy);
885 }
886
887 static void
888 tny_gtk_folder_store_tree_model_foreach_in_the_list (TnyList *self, GFunc func, gpointer user_data)
889 {
890         TnyGtkFolderStoreTreeModel *me = (TnyGtkFolderStoreTreeModel*)self;
891
892         /* Foreach item in the list (without using a slower iterator) */
893
894         g_mutex_lock (me->iterator_lock);
895         g_list_foreach (me->first, func, user_data);
896         g_mutex_unlock (me->iterator_lock);
897
898         return;
899 }
900
901 typedef struct _FindParentHelperInfo {
902         GtkTreeIter *iter;
903         TnyFolder *folder;
904         TnyAccount *account;
905         gboolean found;
906 } FindParentHelperInfo;
907
908 static gboolean
909 find_parent_helper (GtkTreeModel *model,
910                     GtkTreePath *path,
911                     GtkTreeIter *iter,
912                     gpointer userdata)
913 {
914         TnyFolderStore *folder_store;
915         FindParentHelperInfo *helper_info = (FindParentHelperInfo *) userdata;
916         TnyList *children;
917         TnyIterator *iterator;
918
919         gtk_tree_model_get (model, iter,
920                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
921