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