root/trunk/libtinymailui-gtk/tny-gtk-header-list-model.c

Revision 3822 (checked in by mbonnin, 2 weeks ago)

update the doc

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  * TnyGtkFolderStoreTreeModel:
22  *
23  * A #GtkTreeModel for #TnyHeader instances. Just like with any other #TnyList,
24  * you can use it together with a #TnyFolderMonitor. You will typically fill
25  * instances of this type using tny_folder_get_headers().
26  *
27  * Note that a #TnyGtkFolderStoreTreeModel is a #TnyList too. You can use the
28  * #TnyList API on instances of this type too. This is what #TnyFolderMonitor
29  * does too.
30  *
31  * Note that you must make sure that you unreference #TnyHeader instances that
32  * you get out of the instance column of this type using the #GtkTreeModel API
33  * gtk_tree_model_get().
34  *
35  * free-function: g_object_unref
36  **/
37
38 #include <config.h>
39
40 #include <glib.h>
41 #include <glib/gi18n-lib.h>
42 #include <gdk/gdk.h>
43 #include <gtk/gtk.h>
44
45 #include <tny-gtk-header-list-model.h>
46 #include <tny-gtk-header-list-iterator-priv.h>
47
48 #include <tny-header.h>
49 #include <tny-folder.h>
50
51 #include <tny-list.h>
52 #include <tny-iterator.h>
53
54 #define INDEX_OFFSET 64
55 #define INDEX_THRESHOLD 5000
56
57 static GObjectClass *parent_class;
58
59 #include "tny-gtk-header-list-iterator-priv.h"
60
61 #define TINYMAIL_ENABLE_PRIVATE_API
62 #include "tny-common-priv.h"
63 #undef TINYMAIL_ENABLE_PRIVATE_API
64
65
66 static gint
67 add_del_timeout (TnyGtkHeaderListModel *me, guint num)
68 {
69         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (me);
70
71         gint retval = 0;
72         g_mutex_lock (priv->to_lock);
73         if (!priv->del_timeouts)
74                 priv->del_timeouts = g_array_new (FALSE, FALSE, sizeof (guint));
75         g_array_append_val (priv->del_timeouts, num);
76         retval = priv->del_timeouts->len-1;
77         g_mutex_unlock (priv->to_lock);
78
79         return retval;
80 }
81
82 static void
83 remove_del_timeouts (TnyGtkHeaderListModel *me)
84 {
85         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (me);
86
87         gint i;
88         guint src;
89
90         g_mutex_lock (priv->to_lock);
91
92         if (!priv->del_timeouts)
93         {
94                 g_mutex_unlock (priv->to_lock);
95                 return;
96         }
97
98         for (i = 0; i < priv->del_timeouts->len; i++)
99         {
100                 src = g_array_index (priv->del_timeouts, guint, i);
101                 if (src > 0)
102                         g_source_remove (src);
103                 g_array_index (priv->del_timeouts, guint, i) = 0;
104         }
105         g_array_free (priv->del_timeouts, TRUE);
106         priv->del_timeouts = NULL;
107         g_mutex_unlock (priv->to_lock);
108 }
109
110 static guint
111 tny_gtk_header_list_model_get_flags (GtkTreeModel *self)
112 {
113         return 0;
114 }
115
116 static gint
117 tny_gtk_header_list_model_get_n_columns (GtkTreeModel *self)
118 {
119         return TNY_GTK_HEADER_LIST_MODEL_N_COLUMNS;
120 }
121
122 static GType
123 tny_gtk_header_list_model_get_column_type (GtkTreeModel *self, gint column)
124 {
125         GType retval;
126
127         switch (column)
128         {
129                 case TNY_GTK_HEADER_LIST_MODEL_CC_COLUMN:
130                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_COLUMN:
131                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN:
132                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
133                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
134                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
135                         retval = G_TYPE_STRING;
136                         break;
137                 case TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN:
138                         retval = G_TYPE_OBJECT;
139                         break;
140                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
141                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
142                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
143                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
144                         retval = G_TYPE_INT;
145                         break;
146                 default:
147                         retval = G_TYPE_INVALID;
148                         break;
149         }
150
151
152         return retval;
153 }
154
155 static gboolean
156 tny_gtk_header_list_model_get_iter (GtkTreeModel *self, GtkTreeIter *iter, GtkTreePath *path)
157 {
158         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
159
160         gint i, tlen;
161         gboolean retval=FALSE;
162
163         g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
164
165         g_static_rec_mutex_lock (priv->iterator_lock);
166
167         i = gtk_tree_path_get_indices (path)[0];
168
169         g_mutex_lock (priv->ra_lock);
170         tlen = priv->cur_len;
171         g_mutex_unlock (priv->ra_lock);
172
173         if (i >= tlen)
174                 retval = FALSE;
175         else {
176                 if (i >= 0 && i < tlen) {
177                         iter->stamp = priv->stamp;
178                         iter->user_data = (gpointer) i;
179                         retval = TRUE;
180                 }
181         }
182
183         g_static_rec_mutex_unlock (priv->iterator_lock);
184
185         return retval;
186 }
187
188 static GtkTreePath *
189 tny_gtk_header_list_model_get_path (GtkTreeModel *self, GtkTreeIter *iter)
190 {
191         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
192         GtkTreePath *tree_path;
193         gint i = 0, tlen;
194
195         /* Return the path of an existing GtkTreeIter */
196         if  (!(iter->stamp == priv->stamp))
197                 return NULL;
198
199         g_static_rec_mutex_lock (priv->iterator_lock);
200
201         i = (gint) iter->user_data;
202
203         g_mutex_lock (priv->ra_lock);
204         tlen = priv->cur_len;
205         g_mutex_unlock (priv->ra_lock);
206
207         if (i < 0 || i >= tlen) {
208                 g_static_rec_mutex_unlock (priv->iterator_lock);
209                 return NULL;
210         }
211
212         tree_path = gtk_tree_path_new ();
213         gtk_tree_path_append_index (tree_path, i);
214
215         g_static_rec_mutex_unlock (priv->iterator_lock);
216
217         return tree_path;
218 }
219
220
221 static gchar *
222 _get_readable_date (time_t file_time_raw)
223 {
224         struct tm file_time;
225         static gchar readable_date[64];
226         gsize readable_date_size;
227
228         gmtime_r (&file_time_raw, &file_time);
229
230         readable_date_size = strftime (readable_date, 63, _("%Y-%m-%d, %-I:%M %p"), &file_time);
231
232         if (readable_date_size > 0)
233                 return readable_date;
234
235         return NULL;
236 }
237
238 /**
239  * tny_gtk_header_list_model_received_date_sort_func:
240  * @model: a #GtkTreeModel the comparison is within
241  * @a: a #GtkTreeIter in @model
242  * @b: another #GtkTreeIter in @model
243  * @user_data: (null-ok): user data passed
244  *
245  * A #GtkTreeIterCompareFunc that sorts using the received date
246  *
247  * returns: a negative integer, zero, or a positive integer
248  * since: 1.0
249  * audience: application-developer, tinymail-developer
250  **/
251 gint
252 tny_gtk_header_list_model_received_date_sort_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
253 {
254         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (model);
255
256         TnyHeader *hdr_a, *hdr_b;
257         time_t recv_a, recv_b;
258
259         /* Get the index out of the iter, and use the item by looking it up in
260          * the GPtrArray. This must be quite efficient, as this is called lots
261          * of times while you are sorting things. */
262
263         g_static_rec_mutex_lock (priv->iterator_lock);
264
265         hdr_a = priv->items->pdata[(gint)a->user_data];
266         hdr_b = priv->items->pdata[(gint)b->user_data];
267
268         recv_a = tny_header_get_date_received (hdr_a);
269         recv_b = tny_header_get_date_received (hdr_b);
270         g_static_rec_mutex_unlock (priv->iterator_lock);
271
272         return (recv_a - recv_b);
273 }
274
275 /**
276  * tny_gtk_header_list_model_sent_date_sort_func:
277  * @model: the #GtkTreeModel the comparison is within
278  * @a: a GtkTreeIter in @model
279  * @b: another GtkTreeIter in @model
280  * @user_data: (null-ok): user data passed
281  *
282  * A @GtkTreeIterCompareFunc that sorts using the sent date
283  *
284  * returns: a negative integer, zero, or a positive integer
285  * since: 1.0
286  * audience: application-developer, tinymail-developer
287  **/
288 gint 
289 tny_gtk_header_list_model_sent_date_sort_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
290 {
291         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (model);
292
293         TnyHeader *hdr_a, *hdr_b;
294         time_t recv_a, recv_b;
295
296         /* Get the index out of the iter, and use the item by looking it up in
297          * the GPtrArray. This must be quite efficient, as this is called lots
298          * of times while you are sorting things. */
299
300         g_static_rec_mutex_lock (priv->iterator_lock);
301
302         hdr_a = priv->items->pdata[(gint)a->user_data];
303         hdr_b = priv->items->pdata[(gint)b->user_data];
304
305         recv_a = tny_header_get_date_sent (hdr_a);
306         recv_b = tny_header_get_date_sent (hdr_b);
307
308         g_static_rec_mutex_unlock (priv->iterator_lock);
309
310         return (recv_a - recv_b);
311 }
312
313
314 static void
315 set_dummy (gint column, GValue *value)
316 {
317         switch (column)
318         {
319                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
320                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
321                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
322                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN:
323                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_COLUMN:
324                 case TNY_GTK_HEADER_LIST_MODEL_CC_COLUMN:
325                         g_value_init (value, G_TYPE_STRING);
326                         g_value_set_string (value, "Expunged");
327                         break;
328                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
329                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
330                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
331                         g_value_init (value, G_TYPE_INT);
332                         g_value_set_int (value, -1);
333                         break;
334                 case TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN: {
335                         static TnyHeader *header = NULL;
336                         if (!header)
337                                 header = tny_expunged_header_new ();
338                         g_value_init (value, G_TYPE_OBJECT);
339                         g_value_set_object (value, header);
340                         } break;
341                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
342                         g_value_init (value, G_TYPE_INT);
343                         g_value_set_int (value, TNY_HEADER_FLAG_EXPUNGED);
344                         break;
345                 default:
346                         break;
347         }
348 }
349
350 static void
351 tny_gtk_header_list_model_get_value (GtkTreeModel *self, GtkTreeIter *iter, gint column, GValue *value)
352 {
353         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
354
355         gchar *str;
356         gchar *rdate = NULL;
357         gint i;
358
359         if (iter->stamp != priv->stamp) {
360                 g_warning ("GtkTreeModel in invalid state\n");
361                 set_dummy (column, value);
362                 return;
363         }
364
365         g_static_rec_mutex_lock (priv->iterator_lock);
366
367         /* Get the index out of the iter, and use the item by looking it up in
368          * the GPtrArray. This must be quite efficient, as this is called lots
369          * of times while you are sorting things. */
370
371         i = (gint) iter->user_data;
372
373         /* Exception on list_model->cur_len (this one truly allows the full
374          * length of the available data in the array, not only the registered
375          * length, although in 99.999999% of the cases it's the same) */
376         if (i < 0 || i >= priv->items->len)
377         {
378                 set_dummy (column, value);
379                 g_warning ("GtkTreeModel in invalid state\n");
380                 g_static_rec_mutex_unlock (priv->iterator_lock);
381                 return;
382         }
383
384         if (priv->items->pdata[i] == NULL) {
385                 g_warning ("GtkTreeModel in invalid state\n");
386                 set_dummy (column, value);
387                 g_static_rec_mutex_unlock (priv->iterator_lock);
388                 return;
389         }
390
391         if (!TNY_IS_HEADER (priv->items->pdata[i])) {
392                 g_warning ("GtkTreeModel in invalid state\n");
393                 set_dummy (column, value);
394                 g_static_rec_mutex_unlock (priv->iterator_lock);
395                 return;
396         }
397
398         switch (column)
399         {
400                 case TNY_GTK_HEADER_LIST_MODEL_CC_COLUMN:
401                         g_value_init (value, G_TYPE_STRING);
402                         str = tny_header_dup_cc ((TnyHeader*) priv->items->pdata[i]);
403                         if (str)
404                                 g_value_take_string (value, str);
405                         break;
406                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_COLUMN:
407                         g_value_init (value, G_TYPE_STRING);
408                         rdate = _get_readable_date (tny_header_get_date_sent ((TnyHeader*) priv->items->pdata[i]));
409                         if (rdate)
410                                 g_value_set_string (value, rdate);
411                         else
412                                 g_value_set_string (value, "");
413                         break;
414                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN:
415                         g_value_init (value, G_TYPE_STRING);
416                         rdate = _get_readable_date (tny_header_get_date_received ((TnyHeader*) priv->items->pdata[i]));
417                         if (rdate)
418                                 g_value_set_string (value, rdate);
419                         else
420                                 g_value_set_string (value, "");
421                         break;
422                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
423                         g_value_init (value, G_TYPE_INT);
424                         g_value_set_int (value,
425                                             tny_header_get_date_sent ((TnyHeader*) priv->items->pdata[i]));
426                         break;
427                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
428                         g_value_init (value, G_TYPE_INT);
429                         g_value_set_int (value,
430                                          tny_header_get_date_received ((TnyHeader*) priv->items->pdata[i]));
431                         break;
432
433                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
434                         g_value_init (value, G_TYPE_INT);
435                         g_value_set_int (value, tny_header_get_message_size((TnyHeader*) priv->items->pdata[i]));
436                         break;                 
437                 case TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN:
438                         g_value_init (value, G_TYPE_OBJECT);
439                         g_value_set_object (value, (TnyHeader*) priv->items->pdata[i]);
440                         break;
441                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
442                         g_value_init (value, G_TYPE_STRING);
443                         str = tny_header_dup_to ((TnyHeader*) priv->items->pdata[i]);
444                         if (str)
445                                 g_value_take_string (value, str);
446                         break;
447                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
448                         g_value_init (value, G_TYPE_STRING);
449                         str = tny_header_dup_subject ((TnyHeader*) priv->items->pdata[i]);
450                         if (str)
451                                 g_value_take_string (value, str);
452                         break;
453                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
454                         g_value_init (value, G_TYPE_STRING);
455                         str = tny_header_dup_from ((TnyHeader*) priv->items->pdata[i]);
456                         if (str)
457                                 g_value_take_string (value, str);
458                         break;
459                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
460                         g_value_init (value, G_TYPE_INT);
461                         g_value_set_int (value, tny_header_get_flags ((TnyHeader*) priv->items->pdata[i]));
462                         break;
463                 default:
464                         break;
465         }
466
467         g_static_rec_mutex_unlock (priv->iterator_lock);
468
469         return;
470 }
471
472 static gboolean
473 tny_gtk_header_list_model_iter_next (GtkTreeModel *self, GtkTreeIter *iter)
474 {
475         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
476         gboolean retval;
477         gint newv;
478
479         if (iter->stamp != priv->stamp)
480                 return FALSE;
481
482         g_static_rec_mutex_lock (priv->iterator_lock);
483
484         /* The next will simply be the current plus one: That's because we are
485          * storing the GPtrArray's index in the iters. Simple, efficient. Just
486          * always make sure that such an iter doesn't contain an index that
487          * can't be part of the GPtrArray instance (would be devastating). */
488
489         newv = ((gint) iter->user_data);
490         newv++;
491         iter->user_data = (gpointer) newv;
492
493         g_mutex_lock (priv->ra_lock);
494         retval = (newv >= 0 && newv < priv->cur_len);
495         g_mutex_unlock (priv->ra_lock);
496
497         if (!retval) {
498                 iter->stamp = -1;
499                 iter->user_data = (gpointer) 0;
500         }
501
502         g_static_rec_mutex_unlock (priv->iterator_lock);
503
504         return retval;
505 }
506
507 static gboolean
508 tny_gtk_header_list_model_iter_has_child (GtkTreeModel *self, GtkTreeIter *iter)
509 {
510         /* This is a flat list (we don't yet support threaded views anyway), so
511          * the answer is flat-out no. Always. */
512
513         return FALSE;
514 }
515
516 static gint
517 tny_gtk_header_list_model_iter_n_children (GtkTreeModel *self, GtkTreeIter *iter)
518 {
519         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
520         gint retval = -1;
521
522         /* Return the amount of children for this GtkTreeIter. Because this
523            is a flat list and has_child is always FALSE, we'll just always
524            return the full length. */
525
526         g_static_rec_mutex_lock (priv->iterator_lock);
527
528         if (G_LIKELY (!iter))
529                 retval = priv->cur_len;
530
531         g_static_rec_mutex_unlock (priv->iterator_lock);
532
533         return retval;
534 }
535
536 static gboolean
537 tny_gtk_header_list_model_iter_nth_child (GtkTreeModel *self, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
538 {
539         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
540         gboolean retval = FALSE;
541         gint tlen;
542
543         if (G_UNLIKELY (parent))
544                 return FALSE;
545
546         g_static_rec_mutex_lock (priv->iterator_lock);
547
548         g_mutex_lock (priv->ra_lock);
549         tlen = priv->cur_len;
550         g_mutex_unlock (priv->ra_lock);
551
552         if (n >= 0 && n < tlen)
553         {
554                 iter->stamp = priv->stamp;
555                 iter->user_data = (gpointer) n;
556                 retval = TRUE;
557         } else {
558                 iter->stamp = -1;
559                 iter->user_data = (gpointer) 0;
560         }
561
562         g_static_rec_mutex_unlock (priv->iterator_lock);
563
564         return retval;
565 }
566
567 static gboolean
568 tny_gtk_header_list_model_iter_children (GtkTreeModel *tree_model,
569                                          GtkTreeIter  *iter,
570                                          GtkTreeIter  *parent)
571 {
572         gboolean retval = FALSE;
573
574         retval = tny_gtk_header_list_model_iter_nth_child (tree_model, iter, parent, 0);
575        
576         return retval;
577 }
578
579
580 static void
581 tny_gtk_header_list_model_tree_model_init (GtkTreeModelIface *iface)
582 {
583         iface->get_flags = tny_gtk_header_list_model_get_flags;
584         iface->get_n_columns = tny_gtk_header_list_model_get_n_columns;
585         iface->get_column_type = tny_gtk_header_list_model_get_column_type;
586         iface->get_iter = tny_gtk_header_list_model_get_iter;
587         iface->get_path = tny_gtk_header_list_model_get_path;
588         iface->get_value = tny_gtk_header_list_model_get_value;
589         iface->iter_next = tny_gtk_header_list_model_iter_next;
590         iface->iter_has_child = tny_gtk_header_list_model_iter_has_child;
591         iface->iter_n_children = tny_gtk_header_list_model_iter_n_children;
592         iface->iter_children = tny_gtk_header_list_model_iter_children;
593         iface->iter_nth_child = tny_gtk_header_list_model_iter_nth_child;
594
595         return;
596 }
597
598
599 static void
600 notify_views_add_destroy (gpointer data)
601 {
602         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (data);
603
604         g_mutex_lock (priv->ra_lock);
605         priv->updating_views = -1;
606         g_mutex_unlock (priv->ra_lock);
607
608         if (priv->timeout_span < 5000)
609                 priv->timeout_span += 500;
610         priv->add_timeout = 0;
611         g_object_unref (data);
612
613         return;
614 }
615
616 #if GTK_CHECK_VERSION (2,16,0)
617
618 static inline GtkTreePath *
619 gtk_tree_path_new_internal (gint index)
620 {
621         GtkTreePath *path = gtk_tree_path_new ();
622         gtk_tree_path_append_index (path, index);
623         return path;
624 }
625 static inline void
626 gtk_tree_path_free_internal (GtkTreePath *path_in)
627 {
628         gtk_tree_path_free (path_in);
629 }
630
631 #else
632
633 typedef struct
634 {
635   gint depth;
636   gint *indices;
637 }GtkTreePathInternal;
638
639 static inline GtkTreePath *
640 gtk_tree_path_new_internal (gint index)
641 {
642   GtkTreePathInternal *retval = g_slice_new (GtkTreePathInternal);
643   retval->depth = 1;
644   retval->indices = g_slice_alloc (sizeof(gint));
645   retval->indices[0] = index;
646   return (GtkTreePath *) retval;
647 }
648
649 static inline void
650 gtk_tree_path_free_internal (GtkTreePath *path_in)
651 {
652   GtkTreePathInternal *path = (GtkTreePathInternal *) path_in;
653   g_slice_free1 (sizeof(gint), path->indices);
654   g_slice_free (GtkTreePathInternal, path);
655 }
656
657 #endif
658
659 static gboolean
660 notify_views_add (gpointer data)
661 {
662         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (data);
663         gint already_registered, going_tb_registered, i;
664  
665         gboolean needmore = FALSE;
666         GtkTreePath *path;
667         GtkTreeIter iter;
668
669         g_static_rec_mutex_lock (priv->iterator_lock);
670
671         g_mutex_lock (priv->ra_lock);
672         priv->updating_views++;
673         if (priv->registered >= priv->items->len) {
674                 g_mutex_unlock (priv->ra_lock);
675                 g_static_rec_mutex_unlock (priv->iterator_lock);
676                 return FALSE;
677         }
678
679         already_registered = priv->registered;
680
681         if (priv->items->len - already_registered > 3000)
682         {
683                 going_tb_registered = already_registered + 3000;
684                 needmore = TRUE;
685         } else
686                 going_tb_registered = priv->items->len;
687
688         priv->registered = going_tb_registered;
689
690         if (priv->updating_views < 2)
691                 needmore = TRUE;
692
693         g_mutex_unlock (priv->ra_lock);
694         g_static_rec_mutex_unlock (priv->iterator_lock);
695
696         gdk_threads_enter();
697
698         for (i = already_registered; i < going_tb_registered; i++)
699         {
700                 iter.stamp = priv->stamp;
701                 iter.user_data = (gpointer) i;
702                 path = gtk_tree_path_new_internal (i);
703                 priv->cur_len = i+1;
704                 gtk_tree_model_row_inserted ((GtkTreeModel *) data, path, &iter);
705                 gtk_tree_path_free_internal (path);
706         }
707         gdk_threads_leave();
708
709         return needmore;
710 }
711
712
713 static gboolean
714 uid_matcher (TnyList *list, GObject *item, gpointer match_data)
715 {
716         gboolean result = FALSE;
717         char *uid = tny_header_dup_uid ((TnyHeader *) item);
718
719         if (uid && !strcmp (uid, (const char*) match_data))
720                 result = TRUE;
721
722         g_free (uid);
723
724         return result;
725 }
726
727
728 /* This will be called often while you are in tny_folder_refresh(_async). It can
729  * and will be called from a thread, so we must cope with that in case we want
730  * to update the GtkTreeViews that have been attached to this model (self). */
731 static void
732 tny_gtk_header_list_model_prepend (TnyList *self, GObject* item)
733 {
734         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (self);
735
736         g_static_rec_mutex_lock (priv->iterator_lock);
737
738         if (priv->no_duplicates) {
739                 gchar *uid = tny_header_dup_uid ((TnyHeader *) item);
740                 if (uid) {
741                         tny_list_remove_matches (self, uid_matcher, (gpointer) uid);
742                         g_free (uid);
743                 }
744         }
745
746         /* Prepend something to the list itself. The get_length will auto update
747          * because that one uses GPtrArray's len property. We are reusing the
748          * iterator_lock for locking this out, by the way. */
749
750         g_object_ref (item);
751
752         g_mutex_lock (priv->ra_lock);
753
754         g_ptr_array_add (priv->items, item);
755
756         /* This prepend will happen very often, the notificating of the view is,
757          * however, quite slow and gdk wants us to do this from the mainloop */
758
759         if (priv->updating_views == -1)
760         {
761                 priv->updating_views = 0;
762                 g_object_ref (self);
763
764                 if (priv->add_timeout > 0) {
765                         g_source_remove (priv->add_timeout);
766                         priv->add_timeout = 0;
767                 }
768
769                 priv->add_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
770                         priv->timeout_span, notify_views_add, self,
771                         notify_views_add_destroy);
772         }
773
774         g_mutex_unlock (priv->ra_lock);
775
776         g_static_rec_mutex_unlock (priv->iterator_lock);
777
778         return;
779 }
780
781 static void
782 tny_gtk_header_list_model_append (TnyList *self, GObject* item)
783 {
784         /* Not really correct, but what the heck */
785         tny_gtk_header_list_model_prepend (self, item);
786 }
787
788
789 typedef struct
790 {
791         TnyGtkHeaderListModel *self;
792         GObject *item;
793         /* GMainLoop *loop; */
794         gint src;
795 } notify_views_data_t;
796
797 typedef struct
798 {
799         TnyGtkHeaderListModel *self;
800         GList *items;
801         /* GMainLoop *loop; */
802         gint src;
803 } notify_views_data_list_t;
804
805
806 static void
807 notify_views_delete_destroy (gpointer data)
808 {
809         notify_views_data_t *stuff = data;
810         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (stuff->self);
811
812         g_mutex_lock (priv->to_lock);
813         if (stuff->src != -1 && stuff->src < priv->del_timeouts->len)
814                 g_array_index (priv->del_timeouts, guint, (guint) stuff->src) = 0;
815         g_mutex_unlock (priv->to_lock);
816
817         g_object_unref (stuff->item);
818         g_object_unref (stuff->self);
819         /* g_main_loop_unref (stuff->loop); */
820
821         g_slice_free (notify_views_data_t, data);
822         return;
823 }
824
825
826 static gboolean
827 notify_views_delete (gpointer data)
828 {
829         notify_views_data_t *stuff = data;
830         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (stuff->self);
831
832         GtkTreePath *path;
833         GtkTreeIter iter;
834         gint i; gboolean found = FALSE;
835         GObject *mitem, *item = stuff->item;
836
837         g_static_rec_mutex_lock (priv->iterator_lock);
838
839         for (i=0; i < priv->items->len; i++)
840                 if (priv->items->pdata[i] == item)
841                 {
842                         found = TRUE;
843                         break;
844                 }
845
846         if (found)
847         {
848                 iter.stamp = priv->stamp;
849                 iter.user_data = (gpointer) i;
850                 path = gtk_tree_path_new ();
851                 gtk_tree_path_append_index (path, i);
852                 gtk_tree_model_row_deleted ((GtkTreeModel *) stuff->self, path);
853                 priv->stamp++;
854                 g_mutex_lock (priv->ra_lock);
855                 priv->cur_len--;
856                 priv->registered--;
857                 g_mutex_unlock (priv->ra_lock);
858                 gtk_tree_path_free (path);
859
860                 mitem = g_ptr_array_remove_index (priv->items, i);
861                 if (mitem)
862                         g_object_unref (mitem);
863         }
864
865         g_static_rec_mutex_unlock (priv->iterator_lock);
866
867         /* if (g_main_loop_is_running (stuff->loop))
868                 g_main_loop_quit (stuff->loop); */
869
870         return FALSE;
871 }
872
873
874 static void
875 tny_gtk_header_list_model_remove (TnyList *self, GObject* item)
876 {
877         notify_views_data_t *stuff;
878         guint src;
879
880         stuff = g_slice_new (notify_views_data_t);
881         stuff->src = -1;
882         stuff->self = g_object_ref (self);
883         stuff->item = g_object_ref (item);
884
885         /* stuff->loop = g_main_loop_new (NULL, FALSE); */
886
887         src = g_timeout_add_full (G_PRIORITY_HIGH_IDLE, 0,
888                 notify_views_delete, stuff, notify_views_delete_destroy);
889         stuff->src = (gint) add_del_timeout ((TnyGtkHeaderListModel *) self, src);
890
891         /* This truly sucks :-(
892         g_main_loop_run (stuff->loop); */
893
894         return;
895 }
896
897
898 static void
899 notify_views_delete_destroy_list (gpointer data)
900 {
901         notify_views_data_list_t *stuff = data;
902         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (stuff->self);
903
904         g_mutex_lock (priv->to_lock);
905         if (stuff->src != -1 && stuff->src < priv->del_timeouts->len)
906                 g_array_index (priv->del_timeouts, guint, (guint) stuff->src) = 0;
907         g_mutex_unlock (priv->to_lock);
908
909         while (stuff->items) {
910                 g_object_unref (stuff->items->data);
911                 stuff->items = g_list_next (stuff->items);
912         }
913
914         if (stuff->items)
915                 g_list_free (stuff->items);
916
917         g_object_unref (stuff->self);
918         /* g_main_loop_unref (stuff->loop); */
919
920         g_slice_free (notify_views_data_list_t, data);
921         return;
922 }
923
924
925 static gboolean
926 notify_views_delete_list (gpointer data)
927 {
928         notify_views_data_list_t *stuff = data;
929         TnyGtkHeaderListModelPriv *priv = TNY_GTK_HEADER_LIST_MODEL_GET_PRIVATE (stuff->self);
930         GList *copy = stuff->items;
931
932         g_static_rec_mutex_lock (priv->iterator_lock);
933
934         while (copy)
935         {
936                 gint i; gboolean found = FALSE;
937                 GtkTreePath *path;
938                 GtkTreeIter iter;
939                 GObject *mitem, *item = copy->data;
940
941                 for (i=0; i < priv->items->len; i++)
942                         if (priv->items->pdata[i] == item)
943                         {
944                                 found = TRUE;
945                                 break;
946                         }
947
948                 if (found)
949                 {
950                         iter.stamp = priv->stamp;
951                         iter.user_data = (gpointer) i;
952                         path = gtk_tree_path_new ();
953                         gtk_tree_path_append_index (path, i);
954                         gtk_tree_model_row_deleted ((GtkTreeModel *) stuff->self, path);
955                         priv->stamp++;
956                         g_mutex_lock (priv->ra_lock);
957                         priv->cur_len--;
958                         priv->registered--;
959                         g_mutex_unlock (priv->ra_lock);
960                         gtk_tree_path_free (path);
961
962                         mitem = g_ptr_array_remove_index (priv->items, i);
963                         if (mitem)
964                                 g_object_unref (mitem);
965                 }