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 * |
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 |
49 | gtk_tree_walk_free (GtkTreeWalk *walk) |
50 | { |
51 | g_object_unref (object: walk->model); |
52 | |
53 | if (walk->destroy) |
54 | walk->destroy (walk->data); |
55 | |
56 | g_free (mem: walk); |
57 | } |
58 | |
59 | void |
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 |
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 (tree_model: walk->model, iter: &walk->position)) |
82 | return FALSE; |
83 | |
84 | walk->visited = TRUE; |
85 | return TRUE; |
86 | } |
87 | |
88 | if (gtk_tree_model_iter_children (tree_model: walk->model, iter: &next, parent: &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 (tree_model: walk->model, iter: &next)) |
99 | { |
100 | walk->position = next; |
101 | return TRUE; |
102 | } |
103 | } |
104 | while (gtk_tree_model_iter_parent (tree_model: walk->model, iter: &next, child: &up)); |
105 | |
106 | return FALSE; |
107 | } |
108 | |
109 | static gboolean |
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 (tree_model: model, iter: &next, parent)) |
117 | return FALSE; |
118 | |
119 | do |
120 | *iter = next; |
121 | while (gtk_tree_model_iter_next (tree_model: model, iter: &next)); |
122 | |
123 | return TRUE; |
124 | } |
125 | |
126 | static gboolean |
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, iter: &next, NULL)) |
133 | return FALSE; |
134 | |
135 | do |
136 | *iter = next; |
137 | while (gtk_tree_model_iter_last_child (model, iter: &next, parent: &next)); |
138 | |
139 | return TRUE; |
140 | } |
141 | |
142 | static gboolean |
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 (model: walk->model, iter: &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 (tree_model: walk->model, iter: &previous)) |
158 | { |
159 | while (gtk_tree_model_iter_last_child (model: walk->model, iter: &down, parent: &previous)) |
160 | previous = down; |
161 | |
162 | walk->position = previous; |
163 | return TRUE; |
164 | } |
165 | |
166 | if (gtk_tree_model_iter_parent (tree_model: walk->model, iter: &previous, child: &walk->position)) |
167 | { |
168 | walk->position = previous; |
169 | return TRUE; |
170 | } |
171 | |
172 | return FALSE; |
173 | } |
174 | |
175 | static gboolean |
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 |
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 |
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 |
226 | gtk_tree_walk_get_position (GtkTreeWalk *walk, |
227 | GtkTreeIter *iter) |
228 | { |
229 | *iter = walk->position; |
230 | return walk->visited; |
231 | } |
232 | |