1 | /* |
2 | * Copyright © 2019 Benjamin Otte |
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.1 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 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include "gtkmultifilter.h" |
23 | |
24 | #include "gtkbuildable.h" |
25 | #include "gtkintl.h" |
26 | #include "gtktypebuiltins.h" |
27 | |
28 | #define GDK_ARRAY_TYPE_NAME GtkFilters |
29 | #define GDK_ARRAY_NAME gtk_filters |
30 | #define GDK_ARRAY_ELEMENT_TYPE GtkFilter * |
31 | #define GDK_ARRAY_FREE_FUNC g_object_unref |
32 | |
33 | #include "gdk/gdkarrayimpl.c" |
34 | |
35 | /*** MULTI FILTER ***/ |
36 | |
37 | /** |
38 | * GtkMultiFilter: |
39 | * |
40 | * `GtkMultiFilter` is the base class for filters that combine multiple filters. |
41 | */ |
42 | |
43 | /** |
44 | * GtkAnyFilter: |
45 | * |
46 | * `GtkAnyFilter` matches an item when at least one of its filters matches. |
47 | * |
48 | * To add filters to a `GtkAnyFilter`, use [method@Gtk.MultiFilter.append]. |
49 | */ |
50 | |
51 | /** |
52 | * GtkEveryFilter: |
53 | * |
54 | * `GtkEveryFilter` matches an item when each of its filters matches. |
55 | * |
56 | * To add filters to a `GtkEveryFilter`, use [method@Gtk.MultiFilter.append]. |
57 | */ |
58 | |
59 | struct _GtkMultiFilter |
60 | { |
61 | GtkFilter parent_instance; |
62 | |
63 | GtkFilters filters; |
64 | }; |
65 | |
66 | struct _GtkMultiFilterClass |
67 | { |
68 | GtkFilterClass parent_class; |
69 | |
70 | GtkFilterChange addition_change; |
71 | GtkFilterChange removal_change; |
72 | }; |
73 | |
74 | static GType |
75 | gtk_multi_filter_get_item_type (GListModel *list) |
76 | { |
77 | return GTK_TYPE_FILTER; |
78 | } |
79 | |
80 | static guint |
81 | gtk_multi_filter_get_n_items (GListModel *list) |
82 | { |
83 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: list); |
84 | |
85 | return gtk_filters_get_size (self: &self->filters); |
86 | } |
87 | |
88 | static gpointer |
89 | gtk_multi_filter_get_item (GListModel *list, |
90 | guint position) |
91 | { |
92 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: list); |
93 | |
94 | if (position < gtk_filters_get_size (self: &self->filters)) |
95 | return g_object_ref (gtk_filters_get (&self->filters, position)); |
96 | else |
97 | return NULL; |
98 | } |
99 | |
100 | static void |
101 | gtk_multi_filter_list_model_init (GListModelInterface *iface) |
102 | { |
103 | iface->get_item_type = gtk_multi_filter_get_item_type; |
104 | iface->get_n_items = gtk_multi_filter_get_n_items; |
105 | iface->get_item = gtk_multi_filter_get_item; |
106 | } |
107 | |
108 | static GtkBuildableIface *parent_buildable_iface; |
109 | |
110 | static void |
111 | gtk_multi_filter_buildable_add_child (GtkBuildable *buildable, |
112 | GtkBuilder *builder, |
113 | GObject *child, |
114 | const char *type) |
115 | { |
116 | if (GTK_IS_FILTER (ptr: child)) |
117 | gtk_multi_filter_append (self: GTK_MULTI_FILTER (ptr: buildable), g_object_ref (GTK_FILTER (child))); |
118 | else |
119 | parent_buildable_iface->add_child (buildable, builder, child, type); |
120 | } |
121 | |
122 | static void |
123 | gtk_multi_filter_buildable_init (GtkBuildableIface *iface) |
124 | { |
125 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
126 | |
127 | iface->add_child = gtk_multi_filter_buildable_add_child; |
128 | } |
129 | |
130 | G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkMultiFilter, gtk_multi_filter, GTK_TYPE_FILTER, |
131 | G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_multi_filter_list_model_init) |
132 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_multi_filter_buildable_init)) |
133 | |
134 | static void |
135 | gtk_multi_filter_changed_cb (GtkFilter *filter, |
136 | GtkFilterChange change, |
137 | GtkMultiFilter *self) |
138 | { |
139 | gtk_filter_changed (self: GTK_FILTER (ptr: self), change); |
140 | } |
141 | |
142 | static void |
143 | gtk_multi_filter_dispose (GObject *object) |
144 | { |
145 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: object); |
146 | guint i; |
147 | |
148 | for (i = 0; i < gtk_filters_get_size (self: &self->filters); i++) |
149 | { |
150 | GtkFilter *filter = gtk_filters_get (self: &self->filters, pos: i); |
151 | g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self); |
152 | } |
153 | |
154 | gtk_filters_clear (self: &self->filters); |
155 | |
156 | G_OBJECT_CLASS (gtk_multi_filter_parent_class)->dispose (object); |
157 | } |
158 | |
159 | static void |
160 | gtk_multi_filter_class_init (GtkMultiFilterClass *class) |
161 | { |
162 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
163 | |
164 | object_class->dispose = gtk_multi_filter_dispose; |
165 | } |
166 | |
167 | static void |
168 | gtk_multi_filter_init (GtkMultiFilter *self) |
169 | { |
170 | gtk_filters_init (self: &self->filters); |
171 | } |
172 | |
173 | /** |
174 | * gtk_multi_filter_append: |
175 | * @self: a `GtkMultiFilter` |
176 | * @filter: (transfer full): A new filter to use |
177 | * |
178 | * Adds a @filter to @self to use for matching. |
179 | */ |
180 | void |
181 | gtk_multi_filter_append (GtkMultiFilter *self, |
182 | GtkFilter *filter) |
183 | { |
184 | g_return_if_fail (GTK_IS_MULTI_FILTER (self)); |
185 | g_return_if_fail (GTK_IS_FILTER (filter)); |
186 | |
187 | g_signal_connect (filter, "changed" , G_CALLBACK (gtk_multi_filter_changed_cb), self); |
188 | gtk_filters_append (self: &self->filters, value: filter); |
189 | |
190 | gtk_filter_changed (self: GTK_FILTER (ptr: self), |
191 | change: GTK_MULTI_FILTER_GET_CLASS (ptr: self)->addition_change); |
192 | } |
193 | |
194 | /** |
195 | * gtk_multi_filter_remove: |
196 | * @self: a `GtkMultiFilter` |
197 | * @position: position of filter to remove |
198 | * |
199 | * Removes the filter at the given @position from the list of filters used |
200 | * by @self. |
201 | * |
202 | * If @position is larger than the number of filters, nothing happens and |
203 | * the function returns. |
204 | **/ |
205 | void |
206 | gtk_multi_filter_remove (GtkMultiFilter *self, |
207 | guint position) |
208 | { |
209 | guint length; |
210 | GtkFilter *filter; |
211 | |
212 | length = gtk_filters_get_size (self: &self->filters); |
213 | if (position >= length) |
214 | return; |
215 | |
216 | filter = gtk_filters_get (self: &self->filters, pos: position); |
217 | g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self); |
218 | gtk_filters_splice (self: &self->filters, pos: position, removed: 1, FALSE, NULL, added: 0); |
219 | |
220 | gtk_filter_changed (self: GTK_FILTER (ptr: self), |
221 | change: GTK_MULTI_FILTER_GET_CLASS (ptr: self)->removal_change); |
222 | } |
223 | |
224 | /*** ANY FILTER ***/ |
225 | |
226 | struct _GtkAnyFilter |
227 | { |
228 | GtkMultiFilter parent_instance; |
229 | }; |
230 | |
231 | struct _GtkAnyFilterClass |
232 | { |
233 | GtkMultiFilterClass parent_class; |
234 | }; |
235 | |
236 | G_DEFINE_TYPE (GtkAnyFilter, gtk_any_filter, GTK_TYPE_MULTI_FILTER) |
237 | |
238 | static gboolean |
239 | gtk_any_filter_match (GtkFilter *filter, |
240 | gpointer item) |
241 | { |
242 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: filter); |
243 | guint i; |
244 | |
245 | for (i = 0; i < gtk_filters_get_size (self: &self->filters); i++) |
246 | { |
247 | GtkFilter *child = gtk_filters_get (self: &self->filters, pos: i); |
248 | |
249 | if (gtk_filter_match (self: child, item)) |
250 | return TRUE; |
251 | } |
252 | |
253 | return FALSE; |
254 | } |
255 | |
256 | static GtkFilterMatch |
257 | gtk_any_filter_get_strictness (GtkFilter *filter) |
258 | { |
259 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: filter); |
260 | guint i; |
261 | GtkFilterMatch result = GTK_FILTER_MATCH_NONE; |
262 | |
263 | for (i = 0; i < gtk_filters_get_size (self: &self->filters); i++) |
264 | { |
265 | GtkFilter *child = gtk_filters_get (self: &self->filters, pos: i); |
266 | |
267 | switch (gtk_filter_get_strictness (self: child)) |
268 | { |
269 | case GTK_FILTER_MATCH_SOME: |
270 | result = GTK_FILTER_MATCH_SOME; |
271 | break; |
272 | case GTK_FILTER_MATCH_NONE: |
273 | break; |
274 | case GTK_FILTER_MATCH_ALL: |
275 | return GTK_FILTER_MATCH_ALL; |
276 | default: |
277 | g_return_val_if_reached (GTK_FILTER_MATCH_NONE); |
278 | break; |
279 | } |
280 | } |
281 | |
282 | return result; |
283 | } |
284 | |
285 | static void |
286 | gtk_any_filter_class_init (GtkAnyFilterClass *class) |
287 | { |
288 | GtkMultiFilterClass *multi_filter_class = GTK_MULTI_FILTER_CLASS (ptr: class); |
289 | GtkFilterClass *filter_class = GTK_FILTER_CLASS (ptr: class); |
290 | |
291 | multi_filter_class->addition_change = GTK_FILTER_CHANGE_LESS_STRICT; |
292 | multi_filter_class->removal_change = GTK_FILTER_CHANGE_MORE_STRICT; |
293 | |
294 | filter_class->match = gtk_any_filter_match; |
295 | filter_class->get_strictness = gtk_any_filter_get_strictness; |
296 | } |
297 | |
298 | static void |
299 | gtk_any_filter_init (GtkAnyFilter *self) |
300 | { |
301 | } |
302 | |
303 | /** |
304 | * gtk_any_filter_new: |
305 | * |
306 | * Creates a new empty "any" filter. |
307 | * |
308 | * Use [method@Gtk.MultiFilter.append] to add filters to it. |
309 | * |
310 | * This filter matches an item if any of the filters added to it |
311 | * matches the item. In particular, this means that if no filter |
312 | * has been added to it, the filter matches no item. |
313 | * |
314 | * Returns: a new `GtkAnyFilter` |
315 | */ |
316 | GtkAnyFilter * |
317 | gtk_any_filter_new (void) |
318 | { |
319 | return g_object_new (GTK_TYPE_ANY_FILTER, NULL); |
320 | } |
321 | |
322 | /*** EVERY FILTER ***/ |
323 | |
324 | struct _GtkEveryFilter |
325 | { |
326 | GtkMultiFilter parent_instance; |
327 | }; |
328 | |
329 | struct _GtkEveryFilterClass |
330 | { |
331 | GtkMultiFilterClass parent_class; |
332 | }; |
333 | |
334 | G_DEFINE_TYPE (GtkEveryFilter, gtk_every_filter, GTK_TYPE_MULTI_FILTER) |
335 | |
336 | static gboolean |
337 | gtk_every_filter_match (GtkFilter *filter, |
338 | gpointer item) |
339 | { |
340 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: filter); |
341 | guint i; |
342 | |
343 | for (i = 0; i < gtk_filters_get_size (self: &self->filters); i++) |
344 | { |
345 | GtkFilter *child = gtk_filters_get (self: &self->filters, pos: i); |
346 | |
347 | if (!gtk_filter_match (self: child, item)) |
348 | return FALSE; |
349 | } |
350 | |
351 | return TRUE; |
352 | } |
353 | |
354 | static GtkFilterMatch |
355 | gtk_every_filter_get_strictness (GtkFilter *filter) |
356 | { |
357 | GtkMultiFilter *self = GTK_MULTI_FILTER (ptr: filter); |
358 | guint i; |
359 | GtkFilterMatch result = GTK_FILTER_MATCH_ALL; |
360 | |
361 | for (i = 0; i < gtk_filters_get_size (self: &self->filters); i++) |
362 | { |
363 | GtkFilter *child = gtk_filters_get (self: &self->filters, pos: i); |
364 | |
365 | switch (gtk_filter_get_strictness (self: child)) |
366 | { |
367 | case GTK_FILTER_MATCH_SOME: |
368 | result = GTK_FILTER_MATCH_SOME; |
369 | break; |
370 | case GTK_FILTER_MATCH_NONE: |
371 | return GTK_FILTER_MATCH_NONE; |
372 | case GTK_FILTER_MATCH_ALL: |
373 | break; |
374 | default: |
375 | g_return_val_if_reached (GTK_FILTER_MATCH_NONE); |
376 | break; |
377 | } |
378 | } |
379 | |
380 | return result; |
381 | } |
382 | |
383 | static void |
384 | gtk_every_filter_class_init (GtkEveryFilterClass *class) |
385 | { |
386 | GtkMultiFilterClass *multi_filter_class = GTK_MULTI_FILTER_CLASS (ptr: class); |
387 | GtkFilterClass *filter_class = GTK_FILTER_CLASS (ptr: class); |
388 | |
389 | multi_filter_class->addition_change = GTK_FILTER_CHANGE_MORE_STRICT; |
390 | multi_filter_class->removal_change = GTK_FILTER_CHANGE_LESS_STRICT; |
391 | |
392 | filter_class->match = gtk_every_filter_match; |
393 | filter_class->get_strictness = gtk_every_filter_get_strictness; |
394 | } |
395 | |
396 | static void |
397 | gtk_every_filter_init (GtkEveryFilter *self) |
398 | { |
399 | } |
400 | |
401 | /** |
402 | * gtk_every_filter_new: |
403 | * |
404 | * Creates a new empty "every" filter. |
405 | * |
406 | * Use [method@Gtk.MultiFilter.append] to add filters to it. |
407 | * |
408 | * This filter matches an item if each of the filters added to it |
409 | * matches the item. In particular, this means that if no filter |
410 | * has been added to it, the filter matches every item. |
411 | * |
412 | * Returns: a new `GtkEveryFilter` |
413 | */ |
414 | GtkEveryFilter * |
415 | gtk_every_filter_new (void) |
416 | { |
417 | return g_object_new (GTK_TYPE_EVERY_FILTER, NULL); |
418 | } |
419 | |
420 | |