Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 : : /*
3 : : * Copyright 2024 GNOME Foundation, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General
16 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 : : *
18 : : * Authors:
19 : : * - Philip Withnall <pwithnall@gnome.org>
20 : : *
21 : : * SPDX-License-Identifier: GPL-3.0-or-later
22 : : */
23 : :
24 : : #include <glib-object.h>
25 : : #include <gtk/gtk.h>
26 : :
27 : : #include "cc-duration-editor.h"
28 : : #include "cc-timelike-editor.h"
29 : :
30 : : /**
31 : : * CcDurationEditor:
32 : : *
33 : : * An editor for time durations. It shows the hours and minutes of the duration,
34 : : * plus buttons to increment or decrement them, and allows the user to also
35 : : * type a duration in using the keyboard.
36 : : *
37 : : * Contrast with #CcTimeEditor, which is an editor for wall clock times. It
38 : : * looks very similar, but constrains its values to [00:00, 23:59]. Durations
39 : : * may not necessarily be constrained to that (although currently they are, as
40 : : * no users of the widget need anything else).
41 : : */
42 : : struct _CcDurationEditor {
43 : : GtkWidget parent_instance;
44 : :
45 : : CcTimelikeEditor *editor;
46 : : guint minimum;
47 : : guint maximum;
48 : : };
49 : :
50 [ # # # # : 0 : G_DEFINE_TYPE (CcDurationEditor, cc_duration_editor, GTK_TYPE_WIDGET)
# # ]
51 : :
52 : : typedef enum {
53 : : PROP_DURATION = 1,
54 : : PROP_MINIMUM,
55 : : PROP_MAXIMUM,
56 : : } CcDurationEditorProperty;
57 : :
58 : : static GParamSpec *props[PROP_MAXIMUM + 1];
59 : :
60 : : static void cc_duration_editor_get_property (GObject *object,
61 : : guint property_id,
62 : : GValue *value,
63 : : GParamSpec *pspec);
64 : : static void cc_duration_editor_set_property (GObject *object,
65 : : guint property_id,
66 : : const GValue *value,
67 : : GParamSpec *pspec);
68 : : static void cc_duration_editor_dispose (GObject *object);
69 : : static void editor_time_changed_cb (CcTimelikeEditor *editor,
70 : : gpointer user_data);
71 : :
72 : : static void
73 : 0 : cc_duration_editor_class_init (CcDurationEditorClass *klass)
74 : : {
75 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
76 : 0 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
77 : :
78 : 0 : object_class->get_property = cc_duration_editor_get_property;
79 : 0 : object_class->set_property = cc_duration_editor_set_property;
80 : 0 : object_class->dispose = cc_duration_editor_dispose;
81 : :
82 : : /**
83 : : * CcDurationEditor:duration:
84 : : *
85 : : * Duration displayed or chosen in the editor, in minutes.
86 : : */
87 : 0 : props[PROP_DURATION] =
88 : 0 : g_param_spec_uint ("duration",
89 : : NULL, NULL,
90 : : 0, G_MAXUINT, 0,
91 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
92 : :
93 : : /**
94 : : * CcDurationEditor:minimum:
95 : : *
96 : : * Minimum allowed value (inclusive) for #CcDurationEditor:duration, in
97 : : * minutes.
98 : : *
99 : : * If this is changed and the current value of #CcDurationEditor:duration is
100 : : * lower than it, the value of #CcDurationEditor:duration will automatically
101 : : * be clamped to the new minimum.
102 : : */
103 : 0 : props[PROP_MINIMUM] =
104 : 0 : g_param_spec_uint ("minimum",
105 : : NULL, NULL,
106 : : 0, G_MAXUINT, 0,
107 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
108 : :
109 : : /**
110 : : * CcDurationEditor:maximum:
111 : : *
112 : : * Maximum allowed value (inclusive) for #CcDurationEditor:duration, in
113 : : * minutes.
114 : : *
115 : : * If this is changed and the current value of #CcDurationEditor:duration is
116 : : * higher than it, the value of #CcDurationEditor:duration will automatically
117 : : * be clamped to the new maximum.
118 : : */
119 : 0 : props[PROP_MAXIMUM] =
120 : 0 : g_param_spec_uint ("maximum",
121 : : NULL, NULL,
122 : : 0, G_MAXUINT, G_MAXUINT,
123 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
124 : :
125 : 0 : g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
126 : :
127 : 0 : g_type_ensure (CC_TYPE_TIMELIKE_EDITOR);
128 : :
129 : 0 : gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentControl/ui/cc-duration-editor.ui");
130 : :
131 : 0 : gtk_widget_class_bind_template_child (widget_class, CcDurationEditor, editor);
132 : :
133 : 0 : gtk_widget_class_bind_template_callback (widget_class, editor_time_changed_cb);
134 : :
135 : 0 : gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
136 : 0 : }
137 : :
138 : : static void
139 : 0 : cc_duration_editor_init (CcDurationEditor *self)
140 : : {
141 : 0 : gtk_widget_init_template (GTK_WIDGET (self));
142 : :
143 : 0 : self->maximum = G_MAXUINT;
144 : 0 : }
145 : :
146 : : static void
147 : 0 : cc_duration_editor_get_property (GObject *object,
148 : : guint property_id,
149 : : GValue *value,
150 : : GParamSpec *pspec)
151 : : {
152 : 0 : CcDurationEditor *self = CC_DURATION_EDITOR (object);
153 : :
154 [ # # # # ]: 0 : switch ((CcDurationEditorProperty) property_id)
155 : : {
156 : 0 : case PROP_DURATION:
157 : 0 : g_value_set_uint (value, cc_duration_editor_get_duration (self));
158 : 0 : break;
159 : 0 : case PROP_MINIMUM:
160 : 0 : g_value_set_uint (value, cc_duration_editor_get_minimum (self));
161 : 0 : break;
162 : 0 : case PROP_MAXIMUM:
163 : 0 : g_value_set_uint (value, cc_duration_editor_get_maximum (self));
164 : 0 : break;
165 : 0 : default:
166 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
167 : 0 : break;
168 : : }
169 : 0 : }
170 : :
171 : : static void
172 : 0 : cc_duration_editor_set_property (GObject *object,
173 : : guint property_id,
174 : : const GValue *value,
175 : : GParamSpec *pspec)
176 : : {
177 : 0 : CcDurationEditor *self = CC_DURATION_EDITOR (object);
178 : :
179 [ # # # # ]: 0 : switch ((CcDurationEditorProperty) property_id)
180 : : {
181 : 0 : case PROP_DURATION:
182 : 0 : cc_duration_editor_set_duration (self, g_value_get_uint (value));
183 : 0 : break;
184 : 0 : case PROP_MINIMUM:
185 : 0 : cc_duration_editor_set_minimum (self, g_value_get_uint (value));
186 : 0 : break;
187 : 0 : case PROP_MAXIMUM:
188 : 0 : cc_duration_editor_set_maximum (self, g_value_get_uint (value));
189 : 0 : break;
190 : 0 : default:
191 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
192 : 0 : break;
193 : : }
194 : 0 : }
195 : :
196 : : static void
197 : 0 : cc_duration_editor_dispose (GObject *object)
198 : : {
199 : 0 : gtk_widget_dispose_template (GTK_WIDGET (object), CC_TYPE_DURATION_EDITOR);
200 : :
201 : 0 : G_OBJECT_CLASS (cc_duration_editor_parent_class)->dispose (object);
202 : 0 : }
203 : :
204 : : static void
205 : 0 : editor_time_changed_cb (CcTimelikeEditor *editor,
206 : : gpointer user_data)
207 : : {
208 : 0 : CcDurationEditor *self = CC_DURATION_EDITOR (user_data);
209 : : guint duration;
210 : :
211 : : /* Clamp to the minimum/maximum. */
212 : 0 : duration = cc_duration_editor_get_duration (self);
213 [ # # # # ]: 0 : if (duration < self->minimum || duration > self->maximum)
214 : : {
215 [ # # ]: 0 : cc_duration_editor_set_duration (self, CLAMP (duration, self->minimum, self->maximum));
216 : 0 : return;
217 : : }
218 : :
219 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DURATION]);
220 : : }
221 : :
222 : : /**
223 : : * cc_duration_editor_new:
224 : : *
225 : : * Create a new #CcDurationEditor, to represent a duration and allow editing it.
226 : : *
227 : : * Returns: (transfer full): a new #CcDurationEditor
228 : : */
229 : : CcDurationEditor *
230 : 0 : cc_duration_editor_new (void)
231 : : {
232 : 0 : return g_object_new (CC_TYPE_DURATION_EDITOR, NULL);
233 : : }
234 : :
235 : : /**
236 : : * cc_duration_editor_get_duration:
237 : : * @self: a #CcDurationEditor
238 : : *
239 : : * Get the value of #CcDurationEditor:duration.
240 : : *
241 : : * Returns: duration specified in the editor, in minutes
242 : : */
243 : : guint
244 : 0 : cc_duration_editor_get_duration (CcDurationEditor *self)
245 : : {
246 : 0 : g_return_val_if_fail (CC_IS_DURATION_EDITOR (self), 0);
247 : :
248 : 0 : return cc_timelike_editor_get_hour (self->editor) * 60 + cc_timelike_editor_get_minute (self->editor);
249 : : }
250 : :
251 : : /**
252 : : * cc_duration_editor_set_duration:
253 : : * @self: a #CcDurationEditor
254 : : * @duration: duration to show in the editor, in minutes
255 : : *
256 : : * Set the value of #CcDurationEditor:duration to @duration.
257 : : */
258 : : void
259 : 0 : cc_duration_editor_set_duration (CcDurationEditor *self,
260 : : guint duration)
261 : : {
262 : : guint hours, minutes;
263 : :
264 : 0 : g_return_if_fail (CC_IS_DURATION_EDITOR (self));
265 : :
266 : : /* Clamp to the minimum/maximum. */
267 [ # # ]: 0 : duration = CLAMP (duration, self->minimum, self->maximum);
268 : :
269 : 0 : hours = duration / 60;
270 : 0 : minutes = duration % 60;
271 : :
272 [ # # # # ]: 0 : if (hours == cc_timelike_editor_get_hour (self->editor) &&
273 : 0 : minutes == cc_timelike_editor_get_minute (self->editor))
274 : 0 : return;
275 : :
276 : 0 : cc_timelike_editor_set_time (self->editor, hours, minutes);
277 : :
278 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DURATION]);
279 : : }
280 : :
281 : : /**
282 : : * cc_duration_editor_get_minimum:
283 : : * @self: a #CcDurationEditor
284 : : *
285 : : * Get the value of #CcDurationEditor:minimum.
286 : : *
287 : : * Returns: minimum value allowed for the duration, in minutes
288 : : */
289 : : guint
290 : 0 : cc_duration_editor_get_minimum (CcDurationEditor *self)
291 : : {
292 : 0 : g_return_val_if_fail (CC_IS_DURATION_EDITOR (self), 0);
293 : :
294 : 0 : return self->minimum;
295 : : }
296 : :
297 : : /**
298 : : * cc_duration_editor_set_minimum:
299 : : * @self: a #CcDurationEditor
300 : : * @minimum: minimum value allowed for the duration, in minutes
301 : : *
302 : : * Set the value of #CcDurationEditor:minimum to @minimum.
303 : : *
304 : : * If the current value of #CcDurationEditor:duration is lower than @minimum, it
305 : : * will automatically be clamped to @minimum.
306 : : */
307 : : void
308 : 0 : cc_duration_editor_set_minimum (CcDurationEditor *self,
309 : : guint minimum)
310 : : {
311 : 0 : g_return_if_fail (CC_IS_DURATION_EDITOR (self));
312 : :
313 [ # # ]: 0 : if (self->minimum == minimum)
314 : 0 : return;
315 : :
316 : 0 : g_object_freeze_notify (G_OBJECT (self));
317 : :
318 : 0 : self->minimum = minimum;
319 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MINIMUM]);
320 : :
321 : : /* Ensure the duration is clamped to the new range. */
322 : 0 : cc_duration_editor_set_duration (self, cc_duration_editor_get_duration (self));
323 : :
324 : 0 : g_object_thaw_notify (G_OBJECT (self));
325 : : }
326 : :
327 : : /**
328 : : * cc_duration_editor_get_maximum:
329 : : * @self: a #CcDurationEditor
330 : : *
331 : : * Get the value of #CcDurationEditor:maximum.
332 : : *
333 : : * Returns: maximum value allowed for the duration, in minutes
334 : : */
335 : : guint
336 : 0 : cc_duration_editor_get_maximum (CcDurationEditor *self)
337 : : {
338 : 0 : g_return_val_if_fail (CC_IS_DURATION_EDITOR (self), 0);
339 : :
340 : 0 : return self->maximum;
341 : : }
342 : :
343 : : /**
344 : : * cc_duration_editor_set_maximum:
345 : : * @self: a #CcDurationEditor
346 : : * @maximum: maximum value allowed for the duration, in minutes
347 : : *
348 : : * Set the value of #CcDurationEditor:maximum to @maximum.
349 : : *
350 : : * If the current value of #CcDurationEditor:duration is higher than @maximum,
351 : : * it will automatically be clamped to @maximum.
352 : : */
353 : : void
354 : 0 : cc_duration_editor_set_maximum (CcDurationEditor *self,
355 : : guint maximum)
356 : : {
357 : 0 : g_return_if_fail (CC_IS_DURATION_EDITOR (self));
358 : :
359 [ # # ]: 0 : if (self->maximum == maximum)
360 : 0 : return;
361 : :
362 : 0 : g_object_freeze_notify (G_OBJECT (self));
363 : :
364 : 0 : self->maximum = maximum;
365 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_MAXIMUM]);
366 : :
367 : : /* Ensure the duration is clamped to the new range. */
368 : 0 : cc_duration_editor_set_duration (self, cc_duration_editor_get_duration (self));
369 : :
370 : 0 : g_object_thaw_notify (G_OBJECT (self));
371 : : }
|