1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * Pan - A Newsreader for Gtk+
4  * Copyright (C) 2002-2006  Charles Kerr <charles@rebelbase.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include <config.h>
21 #include <iostream>
22 #include <algorithm>
23 #include <cstdarg>
24 #include <set>
25 #include <gobject/gvaluecollector.h>
26 #include <pan/general/debug.h>
27 #include <pan/general/macros.h>
28 #include "pan-tree.h"
29 
30 #define IS_SORTED(tree) \
31   (tree->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
32 
33 /***
34 ****  Row Helper Functions
35 ***/
36 
37 void
set_value_int(GValue * setme,int value)38 PanTreeStore :: Row :: set_value_int (GValue * setme, int value)
39 {
40   g_value_init (setme, G_TYPE_INT);
41   g_value_set_int (setme, value);
42 }
43 
44 void
set_value_pointer(GValue * setme,gpointer value)45 PanTreeStore :: Row :: set_value_pointer (GValue * setme, gpointer value)
46 {
47   g_value_init (setme, G_TYPE_POINTER);
48   g_value_set_pointer (setme, value);
49 }
50 
51 void
set_value_ulong(GValue * setme,unsigned long value)52 PanTreeStore :: Row :: set_value_ulong (GValue * setme, unsigned long value)
53 {
54   g_value_init (setme, G_TYPE_ULONG);
55   g_value_set_ulong (setme, value);
56 }
57 
58 void
set_value_string(GValue * setme,const char * value)59 PanTreeStore :: Row :: set_value_string (GValue * setme, const char * value)
60 {
61   g_value_init (setme, G_TYPE_STRING);
62   g_value_set_string (setme, value);
63 }
64 
65 void
set_value_static_string(GValue * setme,const char * value)66 PanTreeStore :: Row :: set_value_static_string (GValue * setme, const char * value)
67 {
68   g_value_init (setme, G_TYPE_STRING);
69   g_value_set_static_string (setme, value);
70 }
71 
72 
73 /***
74 ****  Low-level utilities
75 ***/
76 
77 GtkTreeIter
get_iter(const Row * row)78 PanTreeStore :: get_iter (const Row * row)
79 {
80   g_assert (row);
81   GtkTreeIter setme;
82   set_iter (&setme, row);
83   return setme;
84 }
85 
86 void
get_iter(const Row * row,GtkTreeIter * setme)87 PanTreeStore :: get_iter (const Row * row, GtkTreeIter * setme)
88 {
89   g_assert (setme);
90   g_assert (row);
91   set_iter (setme, row);
92 }
93 
94 
95 PanTreeStore :: Row*
get_row(GtkTreeIter * iter)96 PanTreeStore :: get_row (GtkTreeIter * iter)
97 {
98   g_assert (iter);
99   g_assert (iter->stamp == stamp);
100   Row * row (static_cast<Row*>(iter->user_data));
101   g_assert (row);
102   return row;
103 }
104 
105 const PanTreeStore :: Row*
get_row(const GtkTreeIter * iter) const106 PanTreeStore :: get_row (const GtkTreeIter * iter) const
107 {
108   g_assert (iter);
109   g_assert (iter->stamp == stamp);
110   const Row * row (static_cast<const Row*>(iter->user_data));
111   g_assert (row);
112   return row;
113 }
114 
115 void
set_iter(GtkTreeIter * iter,const Row * row)116 PanTreeStore :: set_iter (GtkTreeIter * iter,
117                           const Row   * row)
118 {
119   iter->stamp = stamp;
120   iter->user_data = (gpointer)row;
121   iter->user_data2 = 0;
122   iter->user_data3 = 0;
123 }
124 
125 void
invalidate_iter(GtkTreeIter * iter)126 PanTreeStore :: invalidate_iter (GtkTreeIter * iter)
127 {
128   iter->stamp      = 0;
129   iter->user_data  = (void*) 0xDEADBEEF;
130   iter->user_data2 = (void*) 0xDEADBEEF;
131   iter->user_data2 = (void*) 0xDEADBEEF;
132 }
133 
134 bool
set_or_invalidate(GtkTreeIter * iter,const Row * row)135 PanTreeStore :: set_or_invalidate (GtkTreeIter * iter, const Row * row)
136 {
137   if (row)
138     set_iter (iter, row);
139   else
140     invalidate_iter (iter);
141   return row != 0;
142 }
143 
144 
145 /*****
146 ******
147 ******  implementing GtkTreeModel's interface
148 ******
149 *****/
150 
151 GtkTreeModelFlags
model_get_flags(GtkTreeModel *)152 PanTreeStore :: model_get_flags (GtkTreeModel *)
153 {
154   return GTK_TREE_MODEL_ITERS_PERSIST;
155 }
156 
157 gint
model_get_n_columns(GtkTreeModel * model)158 PanTreeStore :: model_get_n_columns (GtkTreeModel * model)
159 {
160   const PanTreeStore * store (PAN_TREE_STORE(model));
161   g_return_val_if_fail (store, 0);
162   return store->n_columns;
163 }
164 
165 GType
model_get_column_type(GtkTreeModel * tree,gint n)166 PanTreeStore :: model_get_column_type (GtkTreeModel * tree,
167                                        gint           n)
168 {
169   return (*((PanTreeStore*)(tree))->column_types)[n];
170 }
171 
172 gboolean
model_get_iter(GtkTreeModel * model,GtkTreeIter * setme,GtkTreePath * path)173 PanTreeStore :: model_get_iter (GtkTreeModel * model,
174                                 GtkTreeIter  * setme,
175                                 GtkTreePath  * path)
176 {
177   PanTreeStore * store = PAN_TREE_STORE(model);
178   g_return_val_if_fail (store, false);
179   g_return_val_if_fail (path, false);
180 
181   // make sure it's not an empty path.
182   const int depth (gtk_tree_path_get_depth (path));
183   if (depth < 1)
184     return false;
185 
186   // find the row that correpsonds to this path.
187   PanTreeStore::Row * row (store->root);
188   const int * indices = gtk_tree_path_get_indices (path);
189   for (int i=0; i<depth; ++i) {
190     row = row->nth_child (*indices++);
191     if (!row)
192       return false;
193   }
194 
195   // build an iter from that row.
196   store->set_iter (setme, row);
197   return true;
198 }
199 
200 GtkTreePath*
model_get_path(GtkTreeModel * model,GtkTreeIter * iter)201 PanTreeStore :: model_get_path (GtkTreeModel * model,
202                                 GtkTreeIter  * iter)
203 {
204   PanTreeStore * store (PAN_TREE_STORE(model));
205   g_return_val_if_fail (store, 0);
206   return store->get_path (iter);
207 }
208 
209 void
model_get_value(GtkTreeModel * model,GtkTreeIter * iter,gint column,GValue * dest_value)210 PanTreeStore :: model_get_value (GtkTreeModel * model,
211                                  GtkTreeIter  * iter,
212                                  gint           column,
213                                  GValue       * dest_value)
214 {
215   g_assert (iter);
216   g_assert (dest_value);
217   PanTreeStore * store = PAN_TREE_STORE(model);
218   g_assert (store);
219   g_assert (iter->stamp == store->stamp);
220   g_assert (0<=column && column<store->n_columns);
221 
222   store->get_row(iter)->get_value (column, dest_value);
223 }
224 
225 gboolean
model_iter_next(GtkTreeModel * model,GtkTreeIter * iter)226 PanTreeStore :: model_iter_next (GtkTreeModel * model,
227                                  GtkTreeIter  * iter)
228 {
229   PanTreeStore * tree = PAN_TREE_STORE(model);
230   g_return_val_if_fail (tree, false);
231   g_return_val_if_fail (iter, false);
232   g_return_val_if_fail (iter->stamp == tree->stamp, false);
233 
234   Row * row (tree->get_row (iter));
235   row = row->parent->nth_child (row->child_index + 1);
236 
237   return tree->set_or_invalidate (iter, row);
238 }
239 
240 gboolean
model_iter_children(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * parent)241 PanTreeStore :: model_iter_children (GtkTreeModel * model,
242                                      GtkTreeIter  * iter,
243                                      GtkTreeIter  * parent)
244 {
245   return model_iter_nth_child (model, iter, parent, 0);
246 }
247 
248 gint
model_iter_n_children(GtkTreeModel * model,GtkTreeIter * iter)249 PanTreeStore :: model_iter_n_children (GtkTreeModel * model,
250                                        GtkTreeIter  * iter)
251 {
252   PanTreeStore * tree = PAN_TREE_STORE(model);
253   g_return_val_if_fail (tree, 0);
254   g_return_val_if_fail (!iter || iter->stamp == tree->stamp, 0);
255 
256   const Row * row (iter ? tree->get_row(iter) : tree->root);
257   return row->n_children();
258 }
259 
260 gboolean
model_iter_has_child(GtkTreeModel * model,GtkTreeIter * iter)261 PanTreeStore :: model_iter_has_child (GtkTreeModel *model,
262                                       GtkTreeIter  *iter)
263 {
264   return model_iter_n_children (model, iter) != 0;
265 }
266 
267 gboolean
model_iter_nth_child(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)268 PanTreeStore :: model_iter_nth_child (GtkTreeModel * model,
269                                       GtkTreeIter  * iter,
270                                       GtkTreeIter  * parent,
271                                       gint           n)
272 {
273   PanTreeStore * tree = PAN_TREE_STORE(model);
274   g_return_val_if_fail (tree, false);
275   g_return_val_if_fail (iter, false);
276   g_return_val_if_fail (!parent || parent->stamp == tree->stamp, false);
277 
278   Row * row (parent ? tree->get_row(parent) : tree->root);
279   row = row->nth_child (n);
280 
281   return tree->set_or_invalidate (iter, row);
282 }
283 
284 gboolean
model_iter_parent(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * child)285 PanTreeStore :: model_iter_parent (GtkTreeModel * model,
286                                    GtkTreeIter  * iter,
287                                    GtkTreeIter  * child)
288 {
289   return PAN_TREE_STORE(model)->get_parent (iter, child);
290 }
291 
292 /*****
293 ******
294 *****/
295 
296 GtkTreePath*
get_path(const Row * row) const297 PanTreeStore :: get_path (const Row * row) const
298 {
299   g_return_val_if_fail (row, NULL);
300 
301   std::vector<int> indices;
302   while (row && row!=root) {
303     indices.push_back (row->child_index);
304     row = row->parent;
305   }
306 
307   GtkTreePath * path = gtk_tree_path_new ();
308   for (std::vector<int>::const_reverse_iterator it(indices.rbegin()), end(indices.rend()); it!=end; ++it)
309     gtk_tree_path_append_index (path, *it);
310 
311   return path;
312 }
313 
314 GtkTreePath*
get_path(GtkTreeIter * iter)315 PanTreeStore :: get_path (GtkTreeIter  * iter)
316 {
317   g_return_val_if_fail (iter, NULL);
318   g_return_val_if_fail (iter->stamp == stamp, NULL);
319 
320   return get_path (get_row (iter));
321 }
322 
323 bool
get_parent(GtkTreeIter * iter,GtkTreeIter * child)324 PanTreeStore :: get_parent (GtkTreeIter * iter,
325                             GtkTreeIter * child)
326 {
327   g_return_val_if_fail (child, false);
328   g_return_val_if_fail (iter, false);
329   g_return_val_if_fail (child->stamp == stamp, false);
330 
331   const Row * row (get_row (child));
332   return set_or_invalidate (iter, row->parent!=root ? row->parent : 0);
333 }
334 
335 bool
is_root(const GtkTreeIter * iter) const336 PanTreeStore :: is_root (const GtkTreeIter* iter) const
337 {
338   g_return_val_if_fail (iter, false);
339   g_return_val_if_fail (iter->stamp == stamp, false);
340   return get_row(iter)->parent == root;
341 }
342 
343 size_t
get_depth(const Row * row) const344 PanTreeStore :: get_depth (const Row * row) const
345 {
346   g_assert (row);
347   size_t depth (0);
348   for (;;) {
349     if (row == root) break;
350     ++depth;
351     row = row->parent;
352     g_assert (row);
353   }
354   return depth;
355 }
356 
357 bool
is_in_tree(Row * row) const358 PanTreeStore :: is_in_tree (Row * row) const
359 {
360   while (row && row!=root)
361     row = row->parent;
362   return row == root;
363 }
364 
365 /*****
366 ******
367 ******  implementing GtkTreeSortable
368 ******
369 *****/
370 
371 gboolean
sortable_get_sort_column_id(GtkTreeSortable * sortable,gint * setme_column_id,GtkSortType * setme_order)372 PanTreeStore :: sortable_get_sort_column_id (GtkTreeSortable  * sortable,
373                                              gint             * setme_column_id,
374                                              GtkSortType      * setme_order)
375 {
376   PanTreeStore * tree (PAN_TREE_STORE (sortable));
377   g_return_val_if_fail (tree, false);
378 
379   if (setme_column_id)
380      *setme_column_id = tree->sort_column_id;
381   if (setme_order)
382      *setme_order = tree->order;
383 
384   return (tree->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
385       && (tree->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID);
386 }
387 
388 gboolean
sortable_has_sort_func(GtkTreeSortable * sortable,gint col)389 PanTreeStore :: sortable_has_sort_func (GtkTreeSortable *sortable, gint col)
390 {
391   PanTreeStore * store (PAN_TREE_STORE (sortable));
392   g_return_val_if_fail (store, false);
393   column_sort_info_t::const_iterator it (store->sort_info->find (col));
394   return (it!=store->sort_info->end()) && (it->second.sort_func!=0);
395 }
396 
397 // SORTING
398 
399 struct
400 PanTreeStore :: SortRowInfo
401 {
402   int pos;
403   Row * row;
404   GtkTreeIter iter;
405 };
406 
407 struct PanTreeStore :: SortData
408 {
409   PanTreeStore * store;
410   GtkTreeModel * model;
411   PanTreeStore::SortInfo& sort_info;
412   GtkSortType order;
SortDataPanTreeStore::SortData413   SortData (PanTreeStore           * tree_store,
414             PanTreeStore::SortInfo & s,
415             GtkSortType              o):
416     store(tree_store), model(GTK_TREE_MODEL(tree_store)), sort_info(s), order(o) {}
417 };
418 
419 int
row_compare_func(gconstpointer a_gpointer,gconstpointer b_gpointer,gpointer user_data)420 PanTreeStore :: row_compare_func (gconstpointer a_gpointer,
421                                   gconstpointer b_gpointer,
422                                   gpointer      user_data)
423 {
424   const SortRowInfo * a = (const SortRowInfo*) a_gpointer;
425   const SortRowInfo * b = (const SortRowInfo*) b_gpointer;
426   const SortData * help = (const SortData*) user_data;
427   int val = (help->sort_info.sort_func) (help->model,
428                                          const_cast<GtkTreeIter*>(&a->iter),
429                                          const_cast<GtkTreeIter*>(&b->iter),
430                                          help->sort_info.user_data);
431   if (!val) // inplace sort
432     val = a->row->child_index - b->row->child_index;
433 
434   if (help->order == GTK_SORT_DESCENDING)
435     val = -val;
436 
437   return val;
438 }
439 
440 void
sort_children(SortInfo & sort_info,Row * parent,bool recurse,int mode)441 PanTreeStore :: sort_children (SortInfo  & sort_info,
442                                Row       * parent,
443                                bool        recurse,
444                                int         mode)
445 {
446   g_assert (parent);
447   g_assert (mode==FLIP || mode==SORT);
448 
449   const int n (parent->n_children());
450   if (n < 2) // no need to sort
451     return;
452 
453   // build a temporary array to sort
454   SortRowInfo * sort_array = new SortRowInfo [n];
455   for (int i=0; i<n; ++i) {
456     SortRowInfo& row_info (sort_array[mode==SORT ? i : n-1-i]);
457     row_info.pos = i;
458     row_info.row = parent->children[i];
459     set_iter (&row_info.iter, row_info.row);
460   }
461 
462   if (mode==SORT) {
463     SortData help (this, sort_info, order);
464     g_qsort_with_data (sort_array,
465 		       n, sizeof(SortRowInfo),
466 		       row_compare_func, &help);
467   }
468 
469   // update the child indices...
470   bool reordered (false);
471   for (int i=0; i<n; ++i) {
472     Row * child = sort_array[i].row;
473     reordered |= (child->child_index != i);
474     child->child_index = i;
475     parent->children[i] = child;
476     g_assert (child->parent == parent);
477   }
478 
479   // let the world know we've changed
480   if (reordered)
481   {
482     int * new_order (new int [n]);
483     for (int i=0; i<n; ++i)
484       new_order[i] = sort_array[i].pos;
485 
486     GtkTreeModel * model (GTK_TREE_MODEL(this));
487     if (parent == root)
488     {
489       GtkTreePath * path (gtk_tree_path_new ());
490       gtk_tree_model_rows_reordered (model, path, 0, new_order);
491       gtk_tree_path_free (path);
492     }
493     else
494     {
495       GtkTreeIter it (get_iter (parent));
496       GtkTreePath * path (get_path (parent));
497       gtk_tree_model_rows_reordered (model, path, &it, new_order);
498       gtk_tree_path_free (path);
499     }
500 
501     delete [] new_order;
502   }
503 
504   delete [] sort_array;
505 
506   for (int i=0; recurse && i<n; ++i)
507     sort_children (sort_info, parent->children[i], recurse, mode);
508 }
509 
510 void
sort(int mode)511 PanTreeStore :: sort (int mode)
512 {
513   if (!sort_paused && (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID))
514   {
515     g_assert (sortable_has_sort_func (GTK_TREE_SORTABLE(this), sort_column_id));
516     sort_children ((*sort_info)[sort_column_id], root, true, mode);
517   }
518 }
519 
520 void
sortable_set_sort_column_id(GtkTreeSortable * sortable,gint sort_column_id,GtkSortType order)521 PanTreeStore :: sortable_set_sort_column_id (GtkTreeSortable * sortable,
522                                              gint              sort_column_id,
523                                              GtkSortType       order)
524 {
525   PanTreeStore * tree (PAN_TREE_STORE (sortable));
526   g_return_if_fail (tree);
527 
528   // if no change, there's nothing to do...
529   if (tree->sort_column_id==sort_column_id && tree->order==order)
530     return;
531 
532   // sanity checks...
533   if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) {
534     g_return_if_fail (tree->sort_info->count(sort_column_id) != 0);
535     g_return_if_fail (tree->sort_info->find(sort_column_id)->second.sort_func != 0);
536   }
537 
538   const bool flip (sort_column_id == tree->sort_column_id);
539   tree->sort_paused = 0;
540   tree->sort_column_id = sort_column_id;
541   tree->order = order;
542   gtk_tree_sortable_sort_column_changed (sortable);
543   tree->sort (flip ? FLIP : SORT);
544 }
545 
546 gboolean
sortable_has_default_sort_func(GtkTreeSortable * sortable)547 PanTreeStore :: sortable_has_default_sort_func (GtkTreeSortable *sortable)
548 {
549   return sortable_has_sort_func (sortable, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID);
550 }
551 
552 void
sortable_set_sort_func(GtkTreeSortable * sortable,gint col,GtkTreeIterCompareFunc func,gpointer data,GDestroyNotify destroy)553 PanTreeStore :: sortable_set_sort_func (GtkTreeSortable        *sortable,
554                                         gint                    col,
555                                         GtkTreeIterCompareFunc  func,
556                                         gpointer                data,
557                                         GDestroyNotify          destroy)
558 {
559   PanTreeStore * tree (PAN_TREE_STORE (sortable));
560   g_return_if_fail (tree);
561 
562   (*tree->sort_info)[col].assign (func, data, destroy);
563 
564   if (tree->sort_column_id == col)
565     tree->sort ();
566 }
567 
568 void
sortable_set_default_sort_func(GtkTreeSortable * s,GtkTreeIterCompareFunc f,gpointer p,GDestroyNotify d)569 PanTreeStore :: sortable_set_default_sort_func (GtkTreeSortable        * s,
570                                                 GtkTreeIterCompareFunc   f,
571                                                 gpointer                 p,
572                                                 GDestroyNotify           d)
573 {
574   sortable_set_sort_func (s, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, f, p, d);
575 }
576 
577 namespace
578 {
579   GObjectClass *parent_class (0);
580 
581   typedef std::vector<GValue> values_t;
582 }
583 
584 struct
585 PanTreeStore :: FreeRowWalker: public PanTreeStore::WalkFunctor
586 {
587   PanTreeStore * store;
FreeRowWalkerPanTreeStore::FreeRowWalker588   FreeRowWalker (PanTreeStore *t): store(t) {}
~FreeRowWalkerPanTreeStore::FreeRowWalker589   virtual ~FreeRowWalker () {}
operator ()PanTreeStore::FreeRowWalker590   virtual bool operator()(PanTreeStore *s, Row *r, GtkTreeIter*, GtkTreePath*) {
591     if (store->row_dispose)
592         store->row_dispose->row_dispose (s, r);
593     else
594         delete r;
595     return true; // keep marching
596   }
597 };
598 
599 void
pan_tree_dispose(GObject * object)600 PanTreeStore :: pan_tree_dispose (GObject *object)
601 {
602   PanTreeStore * store (PAN_TREE_STORE (object));
603 
604   // erase the remaining nodes
605   FreeRowWalker walk (store);
606   store->postfix_walk (walk);
607 
608   // clear the sort info
609   store->sort_info->clear ();
610 }
611 
612 void
pan_tree_finalize(GObject * object)613 PanTreeStore :: pan_tree_finalize (GObject *object)
614 {
615   PanTreeStore * store (PAN_TREE_STORE (object));
616 
617   delete store->root;
618   delete store->column_types;
619   delete store->sort_info;
620   store->root = 0;
621   store->column_types = 0;
622   store->sort_info = 0;
623 
624   (*parent_class->finalize) (object);
625 }
626 
627 void
pan_tree_class_init(PanTreeStoreClass * klass)628 PanTreeStore :: pan_tree_class_init (PanTreeStoreClass *klass)
629 {
630   GObjectClass *object_class;
631 
632   parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
633   object_class = (GObjectClass*) klass;
634 
635   object_class->dispose = pan_tree_dispose;
636   object_class->finalize = pan_tree_finalize;
637 }
638 
639 void
pan_tree_model_init(GtkTreeModelIface * iface)640 PanTreeStore :: pan_tree_model_init (GtkTreeModelIface *iface)
641 {
642   iface->get_flags       = model_get_flags;
643   iface->get_n_columns   = model_get_n_columns;
644   iface->get_column_type = model_get_column_type;
645   iface->get_iter        = model_get_iter;
646   iface->get_path        = model_get_path;
647   iface->get_value       = model_get_value;
648   iface->iter_next       = model_iter_next;
649   iface->iter_children   = model_iter_children;
650   iface->iter_has_child  = model_iter_has_child;
651   iface->iter_n_children = model_iter_n_children;
652   iface->iter_nth_child  = model_iter_nth_child;
653   iface->iter_parent     = model_iter_parent;
654 }
655 
656 void
pan_tree_sortable_init(GtkTreeSortableIface * iface)657 PanTreeStore :: pan_tree_sortable_init (GtkTreeSortableIface *iface)
658 {
659   iface->get_sort_column_id    = sortable_get_sort_column_id;
660   iface->set_sort_column_id    = sortable_set_sort_column_id;
661   iface->set_sort_func         = sortable_set_sort_func;
662   iface->set_default_sort_func = sortable_set_default_sort_func;
663   iface->has_default_sort_func = sortable_has_default_sort_func;
664 }
665 
666 namespace
667 {
668   class RootRow: public PanTreeStore::Row {
get_value(int,GValue *)669     virtual void get_value (int, GValue *) { /* unused */ }
670   };
671 }
672 
673 void
pan_tree_init(PanTreeStore * tree)674 PanTreeStore :: pan_tree_init (PanTreeStore * tree)
675 {
676   tree->stamp = g_random_int();
677   tree->n_columns = 0;
678   tree->root = new RootRow ();
679   tree->row_dispose = 0;
680   tree->column_types = new std::vector<GType>();
681   tree->sort_paused = 0;
682   tree->sort_info = new column_sort_info_t;
683   tree->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
684   tree->order = GTK_SORT_ASCENDING;
685 }
686 
687 /***
688 ****
689 ***/
690 
691 GType
get_type()692 PanTreeStore :: get_type ()
693 {
694   static GType pan_tree_type (0);
695   if (pan_tree_type)
696     return pan_tree_type;
697 
698   static const GTypeInfo pan_tree_info = {
699     sizeof (PanTreeStoreClass),
700     NULL, // base_init
701     NULL, // base_finalize
702     (GClassInitFunc) pan_tree_class_init,
703     NULL, // class finalize
704     NULL, // class_data
705     sizeof (PanTreeStore),
706     0, // n_preallocs
707     (GInstanceInitFunc) pan_tree_init,
708     0 // value_table
709   };
710 
711   pan_tree_type = g_type_register_static (
712     G_TYPE_OBJECT, "PanTreeStore", &pan_tree_info, (GTypeFlags)0);
713   static const GInterfaceInfo tree_model_info =
714     { (GInterfaceInitFunc)pan_tree_model_init, 0, 0 };
715   g_type_add_interface_static (
716     pan_tree_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
717   static const GInterfaceInfo sortable_info =
718     { (GInterfaceInitFunc)pan_tree_sortable_init, 0, 0 };
719   g_type_add_interface_static (
720     pan_tree_type, GTK_TYPE_TREE_SORTABLE, &sortable_info);
721   return pan_tree_type;
722 }
723 
724 PanTreeStore*
new_tree(int n_columns,...)725 PanTreeStore :: new_tree (int n_columns, ...)
726 {
727   g_return_val_if_fail (n_columns>0, 0);
728   PanTreeStore* tree = (PanTreeStore*) g_object_new (PAN_TREE_STORE_TYPE, NULL);
729 
730   va_list args;
731   va_start (args, n_columns);
732   for (int i=0; i<n_columns; ++i) {
733     const GType type (va_arg (args, GType));
734     tree->column_types->push_back (type);
735   }
736   va_end (args);
737   tree->n_columns = tree->column_types->size();
738 
739   return tree;
740 }
741 
742 /***
743 ****
744 ***/
745 
746 void
row_changed(Row * row)747 PanTreeStore :: row_changed (Row * row)
748 {
749   GtkTreeIter iter;
750   get_iter (row, &iter);
751   row_changed (&iter);
752 }
753 
754 void
row_changed(GtkTreeIter * iter)755 PanTreeStore :: row_changed (GtkTreeIter * iter)
756 {
757   GtkTreeModel * model (GTK_TREE_MODEL(this));
758   GtkTreePath * path (gtk_tree_model_get_path (model, iter));
759   gtk_tree_model_row_changed (model, path, iter);
760   gtk_tree_path_free (path);
761 }
762 
763 /***
764 ****
765 ***/
766 
767 void
renumber_children(Row * parent,int child_lo,int child_hi)768 PanTreeStore :: renumber_children (Row * parent,
769                                    int   child_lo,
770                                    int   child_hi)
771 {
772   const int n_children (parent->n_children());
773   child_hi = std::min (child_hi, n_children);
774 
775   g_assert (parent);
776   g_assert (child_lo >= 0);
777   g_assert (child_lo <= child_hi);
778   g_assert (child_hi <= n_children);
779 
780   Row ** it = &parent->children[child_lo];
781   for (int i=child_lo; i!=child_hi; ++i, ++it) {
782     (*it)->child_index = i;
783     (*it)->parent = parent;
784   }
785 }
786 
787 struct PanTreeStore :: RowCompareByDepth
788 {
789   PanTreeStore * store;
RowCompareByDepthPanTreeStore::RowCompareByDepth790   RowCompareByDepth (PanTreeStore *s): store(s) {}
operator ()PanTreeStore::RowCompareByDepth791   bool operator() (const Row* a, const Row* b) const {
792     return store->get_depth(a) < store->get_depth(b);
793   }
794 };
795 
796 void
insert_sorted(const parent_to_children_t & new_parents)797 PanTreeStore :: insert_sorted (const parent_to_children_t& new_parents)
798 {
799   g_return_if_fail (!new_parents.empty());
800 
801   RowCompareByDepth depth_compare (this);
802 
803   // this is a pool of all the parents that need kids added...
804   std::set<Row*> pool;
805   foreach_const (parent_to_children_t, new_parents, it)
806     pool.insert (it->first ? it->first : root);
807 
808   while (!pool.empty())
809   {
810     // get a subset of pool: parents that are are in the tree
811     rows_t keys;
812     foreach_const (std::set<Row*>, pool, it)
813       if (is_in_tree (*it))
814         keys.push_back (*it);
815     g_assert (!keys.empty());
816 
817     // sort these from shallowest to deepest
818     std::sort (keys.begin(), keys.end(), depth_compare);
819 
820     // process each of them
821     foreach_const (rows_t, keys, it)
822     {
823       // add the children to this parent...
824       // caller passes in NULL for the parent of top-level articles...
825       Row * parent (*it);
826       Row * key (parent==root ? 0 : parent);
827       const rows_t& children (new_parents.find(key)->second);
828       insert_sorted (parent, children);
829       pool.erase (parent);
830     }
831   }
832 }
833 
834 struct PanTreeStore :: RowCompareByColumn
835 {
836   PanTreeStore * store;
837   GtkTreeModel * model;
838   GtkTreeIterCompareFunc sort_func;
839   gpointer user_data;
840   bool reverse;
841 
RowCompareByColumnPanTreeStore::RowCompareByColumn842   RowCompareByColumn (PanTreeStore * s):
843     store (s),
844     model (GTK_TREE_MODEL(s))
845   {
846     const SortInfo& info ((*(store->sort_info))[store->sort_column_id]);
847     sort_func = info.sort_func;
848     user_data = info.user_data;
849     reverse = (store->order == GTK_SORT_DESCENDING);
850   }
851 
operator ()PanTreeStore::RowCompareByColumn852   bool operator() (const Row* a, const Row* b) const
853   {
854     GtkTreeIter a_it (store->get_iter (a));
855     GtkTreeIter b_it (store->get_iter (b));
856     int val = sort_func ? (sort_func)(model, &a_it, &b_it, user_data) : (a<b);
857     if (reverse) val = -val;
858     return val < 0;
859   }
860 };
861 
862 /***
863 ****
864 ***/
865 
866 struct PanTreeStore::ReparentWalker: public PanTreeStore::WalkFunctor
867 {
868   GtkTreeModel * model;
869 
ReparentWalkerPanTreeStore::ReparentWalker870   ReparentWalker (PanTreeStore *store): model(GTK_TREE_MODEL(store)) {}
871 
~ReparentWalkerPanTreeStore::ReparentWalker872   virtual ~ReparentWalker () {}
873 
operator ()PanTreeStore::ReparentWalker874   virtual bool operator()(PanTreeStore *store, Row* row,
875                           GtkTreeIter *iter, GtkTreePath *path)
876   {
877     const int n_children (row->n_children());
878 
879     if (n_children)
880     {
881       // fire a "row inserted" for each child
882       GtkTreePath * cpath (gtk_tree_path_copy (path));
883       gtk_tree_path_down (cpath);
884       for (int i=0; i<n_children; ++i) {
885         Row * child (row->nth_child (i));
886         GtkTreeIter citer;
887         store->set_iter (&citer, child);
888         gtk_tree_model_row_inserted (model, cpath, &citer);
889         if (!i)
890           gtk_tree_model_row_has_child_toggled (model, path, iter);
891         gtk_tree_path_next (cpath);
892       }
893       gtk_tree_path_free (cpath);
894     }
895 
896     return true; // keep marching
897   }
898 };
899 
900 void
insert_sorted(Row * parent,const rows_t & children_in)901 PanTreeStore :: insert_sorted (Row           * parent,
902                                const rows_t  & children_in)
903 {
904   g_return_if_fail (!children_in.empty());
905 
906   if (!parent)
907        parent = root;
908 
909 //std::cerr << LINE_ID << " adding " << children_in.size() << " sorted children to " << parent << " which currently has " << parent->children.size() << " children" << std::endl;
910 
911   // sort new children...
912   rows_t children (children_in);
913   foreach (rows_t, children, it)
914     (*it)->parent = parent;
915   RowCompareByColumn compare_column (this);
916   std::sort (children.begin(), children.end(), compare_column);
917 
918 //for (int i=0, n=children.size()-1; i<n; ++i)
919  // g_assert (!compare_column (children[i+1], children[i]));
920 //for (int i=0, n=parent->children.size()-1; i<n; ++i)
921  // g_assert (!compare_column (parent->children[i+1], parent->children[i]));
922 
923 
924 
925   // merge the two sorted containers together...
926   const size_t old_size (parent->children.size());
927   rows_t tmp;
928   tmp.reserve (old_size + children.size());
929   const size_t n_new (children.size());
930   std::vector<int> indices;
931   indices.reserve (n_new);
932   rows_t::const_iterator o_it  (parent->children.begin()),
933                          o_end (parent->children.end()),
934                          n_it  (children.begin()),
935                          n_end (children.end());
936   for (size_t i=0; o_it!=o_end || n_it!=n_end; ++i) {
937     Row * addme (0);
938     if ((n_it==n_end)) {
939       //std::cerr << LINE_ID << " n is empty ... adding another o at " << tmp.size() << std::endl;
940       addme = *o_it++;
941     }
942     else if (o_it==o_end) {
943       //std::cerr << LINE_ID << " o is empty .. adding NEW at " << tmp.size() << std::endl;
944       indices.push_back (i);
945       addme = *n_it++;
946     }
947     else if (compare_column (*n_it, *o_it)) {
948       //std::cerr << LINE_ID << " adding NEW at index " << tmp.size() << std::endl;
949       indices.push_back (i);
950       addme = *n_it++;
951     }
952     else {
953       //std::cerr << LINE_ID << " adding old at index " << tmp.size() << std::endl;
954       addme = *o_it++;
955     }
956   //  g_assert (tmp.empty() || !compare_column(addme,tmp.back()));
957     addme->child_index = i;
958    // g_assert (tmp.empty() || !compare_column(addme,tmp.back()));
959     tmp.push_back (addme);
960   }
961   parent->children.swap (tmp);
962 
963   // check our work...
964   //g_assert (parent->children.size() == old_size + children.size());
965   //g_assert (indices.size() == children.size());
966   //for (int i=0, n=parent->children.size(); i<n; ++i)
967    // g_assert (i==0 || !compare_column(parent->children[i], parent->children[i-1]));
968 
969   // emit the 'row inserted' signals
970   GtkTreePath * path (get_path (parent));
971   GtkTreeIter iter;
972   GtkTreeModel * model (GTK_TREE_MODEL (this));
973   foreach_const (std::vector<int>, indices, it)
974   {
975     Row * child (parent->nth_child (*it));
976     get_iter (child, &iter);
977     gtk_tree_path_append_index (path, *it);
978     gtk_tree_model_row_inserted (model, path, &iter);
979     gtk_tree_path_up (path);
980 
981     // if row has children, this handles all of their
982     // row-inserted and has-child-toggled signals...
983     // if the row doesn't have children, this has
984     // no effect...
985     if (!child->children.empty()) {
986       ReparentWalker walk (this);
987       prefix_walk (walk, &iter, true);
988     }
989   }
990 
991   // maybe emit the 'row has child toggled' signal
992   if (!old_size && parent!=root) {
993     GtkTreeIter it (get_iter (parent));
994     gtk_tree_model_row_has_child_toggled (model, path, &it);
995   }
996 
997   // cleanup
998   gtk_tree_path_free (path);
999 
1000   //for (int i=0, n=parent->children.size(); i<n; ++i)
1001    // g_assert (i==0 || !compare_column(parent->children[i], parent->children[i-1]));
1002 }
1003 
1004 void
insert(Row * parent_row,const rows_t & new_children,int position)1005 PanTreeStore :: insert (Row            * parent_row,
1006                         const rows_t   & new_children,
1007                         int              position)
1008 {
1009   g_return_if_fail (!new_children.empty());
1010   g_return_if_fail (position >= 0);
1011 
1012   if (!parent_row)
1013        parent_row = root;
1014 
1015   // insert the rows
1016   const size_t n_rows (new_children.size());
1017   const int old_size (parent_row->n_children());
1018   position = std::min (position, old_size);
1019   parent_row->children.insert (parent_row->children.begin()+position,
1020                                new_children.begin(),
1021                                new_children.end());
1022   renumber_children (parent_row, position);
1023 
1024   // set the return iter
1025   GtkTreeIter iter (get_iter (parent_row->children[position]));
1026 
1027   // emit the 'row inserted' signals
1028   GtkTreeModel * model (GTK_TREE_MODEL(this));
1029   Row ** it (&parent_row->children[position]);
1030   GtkTreePath * path (get_path (*it));
1031   for (size_t i=0; i<n_rows; ++i)
1032   {
1033     Row * child (*it++);
1034     set_iter (&iter, child);
1035     gtk_tree_model_row_inserted (model, path, &iter);
1036     gtk_tree_path_next (path);
1037 
1038     // if row has children, this handles all of their
1039     // row-inserted and has-child-toggled signals...
1040     // if the row doesn't have children, this has
1041     // no effect...
1042     if (!child->children.empty()) {
1043       ReparentWalker walk (this);
1044       prefix_walk (walk, &iter, true);
1045     }
1046   }
1047 
1048   // maybe emit the 'row has child toggled' signal
1049   if (!old_size && parent_row!=root) {
1050     GtkTreeIter parent_it (get_iter (parent_row));
1051     gtk_tree_path_up (path);
1052     gtk_tree_model_row_has_child_toggled (model, path, &parent_it);
1053   }
1054 
1055   // cleanup
1056   gtk_tree_path_free (path);
1057 }
1058 
1059 void
reparent(Row * new_parent,Row * row,int position)1060 PanTreeStore :: reparent (Row  * new_parent,
1061                           Row  * row,
1062                           int    position)
1063 {
1064   g_return_if_fail (row != 0);
1065 
1066   GtkTreeModel * model (GTK_TREE_MODEL(this));
1067 
1068   if (!new_parent)
1069     new_parent = root;
1070 
1071   // remove our subtree's toplevel from its old parent
1072   rows_t tmp;
1073   tmp.push_back (row);
1074   remove_siblings (tmp, false);
1075 
1076   // add the subtree's toplevel to its new parent row
1077   const int new_parent_old_n_children (new_parent->n_children());
1078   position = std::min (position, new_parent_old_n_children);
1079   new_parent->children.insert (new_parent->children.begin()+position, row);
1080   renumber_children (new_parent, position);
1081 
1082   // emit a row-inserted for iter
1083   GtkTreeIter iter (get_iter (row));
1084   GtkTreePath * path (get_path (row));
1085   gtk_tree_model_row_inserted (model, path, &iter);
1086   gtk_tree_path_free (path);
1087 
1088   // this emits all the row-inserted and has-child-toggled signals EXCEPT
1089   // for iter's row-inserted (above) and parent-iter's has-child-toggled (below).
1090   // It's kind of kludgy but gets all the signals emitted in the right sequence.
1091   ReparentWalker walk (this);
1092   prefix_walk (walk, &iter, true);
1093 
1094   // if this was the new parent's first child, fire a has-child-toggled event
1095   if (!new_parent_old_n_children) {
1096     GtkTreePath * path (get_path (new_parent));
1097     GtkTreeIter new_parent_iter;
1098     get_iter (new_parent, &new_parent_iter);
1099     gtk_tree_model_row_has_child_toggled (model, path, &new_parent_iter);
1100     gtk_tree_path_free (path);
1101   }
1102 }
1103 
1104 void
reparent(const parent_to_children_t & new_parents)1105 PanTreeStore :: reparent (const parent_to_children_t& new_parents)
1106 {
1107   const RowCompareByDepth depth_compare (this);
1108 
1109   // REMOVE children from their OLD parents
1110 
1111   rows_t remove_me;
1112   foreach_const (parent_to_children_t, new_parents, it)
1113     remove_me.insert (remove_me.end(), it->second.begin(), it->second.end());
1114   remove (remove_me, false);
1115 
1116   // ADD children to their NEW parent
1117 
1118   insert_sorted (new_parents);
1119 }
1120 
1121 /***
1122 ****
1123 ***/
1124 
1125 void
remove(const rows_t & rows,bool delete_rows)1126 PanTreeStore :: remove (const rows_t& rows, bool delete_rows)
1127 {
1128   parent_to_children_t parent_to_kids;
1129   foreach_const (rows_t, rows, it) {
1130     g_assert (*it);
1131     g_assert ((*it)->parent);
1132     parent_to_kids[(*it)->parent].push_back (*it);
1133   }
1134 
1135   // remove them in same-parent batches, starting at the
1136   // deepest parents and working up to the shallower ones
1137   rows_t keys;
1138   foreach_const (parent_to_children_t, parent_to_kids, it)
1139     keys.push_back (it->first);
1140   const RowCompareByDepth compare_depth (this);
1141   std::sort (keys.begin(), keys.end(), compare_depth);
1142   foreach_const_r (rows_t, keys, it)
1143     remove_siblings (parent_to_kids[*it], delete_rows);
1144 }
1145 
1146 struct PanTreeStore::ClearWalker: public PanTreeStore::WalkFunctor
1147 {
~ClearWalkerPanTreeStore::ClearWalker1148   virtual ~ClearWalker () {}
1149 
clear_childrenPanTreeStore::ClearWalker1150   void clear_children (PanTreeStore* store, Row * row)
1151   {
1152     store->remove_siblings (row->children, true);
1153   }
1154 
operator ()PanTreeStore::ClearWalker1155   virtual bool operator()(PanTreeStore* store,
1156                           PanTreeStore::Row*,
1157                           GtkTreeIter* iter, GtkTreePath*)
1158   {
1159     clear_children (store, store->get_row(iter));
1160     return true;
1161   }
1162 };
1163 
1164 void
clear()1165 PanTreeStore :: clear ()
1166 {
1167   ClearWalker walker;
1168   postfix_walk (walker);
1169   walker.clear_children (this, root);
1170 }
1171 
1172 
1173 struct PanTreeStore :: RowCompareByChildPos {
operator ()PanTreeStore::RowCompareByChildPos1174   bool operator() (const Row* a, const Row* b) const {
1175     return a->child_index < b->child_index;
1176   }
1177 };
1178 
1179 void
remove_siblings(const rows_t & siblings,bool delete_rows)1180 PanTreeStore :: remove_siblings (const rows_t& siblings,
1181                                  bool          delete_rows)
1182 {
1183   // entry assertions
1184   g_assert (!siblings.empty());
1185   Row * parent (siblings[0]->parent);
1186   g_assert (parent);
1187   foreach_const (rows_t, siblings, it)
1188     g_assert ((*it)->parent == parent); // all are siblings
1189 
1190   // unthread the doomed rows
1191   std::set<int> removed_indices;
1192   GtkTreeModel * model (GTK_TREE_MODEL(this));
1193   GtkTreePath * path (get_path (parent));
1194   foreach_const (rows_t, siblings, nit) {
1195     Row * row (*nit);
1196     removed_indices.insert (row->child_index);
1197     row->parent = 0;
1198     row->child_index = -1;
1199   }
1200 
1201   // remove the dead rows; re-index the live ones
1202   int pos (0);
1203   rows_t keepers, purged;
1204   keepers.reserve (parent->n_children());
1205   foreach_const (rows_t, parent->children, it) {
1206     Row * row (*it);
1207     if (!row->parent)
1208       purged.push_back (row);
1209     else {
1210       row->child_index = pos++;
1211       keepers.push_back (row);
1212     }
1213   }
1214   parent->children.swap (keepers);
1215 
1216   // fire removal signal for child
1217   foreach_const_r (std::set<int>, removed_indices, it) {
1218     gtk_tree_path_append_index (path, *it);
1219     gtk_tree_model_row_deleted (model, path);
1220     gtk_tree_path_up (path);
1221   }
1222 
1223   // maybe fire has-children signal for parent
1224   if (parent!=root && !parent->n_children()) {
1225     GtkTreeIter pit;
1226     set_iter (&pit, parent);
1227     gtk_tree_model_row_has_child_toggled (model, path, &pit);
1228   }
1229 
1230   // clean up purged rows
1231   if (delete_rows) {
1232     foreach (rows_t, purged, it) {
1233       Row * row (*it);
1234       g_assert (row->child_index == -1);
1235       FreeRowWalker walk (this);
1236       GtkTreeIter iter (get_iter (row));
1237       postfix_walk (walk, &iter);
1238     }
1239   }
1240 
1241   gtk_tree_path_free (path);
1242 }
1243 
1244 /***
1245 ****
1246 ***/
1247 
1248 bool
walk_helper(int walk_mode,Row * row,GtkTreePath * path,WalkFunctor & walker)1249 PanTreeStore :: walk_helper (int           walk_mode,
1250                              Row         * row,
1251                              GtkTreePath * path,
1252                              WalkFunctor & walker)
1253 {
1254   g_assert (row);
1255   g_assert (walk_mode==WALK_PREFIX || walk_mode==WALK_POSTFIX);
1256 
1257   bool more (true);
1258 
1259   GtkTreeIter iter;
1260   set_iter (&iter, row);
1261 
1262   if (more && row!=root && walk_mode==WALK_PREFIX)
1263     more = walker (this, row, &iter, path);
1264 
1265   const size_t n_children (row->n_children());
1266   if (more && n_children) {
1267     if (path)
1268       gtk_tree_path_append_index (path, 0);
1269     for (Row ** it(&row->children.front()),
1270               ** end(it+n_children); more && it!=end; ++it) {
1271       more = walk_helper (walk_mode, *it, path, walker);
1272       if (path)
1273         gtk_tree_path_next (path);
1274     }
1275     if (path)
1276       gtk_tree_path_up (path);
1277   }
1278 
1279   if (more && row!=root && walk_mode==WALK_POSTFIX)
1280     more = walker (this, row, &iter, path);
1281 
1282   return more;
1283 }
1284 
1285 void
walk(int walk_mode,WalkFunctor & walker,GtkTreeIter * top,bool need_path)1286 PanTreeStore :: walk (int             walk_mode,
1287                       WalkFunctor   & walker,
1288                       GtkTreeIter   * top,
1289                       bool            need_path)
1290 {
1291   GtkTreePath * path (0);
1292   if (need_path)
1293     path = top ? get_path(top) : gtk_tree_path_new();
1294 
1295   Row * row (top ? get_row(top) : root);
1296   walk_helper (walk_mode, row, path, walker);
1297   gtk_tree_path_free (path);
1298 }
1299 
1300 void
prefix_walk(WalkFunctor & walker,GtkTreeIter * top,bool need_path)1301 PanTreeStore :: prefix_walk (WalkFunctor   & walker,
1302                              GtkTreeIter   * top,
1303                              bool            need_path)
1304 {
1305   walk (WALK_PREFIX, walker, top, need_path);
1306 }
1307 
1308 void
postfix_walk(WalkFunctor & walker,GtkTreeIter * top,bool need_path)1309 PanTreeStore :: postfix_walk (WalkFunctor   & walker,
1310                               GtkTreeIter   * top,
1311                               bool            need_path)
1312 {
1313   walk (WALK_POSTFIX, walker, top, need_path);
1314 }
1315 
1316 /***
1317 ****
1318 ***/
1319 
1320 PanTreeStore :: Row*
get_prev(Row * row)1321 PanTreeStore :: get_prev (Row * row)
1322 {
1323   if (!row || row==root)
1324     return 0;
1325   if (row->child_index==0)
1326     return row->parent==root ? 0 : row->parent;
1327   Row * sibling = row->parent->nth_child (row->child_index-1);
1328   return sibling->get_last_descendant ();
1329 }
1330 
1331 PanTreeStore :: Row*
get_next(Row * row)1332 PanTreeStore :: get_next (Row * row)
1333 {
1334   // child
1335   if (!row->children.empty())
1336     return row->nth_child (0);
1337 
1338   // sibling
1339   while (row && row!=root) {
1340     Row * sibling = row->parent->nth_child (row->child_index + 1);
1341     if (sibling)
1342       return sibling;
1343     row = row->parent;
1344   }
1345 
1346   // if all else fails, just return the first node
1347   return root->nth_child(0);
1348 }
1349 
1350 bool
get_prev(GtkTreeIter * iter)1351 PanTreeStore :: get_prev (GtkTreeIter * iter)
1352 {
1353   return set_or_invalidate (iter, get_prev(get_row(iter)));
1354 }
1355 
1356 bool
get_next(GtkTreeIter * iter)1357 PanTreeStore :: get_next (GtkTreeIter * iter)
1358 {
1359   return set_or_invalidate (iter, get_next(get_row(iter)));
1360 }
1361 
1362 bool
back(GtkTreeIter * iter)1363 PanTreeStore :: back (GtkTreeIter * iter)
1364 {
1365   return set_or_invalidate (iter, root->get_last_descendant());
1366 }
1367 
1368 bool
front(GtkTreeIter * iter)1369 PanTreeStore :: front (GtkTreeIter * iter)
1370 {
1371   return set_or_invalidate (iter, get_next(root));
1372 }
1373