1 /*
2  * Copyright (c) 2014 Red Hat, Inc.
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 this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "treewalk.h"
19 
20 struct _GtkTreeWalk
21 {
22   GtkTreeModel *model;
23   GtkTreeIter position;
24   gboolean visited;
25   RowPredicate predicate;
26   gpointer data;
27   GDestroyNotify destroy;
28 };
29 
30 GtkTreeWalk *
gtk_tree_walk_new(GtkTreeModel * model,RowPredicate predicate,gpointer data,GDestroyNotify destroy)31 gtk_tree_walk_new (GtkTreeModel   *model,
32                    RowPredicate    predicate,
33                    gpointer        data,
34                    GDestroyNotify  destroy)
35 {
36   GtkTreeWalk *walk;
37 
38   walk = g_new (GtkTreeWalk, 1);
39   walk->model = g_object_ref (model);
40   walk->visited = FALSE;
41   walk->predicate = predicate;
42   walk->data = data;
43   walk->destroy = destroy;
44 
45   return walk;
46 }
47 
48 void
gtk_tree_walk_free(GtkTreeWalk * walk)49 gtk_tree_walk_free (GtkTreeWalk *walk)
50 {
51   g_object_unref (walk->model);
52 
53   if (walk->destroy)
54     walk->destroy (walk->data);
55 
56   g_free (walk);
57 }
58 
59 void
gtk_tree_walk_reset(GtkTreeWalk * walk,GtkTreeIter * iter)60 gtk_tree_walk_reset (GtkTreeWalk *walk,
61                      GtkTreeIter *iter)
62 {
63   if (iter)
64     {
65       walk->position = *iter;
66       walk->visited = TRUE;
67     }
68   else
69     {
70       walk->visited = FALSE;
71     }
72 }
73 
74 static gboolean
gtk_tree_walk_step_forward(GtkTreeWalk * walk)75 gtk_tree_walk_step_forward (GtkTreeWalk *walk)
76 {
77   GtkTreeIter next, up;
78 
79   if (!walk->visited)
80     {
81       if (!gtk_tree_model_get_iter_first (walk->model, &walk->position))
82         return FALSE;
83 
84       walk->visited = TRUE;
85       return TRUE;
86     }
87 
88   if (gtk_tree_model_iter_children (walk->model, &next, &walk->position))
89     {
90       walk->position = next;
91       return TRUE;
92     }
93 
94   next = walk->position;
95   do
96     {
97       up = next;
98       if (gtk_tree_model_iter_next (walk->model, &next))
99         {
100           walk->position = next;
101           return TRUE;
102         }
103     }
104   while (gtk_tree_model_iter_parent (walk->model, &next, &up));
105 
106   return FALSE;
107 }
108 
109 static gboolean
gtk_tree_model_iter_last_child(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * parent)110 gtk_tree_model_iter_last_child (GtkTreeModel *model,
111                                 GtkTreeIter  *iter,
112                                 GtkTreeIter  *parent)
113 {
114   GtkTreeIter next;
115 
116   if (!gtk_tree_model_iter_children (model, &next, parent))
117     return FALSE;
118 
119   do
120     *iter = next;
121   while (gtk_tree_model_iter_next (model, &next));
122 
123   return TRUE;
124 }
125 
126 static gboolean
gtk_tree_model_get_iter_last(GtkTreeModel * model,GtkTreeIter * iter)127 gtk_tree_model_get_iter_last (GtkTreeModel *model,
128                               GtkTreeIter  *iter)
129 {
130   GtkTreeIter next;
131 
132   if (!gtk_tree_model_iter_last_child (model, &next, NULL))
133     return FALSE;
134 
135   do
136     *iter = next;
137   while (gtk_tree_model_iter_last_child (model, &next, &next));
138 
139   return TRUE;
140 }
141 
142 static gboolean
gtk_tree_walk_step_back(GtkTreeWalk * walk)143 gtk_tree_walk_step_back (GtkTreeWalk *walk)
144 {
145   GtkTreeIter previous, down;
146 
147   if (!walk->visited)
148     {
149       if (!gtk_tree_model_get_iter_last (walk->model, &walk->position))
150         return FALSE;
151 
152       walk->visited = TRUE;
153       return TRUE;
154     }
155 
156   previous = walk->position;
157   if (gtk_tree_model_iter_previous (walk->model, &previous))
158     {
159       while (gtk_tree_model_iter_last_child (walk->model, &down, &previous))
160         previous = down;
161 
162       walk->position = previous;
163       return TRUE;
164     }
165 
166   if (gtk_tree_model_iter_parent (walk->model, &previous, &walk->position))
167     {
168       walk->position = previous;
169       return TRUE;
170     }
171 
172   return FALSE;
173 }
174 
175 static gboolean
gtk_tree_walk_step(GtkTreeWalk * walk,gboolean backwards)176 gtk_tree_walk_step (GtkTreeWalk *walk, gboolean backwards)
177 {
178   if (backwards)
179     return gtk_tree_walk_step_back (walk);
180   else
181     return gtk_tree_walk_step_forward (walk);
182 }
183 
184 static gboolean
row_is_match(GtkTreeWalk * walk)185 row_is_match (GtkTreeWalk *walk)
186 {
187   if (walk->predicate)
188     return walk->predicate (walk->model, &walk->position, walk->data);
189   return TRUE;
190 }
191 
192 gboolean
gtk_tree_walk_next_match(GtkTreeWalk * walk,gboolean force_move,gboolean backwards,GtkTreeIter * iter)193 gtk_tree_walk_next_match (GtkTreeWalk *walk,
194                           gboolean     force_move,
195                           gboolean     backwards,
196                           GtkTreeIter *iter)
197 {
198   gboolean moved = FALSE;
199   gboolean was_visited;
200   GtkTreeIter position;
201 
202   was_visited = walk->visited;
203   position = walk->position;
204 
205   do
206     {
207       if (moved || (!force_move && walk->visited))
208         {
209           if (row_is_match (walk))
210             {
211               *iter = walk->position;
212               return TRUE;
213             }
214         }
215       moved = TRUE;
216     }
217   while (gtk_tree_walk_step (walk, backwards));
218 
219   walk->visited = was_visited;
220   walk->position = position;
221 
222   return FALSE;
223 }
224 
225 gboolean
gtk_tree_walk_get_position(GtkTreeWalk * walk,GtkTreeIter * iter)226 gtk_tree_walk_get_position (GtkTreeWalk *walk,
227                             GtkTreeIter *iter)
228 {
229   *iter = walk->position;
230   return walk->visited;
231 }
232