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 "gtknoselection.h" |
23 | |
24 | #include "gtkbitset.h" |
25 | #include "gtkintl.h" |
26 | #include "gtkselectionmodel.h" |
27 | |
28 | /** |
29 | * GtkNoSelection: |
30 | * |
31 | * `GtkNoSelection` is a `GtkSelectionModel` that does not allow selecting |
32 | * anything. |
33 | * |
34 | * This model is meant to be used as a simple wrapper around a `GListModel` |
35 | * when a `GtkSelectionModel` is required. |
36 | */ |
37 | struct _GtkNoSelection |
38 | { |
39 | GObject parent_instance; |
40 | |
41 | GListModel *model; |
42 | }; |
43 | |
44 | struct _GtkNoSelectionClass |
45 | { |
46 | GObjectClass parent_class; |
47 | }; |
48 | |
49 | enum { |
50 | PROP_0, |
51 | PROP_MODEL, |
52 | N_PROPS |
53 | }; |
54 | |
55 | static GParamSpec *properties[N_PROPS] = { NULL, }; |
56 | |
57 | static GType |
58 | gtk_no_selection_get_item_type (GListModel *list) |
59 | { |
60 | return G_TYPE_OBJECT; |
61 | } |
62 | |
63 | static guint |
64 | gtk_no_selection_get_n_items (GListModel *list) |
65 | { |
66 | GtkNoSelection *self = GTK_NO_SELECTION (ptr: list); |
67 | |
68 | if (self->model == NULL) |
69 | return 0; |
70 | |
71 | return g_list_model_get_n_items (list: self->model); |
72 | } |
73 | |
74 | static gpointer |
75 | gtk_no_selection_get_item (GListModel *list, |
76 | guint position) |
77 | { |
78 | GtkNoSelection *self = GTK_NO_SELECTION (ptr: list); |
79 | |
80 | if (self->model == NULL) |
81 | return NULL; |
82 | |
83 | return g_list_model_get_item (list: self->model, position); |
84 | } |
85 | |
86 | static void |
87 | gtk_no_selection_list_model_init (GListModelInterface *iface) |
88 | { |
89 | iface->get_item_type = gtk_no_selection_get_item_type; |
90 | iface->get_n_items = gtk_no_selection_get_n_items; |
91 | iface->get_item = gtk_no_selection_get_item; |
92 | } |
93 | |
94 | static gboolean |
95 | gtk_no_selection_is_selected (GtkSelectionModel *model, |
96 | guint position) |
97 | { |
98 | return FALSE; |
99 | } |
100 | |
101 | static GtkBitset * |
102 | gtk_no_selection_get_selection_in_range (GtkSelectionModel *model, |
103 | guint pos, |
104 | guint n_items) |
105 | { |
106 | return gtk_bitset_new_empty (); |
107 | } |
108 | |
109 | static void |
110 | gtk_no_selection_selection_model_init (GtkSelectionModelInterface *iface) |
111 | { |
112 | iface->is_selected = gtk_no_selection_is_selected; |
113 | iface->get_selection_in_range = gtk_no_selection_get_selection_in_range; |
114 | } |
115 | |
116 | G_DEFINE_TYPE_EXTENDED (GtkNoSelection, gtk_no_selection, G_TYPE_OBJECT, 0, |
117 | G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, |
118 | gtk_no_selection_list_model_init) |
119 | G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL, |
120 | gtk_no_selection_selection_model_init)) |
121 | |
122 | static void |
123 | gtk_no_selection_clear_model (GtkNoSelection *self) |
124 | { |
125 | if (self->model == NULL) |
126 | return; |
127 | |
128 | g_signal_handlers_disconnect_by_func (self->model, |
129 | g_list_model_items_changed, |
130 | self); |
131 | g_clear_object (&self->model); |
132 | } |
133 | |
134 | static void |
135 | gtk_no_selection_set_property (GObject *object, |
136 | guint prop_id, |
137 | const GValue *value, |
138 | GParamSpec *pspec) |
139 | |
140 | { |
141 | GtkNoSelection *self = GTK_NO_SELECTION (ptr: object); |
142 | |
143 | switch (prop_id) |
144 | { |
145 | case PROP_MODEL: |
146 | gtk_no_selection_set_model (self, model: g_value_get_object (value)); |
147 | break; |
148 | |
149 | default: |
150 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
151 | break; |
152 | } |
153 | } |
154 | |
155 | static void |
156 | gtk_no_selection_get_property (GObject *object, |
157 | guint prop_id, |
158 | GValue *value, |
159 | GParamSpec *pspec) |
160 | { |
161 | GtkNoSelection *self = GTK_NO_SELECTION (ptr: object); |
162 | |
163 | switch (prop_id) |
164 | { |
165 | case PROP_MODEL: |
166 | g_value_set_object (value, v_object: self->model); |
167 | break; |
168 | |
169 | default: |
170 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
171 | break; |
172 | } |
173 | } |
174 | |
175 | static void |
176 | gtk_no_selection_dispose (GObject *object) |
177 | { |
178 | GtkNoSelection *self = GTK_NO_SELECTION (ptr: object); |
179 | |
180 | gtk_no_selection_clear_model (self); |
181 | |
182 | G_OBJECT_CLASS (gtk_no_selection_parent_class)->dispose (object); |
183 | } |
184 | |
185 | static void |
186 | gtk_no_selection_class_init (GtkNoSelectionClass *klass) |
187 | { |
188 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
189 | |
190 | gobject_class->get_property = gtk_no_selection_get_property; |
191 | gobject_class->set_property = gtk_no_selection_set_property; |
192 | gobject_class->dispose = gtk_no_selection_dispose; |
193 | |
194 | /** |
195 | * GtkNoSelection:model: (attributes org.gtk.property.get=gtk_no_selection_get_model org.gtk.Property.set=gtk_no_selection_set_model) |
196 | * |
197 | * The model being managed. |
198 | */ |
199 | properties[PROP_MODEL] = |
200 | g_param_spec_object (name: "model" , |
201 | P_("The model" ), |
202 | P_("The model being managed" ), |
203 | G_TYPE_LIST_MODEL, |
204 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); |
205 | |
206 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties); |
207 | } |
208 | |
209 | static void |
210 | gtk_no_selection_init (GtkNoSelection *self) |
211 | { |
212 | } |
213 | |
214 | /** |
215 | * gtk_no_selection_new: |
216 | * @model: (nullable) (transfer full): the `GListModel` to manage |
217 | * |
218 | * Creates a new selection to handle @model. |
219 | * |
220 | * Returns: (transfer full) (type GtkNoSelection): a new `GtkNoSelection` |
221 | */ |
222 | GtkNoSelection * |
223 | gtk_no_selection_new (GListModel *model) |
224 | { |
225 | GtkNoSelection *self; |
226 | |
227 | g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL); |
228 | |
229 | self = g_object_new (GTK_TYPE_NO_SELECTION, |
230 | first_property_name: "model" , model, |
231 | NULL); |
232 | |
233 | /* consume the reference */ |
234 | g_clear_object (&model); |
235 | |
236 | return self; |
237 | } |
238 | |
239 | /** |
240 | * gtk_no_selection_get_model: (attributes org.gtk.Method.get_property=model) |
241 | * @self: a `GtkNoSelection` |
242 | * |
243 | * Gets the model that @self is wrapping. |
244 | * |
245 | * Returns: (transfer none) (nullable): The model being wrapped |
246 | */ |
247 | GListModel * |
248 | gtk_no_selection_get_model (GtkNoSelection *self) |
249 | { |
250 | g_return_val_if_fail (GTK_IS_NO_SELECTION (self), NULL); |
251 | |
252 | return self->model; |
253 | } |
254 | |
255 | /** |
256 | * gtk_no_selection_set_model: (attributes org.gtk.Method.set_property=model) |
257 | * @self: a `GtkNoSelection` |
258 | * @model: (nullable): A `GListModel` to wrap |
259 | * |
260 | * Sets the model that @self should wrap. |
261 | * |
262 | * If @model is %NULL, this model will be empty. |
263 | */ |
264 | void |
265 | gtk_no_selection_set_model (GtkNoSelection *self, |
266 | GListModel *model) |
267 | { |
268 | guint n_items_before; |
269 | |
270 | g_return_if_fail (GTK_IS_NO_SELECTION (self)); |
271 | g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model)); |
272 | |
273 | if (self->model == model) |
274 | return; |
275 | |
276 | n_items_before = self->model ? g_list_model_get_n_items (list: self->model) : 0; |
277 | gtk_no_selection_clear_model (self); |
278 | |
279 | if (model) |
280 | { |
281 | self->model = g_object_ref (model); |
282 | g_signal_connect_swapped (self->model, "items-changed" , |
283 | G_CALLBACK (g_list_model_items_changed), self); |
284 | } |
285 | |
286 | g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), |
287 | position: 0, |
288 | removed: n_items_before, |
289 | added: model ? g_list_model_get_n_items (list: self->model) : 0); |
290 | |
291 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_MODEL]); |
292 | } |
293 | |