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 */
37struct _GtkNoSelection
38{
39 GObject parent_instance;
40
41 GListModel *model;
42};
43
44struct _GtkNoSelectionClass
45{
46 GObjectClass parent_class;
47};
48
49enum {
50 PROP_0,
51 PROP_MODEL,
52 N_PROPS
53};
54
55static GParamSpec *properties[N_PROPS] = { NULL, };
56
57static GType
58gtk_no_selection_get_item_type (GListModel *list)
59{
60 return G_TYPE_OBJECT;
61}
62
63static guint
64gtk_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
74static gpointer
75gtk_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
86static void
87gtk_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
94static gboolean
95gtk_no_selection_is_selected (GtkSelectionModel *model,
96 guint position)
97{
98 return FALSE;
99}
100
101static GtkBitset *
102gtk_no_selection_get_selection_in_range (GtkSelectionModel *model,
103 guint pos,
104 guint n_items)
105{
106 return gtk_bitset_new_empty ();
107}
108
109static void
110gtk_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
116G_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
122static void
123gtk_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
134static void
135gtk_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
155static void
156gtk_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
175static void
176gtk_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
185static void
186gtk_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
209static void
210gtk_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 */
222GtkNoSelection *
223gtk_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 */
247GListModel *
248gtk_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 */
264void
265gtk_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

source code of gtk/gtk/gtknoselection.c