Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright 2025 GNOME Foundation, Inc.
4 : : *
5 : : * SPDX-License-Identifier: GPL-2.0-or-later
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Authors:
21 : : * - Ignacy Kuchciński <ignacykuchcinski@gnome.org>
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include <glib/gi18n-lib.h>
27 : : #define GNOME_DESKTOP_USE_UNSTABLE_API
28 : : #include <libgnome-desktop/gnome-wall-clock.h>
29 : : #include <libmalcontent-ui/malcontent-ui.h>
30 : : #include <stdint.h>
31 : :
32 : : #include "user-page.h"
33 : : #include "user-image.h"
34 : : #include "access-page.h"
35 : :
36 : : #define CLOCK_SCHEMA "org.gnome.desktop.interface"
37 : : #define CLOCK_FORMAT_KEY "clock-format"
38 : :
39 : : static void user_page_clock_changed_cb (GSettings *settings, char *key, void *user_data);
40 : : static void update_last_login_label (MctUserPage *self);
41 : :
42 : : /* Copied from panels/wellbeing/cc-screen-time-statistics-row.c
43 : : * in gnome-control-center */
44 : : static char *
45 : 0 : format_hours_and_minutes (unsigned int minutes,
46 : : gboolean omit_minutes_if_zero)
47 : : {
48 : 0 : unsigned int hours = minutes / 60;
49 : 0 : minutes %= 60;
50 : :
51 : : /* Technically we should be formatting these units as per the SI Brochure,
52 : : * table 8 and §5.4.3: with a 0+00A0 (non-breaking space) between the value
53 : : * and unit; and using ‘min’ as the unit for minutes, not ‘m’.
54 : : *
55 : : * However, space is very restricted here, so we’re optimising for that.
56 : : * Given that the whole panel is about screen *time*, hopefully the meaning of
57 : : * the numbers should be obvious. */
58 : :
59 [ # # # # ]: 0 : if (hours == 0 && minutes > 0)
60 : : {
61 : : /* Translators: This is a duration in minutes, for example ‘15m’ for 15 minutes.
62 : : * Use whatever shortest unit label is used for minutes in your locale. */
63 : 0 : return g_strdup_printf (_("%um"), minutes);
64 : : }
65 [ # # ]: 0 : else if (minutes == 0)
66 : : {
67 : : /* Translators: This is a duration in hours, for example ‘2h’ for 2 hours.
68 : : * Use whatever shortest unit label is used for hours in your locale. */
69 : 0 : return g_strdup_printf (_("%uh"), hours);
70 : : }
71 : : else
72 : : {
73 : : /* Translators: This is a duration in hours and minutes, for example
74 : : * ‘3h 15m’ for 3 hours and 15 minutes. Use whatever shortest unit label
75 : : * is used for hours and minutes in your locale. */
76 : 0 : return g_strdup_printf (_("%uh %um"), hours, minutes);
77 : : }
78 : : }
79 : :
80 : : /**
81 : : * MctUserPage:
82 : : *
83 : : * A widget which shows available parental controls
84 : : * for the selected user.
85 : : *
86 : : * Since: 0.14.0
87 : : */
88 : : struct _MctUserPage
89 : : {
90 : : AdwNavigationPage parent;
91 : :
92 : : MctUserImage *user_image;
93 : : GtkLabel *name_label;
94 : : GtkLabel *date_label;
95 : : GtkLabel *screen_time_label;
96 : : GtkLabel *last_login_label;
97 : : GtkLevelBar *screen_time_level_bar;
98 : : GSettings *clock_settings; /* (owned) */
99 : : GCancellable *cancellable; /* (owned) */
100 : : unsigned long user_notify_id;
101 : : unsigned int daily_limit_secs;
102 : : gboolean daily_limit_enabled;
103 : : uint64_t active_today_time_secs;
104 : :
105 : : unsigned long clock_changed_id;
106 : :
107 : : MctUser *user; /* (owned) */
108 : : GDBusConnection *connection; /* (owned) */
109 : : MctManager *policy_manager; /* (owned) */
110 : : };
111 : :
112 [ # # # # : 0 : G_DEFINE_TYPE (MctUserPage, mct_user_page, ADW_TYPE_NAVIGATION_PAGE)
# # ]
113 : :
114 : : typedef enum
115 : : {
116 : : PROP_USER = 1,
117 : : PROP_CONNECTION,
118 : : PROP_POLICY_MANAGER,
119 : : } MctUserPageProperties;
120 : :
121 : : static GParamSpec *properties[PROP_POLICY_MANAGER + 1];
122 : :
123 : : static void
124 : 0 : mct_user_page_get_property (GObject *object,
125 : : guint prop_id,
126 : : GValue *value,
127 : : GParamSpec *pspec)
128 : : {
129 : 0 : MctUserPage *self = MCT_USER_PAGE (object);
130 : :
131 [ # # # # ]: 0 : switch ((MctUserPageProperties) prop_id)
132 : : {
133 : 0 : case PROP_USER:
134 : 0 : g_value_set_object (value, self->user);
135 : 0 : break;
136 : :
137 : 0 : case PROP_CONNECTION:
138 : 0 : g_value_set_object (value, self->connection);
139 : 0 : break;
140 : :
141 : 0 : case PROP_POLICY_MANAGER:
142 : 0 : g_value_set_object (value, self->policy_manager);
143 : 0 : break;
144 : :
145 : 0 : default:
146 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 : : }
148 : 0 : }
149 : :
150 : : static void
151 : 0 : mct_user_page_set_property (GObject *object,
152 : : guint prop_id,
153 : : const GValue *value,
154 : : GParamSpec *pspec)
155 : : {
156 : 0 : MctUserPage *self = MCT_USER_PAGE (object);
157 : :
158 [ # # # # ]: 0 : switch ((MctUserPageProperties) prop_id)
159 : : {
160 : 0 : case PROP_USER:
161 : 0 : mct_user_page_set_user (self, g_value_get_object (value), NULL);
162 : 0 : break;
163 : :
164 : 0 : case PROP_CONNECTION:
165 : : /* Construct-only. May not be %NULL. */
166 : 0 : g_assert (self->connection == NULL);
167 : 0 : self->connection = g_value_dup_object (value);
168 : 0 : g_assert (self->connection != NULL);
169 : 0 : break;
170 : :
171 : 0 : case PROP_POLICY_MANAGER:
172 : : /* Construct-only. May not be %NULL. */
173 : 0 : g_assert (self->policy_manager == NULL);
174 : 0 : self->policy_manager = g_value_dup_object (value);
175 : 0 : g_assert (self->policy_manager != NULL);
176 : 0 : break;
177 : :
178 : 0 : default:
179 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180 : : }
181 : 0 : }
182 : :
183 : : static void
184 : 0 : mct_user_page_constructed (GObject *object)
185 : : {
186 : 0 : MctUserPage *self = MCT_USER_PAGE (object);
187 : :
188 : 0 : g_assert (self->connection != NULL);
189 : :
190 : 0 : G_OBJECT_CLASS (mct_user_page_parent_class)->constructed (object);
191 : 0 : }
192 : :
193 : : static void
194 : 0 : mct_user_page_dispose (GObject *object)
195 : : {
196 : 0 : MctUserPage *self = MCT_USER_PAGE (object);
197 : :
198 [ # # ]: 0 : g_clear_object (&self->cancellable);
199 [ # # ]: 0 : g_clear_signal_handler (&self->user_notify_id, self->user);
200 [ # # ]: 0 : g_clear_object (&self->user);
201 [ # # ]: 0 : g_clear_signal_handler (&self->clock_changed_id, self->clock_settings);
202 [ # # ]: 0 : g_clear_object (&self->clock_settings);
203 [ # # ]: 0 : g_clear_object (&self->connection);
204 [ # # ]: 0 : g_clear_object (&self->policy_manager);
205 : :
206 : 0 : G_OBJECT_CLASS (mct_user_page_parent_class)->dispose (object);
207 : 0 : }
208 : :
209 : : static void
210 : 0 : mct_user_page_class_init (MctUserPageClass *klass)
211 : : {
212 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 : 0 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
214 : :
215 : 0 : g_type_ensure (MCT_TYPE_USER_IMAGE);
216 : :
217 : 0 : object_class->get_property = mct_user_page_get_property;
218 : 0 : object_class->set_property = mct_user_page_set_property;
219 : 0 : object_class->constructed = mct_user_page_constructed;
220 : 0 : object_class->dispose = mct_user_page_dispose;
221 : :
222 : : /**
223 : : * MctUserPage:user:
224 : : *
225 : : * The currently selected user account.
226 : : *
227 : : * Since 0.14.0
228 : : */
229 : 0 : properties[PROP_USER] =
230 : 0 : g_param_spec_object ("user", NULL, NULL,
231 : : MCT_TYPE_USER,
232 : : G_PARAM_READWRITE |
233 : : G_PARAM_STATIC_STRINGS |
234 : : G_PARAM_EXPLICIT_NOTIFY);
235 : :
236 : : /**
237 : : * MctUserPage:connection: (not nullable)
238 : : *
239 : : * A connection to the system bus, where malcontent-timerd runs. It’s
240 : : * provided to allow an existing connection to be re-used and for testing
241 : : * purposes.
242 : : *
243 : : * Since 0.14.0
244 : : */
245 : 0 : properties[PROP_CONNECTION] =
246 : 0 : g_param_spec_object ("connection", NULL, NULL,
247 : : G_TYPE_DBUS_CONNECTION,
248 : : G_PARAM_READWRITE |
249 : : G_PARAM_CONSTRUCT_ONLY |
250 : : G_PARAM_STATIC_STRINGS);
251 : :
252 : : /**
253 : : * MctUserPage:policy-manager: (not nullable)
254 : : *
255 : : * The policy manager providing the data for the widget.
256 : : *
257 : : * Since: 0.14.0
258 : : */
259 : 0 : properties[PROP_POLICY_MANAGER] =
260 : 0 : g_param_spec_object ("policy-manager", NULL, NULL,
261 : : MCT_TYPE_MANAGER,
262 : : G_PARAM_READWRITE |
263 : : G_PARAM_CONSTRUCT_ONLY |
264 : : G_PARAM_STATIC_STRINGS);
265 : :
266 : 0 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
267 : :
268 : 0 : gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentControl/ui/user-page.ui");
269 : :
270 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, user_image);
271 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, name_label);
272 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, date_label);
273 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, screen_time_label);
274 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, last_login_label);
275 : 0 : gtk_widget_class_bind_template_child (widget_class, MctUserPage, screen_time_level_bar);
276 : 0 : }
277 : :
278 : : static void
279 : 0 : mct_user_page_init (MctUserPage *self)
280 : : {
281 : 0 : gtk_widget_init_template (GTK_WIDGET (self));
282 : :
283 : 0 : self->cancellable = g_cancellable_new ();
284 : :
285 : 0 : self->clock_settings = g_settings_new (CLOCK_SCHEMA);
286 : :
287 : 0 : self->clock_changed_id = g_signal_connect (self->clock_settings,
288 : : "changed::" CLOCK_FORMAT_KEY,
289 : : G_CALLBACK (user_page_clock_changed_cb),
290 : : self);
291 : 0 : }
292 : :
293 : : static void
294 : 0 : update_date_label (MctUserPage *self)
295 : : {
296 : 0 : g_autoptr (GDateTime) now_date_time = NULL;
297 : : GDate today_date;
298 : 0 : char today_date_text[100] = { 0, };
299 : :
300 : 0 : now_date_time = g_date_time_new_now_local ();
301 : 0 : g_date_set_time_t (&today_date, g_date_time_to_unix (now_date_time));
302 : 0 : g_date_strftime (today_date_text, sizeof (today_date_text), _("Today, %-d %B"), &today_date);
303 : 0 : gtk_label_set_label (self->date_label, today_date_text);
304 : 0 : }
305 : :
306 : : static void
307 : 0 : update_screen_time_label (MctUserPage *self)
308 : : {
309 : 0 : g_autofree char *active_today_time_text = NULL;
310 : :
311 : 0 : active_today_time_text = format_hours_and_minutes (self->active_today_time_secs / 60, FALSE);
312 : 0 : gtk_label_set_label (self->screen_time_label, active_today_time_text);
313 : 0 : }
314 : :
315 : : static void
316 : 0 : update_screen_time_level_bar (MctUserPage *self)
317 : : {
318 : : double max_value;
319 : :
320 : 0 : max_value = self->daily_limit_secs;
321 : :
322 : : /* If the daily limit is not set, use 24 hours as
323 : : * a maximum in the level bar. */
324 [ # # ]: 0 : if (self->daily_limit_secs == 0)
325 : : {
326 : 0 : max_value = 24 * 60 * 60;
327 : : }
328 : :
329 : 0 : gtk_level_bar_set_value (self->screen_time_level_bar, self->active_today_time_secs);
330 : 0 : gtk_level_bar_set_max_value (self->screen_time_level_bar, max_value);
331 : 0 : }
332 : :
333 : : static void
334 : 0 : update_last_login_label (MctUserPage *self)
335 : : {
336 : : GDesktopClockFormat clock_format;
337 : : uint64_t login_time;
338 [ # # ]: 0 : g_autoptr (GDateTime) date_time = NULL;
339 [ # # ]: 0 : g_autofree char *last_login = NULL;
340 [ # # ]: 0 : g_autofree char *last_login_label = NULL;
341 : :
342 : 0 : clock_format = g_settings_get_enum (self->clock_settings, CLOCK_FORMAT_KEY);
343 : 0 : login_time = mct_user_get_login_time (self->user);
344 : :
345 [ # # ]: 0 : if (login_time == 0)
346 : : {
347 : 0 : gtk_widget_set_visible (GTK_WIDGET (self->last_login_label), FALSE);
348 : 0 : return;
349 : : }
350 : : else
351 : : {
352 : 0 : gtk_widget_set_visible (GTK_WIDGET (self->last_login_label), TRUE);
353 : : }
354 : :
355 : 0 : date_time = g_date_time_new_from_unix_local (login_time);
356 : :
357 [ # # ]: 0 : if (clock_format == G_DESKTOP_CLOCK_FORMAT_12H)
358 : : {
359 : : /* Translators: This is the full date and time format used in 12-hour mode. */
360 : 0 : last_login = g_date_time_format (date_time, _("%-e %B %Y, %-l:%M %P"));
361 : : }
362 : : else
363 : : {
364 : : /* Translators: This is the full date and time format used in 24-hour mode. */
365 : 0 : last_login = g_date_time_format (date_time, _("%-e %B %Y, %-k:%M"));
366 : : }
367 : :
368 : 0 : last_login_label = g_strdup_printf (_("Last logged in: %s"), last_login);
369 : 0 : gtk_label_set_label (self->last_login_label, last_login_label);
370 : : }
371 : :
372 : : static void
373 : : query_usage_cb (GObject *object,
374 : : GAsyncResult *result,
375 : : void *user_data);
376 : :
377 : : static void
378 : : get_session_limits_cb (GObject *object,
379 : : GAsyncResult *result,
380 : : void *user_data);
381 : :
382 : : static void
383 : 0 : update_cached_usage (MctUserPage *self)
384 : : {
385 : 0 : g_dbus_connection_call (self->connection,
386 : : "org.freedesktop.MalcontentTimer1",
387 : : "/org/freedesktop/MalcontentTimer1",
388 : : "org.freedesktop.MalcontentTimer1.Parent",
389 : : "QueryUsage",
390 : : g_variant_new ("(uss)",
391 : : mct_user_get_uid (self->user),
392 : : "login-session",
393 : : ""),
394 : : (const GVariantType *) "(a(tt))",
395 : : G_DBUS_CALL_FLAGS_NONE,
396 : : -1,
397 : : self->cancellable,
398 : : query_usage_cb,
399 : : self);
400 : 0 : }
401 : :
402 : : static void
403 : 0 : update_cached_limits (MctUserPage *self)
404 : : {
405 : 0 : mct_manager_get_session_limits_async (self->policy_manager,
406 : : mct_user_get_uid (self->user),
407 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE,
408 : : self->cancellable,
409 : : get_session_limits_cb,
410 : : self);
411 : 0 : }
412 : :
413 : : static void
414 : 0 : get_session_limits_cb (GObject *object,
415 : : GAsyncResult *result,
416 : : void *user_data)
417 : : {
418 : 0 : MctUserPage *self = MCT_USER_PAGE (user_data);
419 [ # # ]: 0 : g_autoptr (MctSessionLimits) limits = NULL;
420 [ # # ]: 0 : g_autoptr (GError) local_error = NULL;
421 : :
422 : 0 : limits = mct_manager_get_session_limits_finish (self->policy_manager,
423 : : result,
424 : : &local_error);
425 : :
426 [ # # ]: 0 : if (limits == NULL)
427 : : {
428 : 0 : g_warning ("Error getting session limits: %s", local_error->message);
429 : 0 : return;
430 : : }
431 : :
432 : 0 : self->daily_limit_enabled = mct_session_limits_get_daily_limit (limits, &self->daily_limit_secs);
433 : :
434 : 0 : update_screen_time_level_bar (self);
435 : : }
436 : :
437 : : static void
438 : 0 : query_usage_cb (GObject *object,
439 : : GAsyncResult *result,
440 : : void *user_data)
441 : : {
442 : 0 : MctUserPage *self = MCT_USER_PAGE (user_data);
443 [ # # ]: 0 : g_autoptr (GDateTime) now_date_time = NULL;
444 : : GDate today_date;
445 [ # # ]: 0 : g_autoptr (GVariant) result_variant = NULL;
446 [ # # ]: 0 : g_autoptr (GError) local_error = NULL;
447 [ # # ]: 0 : g_autoptr (GVariantIter) entries_iter = NULL;
448 : : uint64_t start_wall_time_secs, end_wall_time_secs;
449 : :
450 : 0 : now_date_time = g_date_time_new_now_local ();
451 : 0 : g_date_set_time_t (&today_date, g_date_time_to_unix (now_date_time));
452 : :
453 : 0 : result_variant = g_dbus_connection_call_finish (self->connection,
454 : : result,
455 : : &local_error);
456 : :
457 [ # # ]: 0 : if (result_variant == NULL)
458 : : {
459 : 0 : g_warning ("Failed to get usage data for the level bar: %s", local_error->message);
460 : 0 : return;
461 : : }
462 : :
463 : 0 : g_variant_get (result_variant, "(a(tt))", &entries_iter);
464 : :
465 : 0 : self->active_today_time_secs = 0;
466 : :
467 [ # # ]: 0 : while (g_variant_iter_loop (entries_iter, "(tt)", &start_wall_time_secs, &end_wall_time_secs))
468 : : {
469 : : GDate start_date, end_date;
470 : :
471 : 0 : g_date_set_time_t (&start_date, start_wall_time_secs);
472 : 0 : g_date_set_time_t (&end_date, end_wall_time_secs);
473 : :
474 [ # # ]: 0 : if (g_date_compare (&end_date, &today_date) != 0)
475 : : {
476 : 0 : continue;
477 : : }
478 : :
479 : : /* If the recorded usage entry began on an earlier day than today,
480 : : * clamp it to the beginning of today */
481 [ # # ]: 0 : if (g_date_compare (&start_date, &end_date) != 0)
482 : : {
483 : 0 : start_wall_time_secs = end_wall_time_secs % 86400;
484 : : }
485 : :
486 : 0 : self->active_today_time_secs += end_wall_time_secs - start_wall_time_secs;
487 : : }
488 : :
489 : 0 : update_date_label (self);
490 : 0 : update_screen_time_level_bar (self);
491 : 0 : update_screen_time_label (self);
492 : : }
493 : :
494 : : static void
495 : 0 : user_page_clock_changed_cb (GSettings *settings,
496 : : char *key,
497 : : void *user_data)
498 : : {
499 : 0 : MctUserPage *self = MCT_USER_PAGE (user_data);
500 : :
501 : 0 : update_last_login_label (self);
502 : 0 : }
503 : :
504 : : /**
505 : : * mct_user_page_new:
506 : : * @connection: (transfer none): a #GDBusConnection to use
507 : : * @policy-manager: (transfer none): a #MctManager to use
508 : : *
509 : : * Create a new #MctUserPage widget.
510 : : *
511 : : * Returns: (transfer full): a new user page
512 : : * Since: 0.14.0
513 : : */
514 : : MctUserPage *
515 : 0 : mct_user_page_new (GDBusConnection *connection,
516 : : MctManager *policy_manager)
517 : : {
518 : 0 : return g_object_new (MCT_TYPE_USER_PAGE,
519 : : "connection", connection,
520 : : "policy-manager", policy_manager,
521 : : NULL);
522 : : }
523 : :
524 : : /**
525 : : * mct_user_page_get_user:
526 : : * @self: an #MctUserPage
527 : : *
528 : : * Get the currently selected user.
529 : : *
530 : : * Returns: (transfer none): the currently selected user
531 : : * Since: 0.14.0
532 : : */
533 : : MctUser *
534 : 0 : mct_user_page_get_user (MctUserPage *self)
535 : : {
536 : 0 : g_return_val_if_fail (MCT_IS_USER_PAGE (self), NULL);
537 : :
538 : 0 : return self->user;
539 : : }
540 : :
541 : : static void
542 : 0 : user_notify_cb (GObject *object,
543 : : GParamSpec *pspec,
544 : : void *user_data)
545 : : {
546 : 0 : MctUser *user = MCT_USER (object);
547 : 0 : MctUserPage *self = MCT_USER_PAGE (user_data);
548 : :
549 : 0 : gtk_label_set_label (self->name_label, mct_user_get_display_name (user));
550 : 0 : update_last_login_label (self);
551 : 0 : }
552 : :
553 : : /**
554 : : * mct_user_page_set_user:
555 : : * @self: an #MctUserPage
556 : : * @user: an #MctUser
557 : : * @permission: (nullable) (transfer none): the #GPermission indicating whether
558 : : * the current user has permission to view or change parental controls, or
559 : : * %NULL if permission is not allowed or is unknown
560 : : *
561 : : * Set the currently selected user
562 : : *
563 : : * Since: 0.14.0
564 : : */
565 : : void
566 : 0 : mct_user_page_set_user (MctUserPage *self,
567 : : MctUser *user,
568 : : GPermission *permission)
569 : : {
570 : 0 : g_return_if_fail (MCT_IS_USER_PAGE (self));
571 : 0 : g_return_if_fail (MCT_IS_USER (user));
572 : :
573 [ # # ]: 0 : if (g_set_object (&self->user, user))
574 : : {
575 : 0 : mct_user_image_set_user (self->user_image, user);
576 : 0 : mct_user_image_set_size (self->user_image, 128);
577 : 0 : gtk_widget_set_margin_top (GTK_WIDGET (self->user_image), 24);
578 : 0 : gtk_widget_set_margin_bottom (GTK_WIDGET (self->user_image), 18);
579 : :
580 : 0 : self->user_notify_id = g_signal_connect (user,
581 : : "notify",
582 : : G_CALLBACK (user_notify_cb),
583 : : self);
584 : 0 : user_notify_cb (G_OBJECT (user), NULL, self);
585 : :
586 : 0 : update_cached_limits (self);
587 : 0 : update_cached_usage (self);
588 : :
589 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER]);
590 : : }
591 : : }
|