GCC Code Coverage Report


Directory: ./
File: panels/system/datetime/cc-datetime-page.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 397 0.0%
Functions: 0 40 0.0%
Branches: 0 184 0.0%

Line Branch Exec Source
1 /*
2 * cc-datetime-page.c
3 *
4 * Copyright (C) 2010 Intel, Inc
5 * Copyright (C) 2013 Kalev Lember <kalevlember@gmail.com>
6 * Copyright 2023 Gotam Gorabh <gautamy672@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include "config.h"
24 #include "cc-list-row.h"
25 #include "cc-time-editor.h"
26 #include "cc-datetime-page.h"
27
28 #include <langinfo.h>
29 #include <sys/time.h>
30 #include "cc-tz-dialog.h"
31 #include "timedated.h"
32 #include "date-endian.h"
33 #define GNOME_DESKTOP_USE_UNSTABLE_API
34
35 #include <gdesktop-enums.h>
36 #include "gdesktop-enums-types.h"
37 #include <string.h>
38 #include <stdlib.h>
39 #include <libintl.h>
40
41 #include <glib/gi18n.h>
42 #include <libgnome-desktop/gnome-languages.h>
43 #include <libgnome-desktop/gnome-wall-clock.h>
44 #include <polkit/polkit.h>
45
46 /* FIXME: This should be "Etc/GMT" instead */
47 #define DEFAULT_TZ "Europe/London"
48 #define GETTEXT_PACKAGE_TIMEZONES GETTEXT_PACKAGE "-timezones"
49
50 #define DATETIME_PERMISSION "org.gnome.controlcenter.datetime.configure"
51 #define DATETIME_TZ_PERMISSION "org.freedesktop.timedate1.set-timezone"
52 #define LOCATION_SETTINGS "org.gnome.system.location"
53 #define LOCATION_ENABLED "enabled"
54
55 #define CLOCK_SCHEMA "org.gnome.desktop.interface"
56 #define CLOCK_FORMAT_KEY "clock-format"
57 #define CLOCK_SHOW_WEEKDAY_KEY "clock-show-weekday"
58 #define CLOCK_SHOW_DATE_KEY "clock-show-date"
59 #define CLOCK_SHOW_SECONDS_KEY "clock-show-seconds"
60
61 #define CALENDAR_SCHEMA "org.gnome.desktop.calendar"
62 #define CALENDAR_SHOW_WEEK_NUMBERS_KEY "show-weekdate"
63
64 #define FILECHOOSER_SCHEMA "org.gtk.Settings.FileChooser"
65
66 #define DATETIME_SCHEMA "org.gnome.desktop.datetime"
67 #define AUTO_TIMEZONE_KEY "automatic-timezone"
68
69 struct _CcDateTimePage
70 {
71 AdwNavigationPage parent_instance;
72
73 GList *toplevels;
74
75 TzLocation *current_location;
76
77 GtkTreeModelFilter *city_filter;
78
79 GDateTime *date;
80
81 GSettings *clock_settings;
82 GSettings *calendar_settings;
83 GSettings *datetime_settings;
84 GSettings *filechooser_settings;
85 GDesktopClockFormat clock_format;
86 AdwActionRow *auto_datetime_row;
87 AdwSwitchRow *auto_timezone_row;
88 CcListRow *datetime_row;
89 AdwDialog *datetime_dialog;
90 AdwSpinRow *day_spin_row;
91 GtkToggleButton *twentyfour_format_button;
92 GtkToggleButton *ampm_format_button;
93 GtkSpinButton *h_spinbutton;
94 AdwSwitchRow *weekday_row;
95 AdwSwitchRow *date_row;
96 AdwSwitchRow *seconds_row;
97 AdwSwitchRow *week_numbers_row;
98 GtkLockButton *lock_button;
99 GtkListBox *date_box;
100 GtkSingleSelection *month_model;
101 GtkPopover *month_popover;
102 CcListRow *month_row;
103 GtkSwitch *network_time_switch;
104 CcTimeEditor *time_editor;
105 CcListRow *timezone_row;
106 CcTzDialog *timezone_dialog;
107 AdwSpinRow *year_spin_row;
108
109 GnomeWallClock *clock_tracker;
110
111 Timedate1 *dtm;
112 GCancellable *cancellable;
113
114 gboolean pending_ntp_state;
115
116 GPermission *permission;
117 GPermission *tz_permission;
118 GSettings *location_settings;
119
120 int month; /* index starts from 1 */
121 };
122
123 G_DEFINE_TYPE (CcDateTimePage, cc_date_time_page, ADW_TYPE_NAVIGATION_PAGE)
124
125 static void update_time (CcDateTimePage *self);
126
127 static void
128 cc_date_time_page_dispose (GObject *object)
129 {
130 CcDateTimePage *self = CC_DATE_TIME_PAGE (object);
131
132 if (self->cancellable)
133 {
134 g_cancellable_cancel (self->cancellable);
135 g_clear_object (&self->cancellable);
136 }
137
138 if (self->toplevels)
139 {
140 g_list_free_full (self->toplevels, (GDestroyNotify) adw_dialog_force_close);
141 self->toplevels = NULL;
142 }
143
144 g_clear_object (&self->clock_tracker);
145 g_clear_object (&self->dtm);
146 g_clear_object (&self->permission);
147 g_clear_object (&self->tz_permission);
148 g_clear_object (&self->location_settings);
149 g_clear_object (&self->clock_settings);
150 g_clear_object (&self->calendar_settings);
151 g_clear_object (&self->datetime_settings);
152 g_clear_object (&self->filechooser_settings);
153
154 g_clear_pointer (&self->date, g_date_time_unref);
155
156 G_OBJECT_CLASS (cc_date_time_page_parent_class)->dispose (object);
157 }
158
159 static void clock_settings_changed_cb (CcDateTimePage *self,
160 gchar *key);
161
162 static void
163 change_clock_settings (CcDateTimePage *self)
164 {
165 GDesktopClockFormat value;
166
167 g_signal_handlers_block_by_func (self->clock_settings, clock_settings_changed_cb,
168 self);
169
170 if (gtk_toggle_button_get_active (self->twentyfour_format_button))
171 value = G_DESKTOP_CLOCK_FORMAT_24H;
172 else
173 value = G_DESKTOP_CLOCK_FORMAT_12H;
174
175 g_settings_set_enum (self->clock_settings, CLOCK_FORMAT_KEY, value);
176 g_settings_set_enum (self->filechooser_settings, CLOCK_FORMAT_KEY, value);
177 self->clock_format = value;
178
179 update_time (self);
180
181 g_signal_handlers_unblock_by_func (self->clock_settings, clock_settings_changed_cb,
182 self);
183 }
184
185 static void
186 clock_settings_changed_cb (CcDateTimePage *self,
187 gchar *key)
188 {
189 GDesktopClockFormat value;
190
191 value = g_settings_get_enum (self->clock_settings, CLOCK_FORMAT_KEY);
192 self->clock_format = value;
193
194 g_signal_handlers_block_by_func (self->ampm_format_button, change_clock_settings, self);
195 g_signal_handlers_block_by_func (self->twentyfour_format_button, change_clock_settings, self);
196
197 gtk_toggle_button_set_active (self->twentyfour_format_button,
198 value == G_DESKTOP_CLOCK_FORMAT_24H);
199 gtk_toggle_button_set_active (self->ampm_format_button,
200 value == G_DESKTOP_CLOCK_FORMAT_12H);
201 cc_time_editor_set_am_pm (self->time_editor,
202 value == G_DESKTOP_CLOCK_FORMAT_12H);
203 update_time (self);
204
205 g_signal_handlers_unblock_by_func (self->twentyfour_format_button, change_clock_settings, self);
206 g_signal_handlers_unblock_by_func (self->ampm_format_button, change_clock_settings, self);
207 }
208
209 static void on_month_selection_changed_cb (CcDateTimePage *self);
210
211 static void time_changed_cb (CcDateTimePage *self,
212 CcTimeEditor *editor);
213
214 /* Update the widgets based on the system time */
215 static void
216 update_time (CcDateTimePage *self)
217 {
218 g_autofree gchar *label = NULL;
219 gboolean use_ampm;
220
221 if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_12H)
222 use_ampm = TRUE;
223 else
224 use_ampm = FALSE;
225
226 g_signal_handlers_block_by_func (self->time_editor, time_changed_cb, self);
227 cc_time_editor_set_time (self->time_editor,
228 g_date_time_get_hour (self->date),
229 g_date_time_get_minute (self->date));
230 g_signal_handlers_unblock_by_func (self->time_editor, time_changed_cb, self);
231
232 /* Update the time on the listbow row */
233 if (use_ampm)
234 {
235 /* Translators: This is the full date and time format used in 12-hour mode. */
236 label = g_date_time_format (self->date, _("%e %B %Y, %l:%M %p"));
237 }
238 else
239 {
240 /* Translators: This is the full date and time format used in 24-hour mode. */
241 label = g_date_time_format (self->date, _("%e %B %Y, %R"));
242 }
243
244 self->month = g_date_time_get_month (self->date);
245 g_signal_handlers_block_by_func (self->month_model, on_month_selection_changed_cb, self);
246 gtk_single_selection_set_selected (self->month_model, self->month - 1);
247 g_signal_handlers_unblock_by_func (self->month_model, on_month_selection_changed_cb, self);
248 cc_list_row_set_secondary_label (self->datetime_row, label);
249 }
250
251 static void
252 set_time_cb (GObject *source,
253 GAsyncResult *res,
254 gpointer user_data)
255 {
256 CcDateTimePage *self = user_data;
257 g_autoptr(GError) error = NULL;
258
259 if (!timedate1_call_set_time_finish (self->dtm,
260 res,
261 &error))
262 {
263 /* TODO: display any error in a user friendly way */
264 g_warning ("Could not set system time: %s", error->message);
265 }
266 else
267 {
268 update_time (self);
269 }
270 }
271
272 static void
273 set_timezone_cb (GObject *source,
274 GAsyncResult *res,
275 gpointer user_data)
276 {
277 CcDateTimePage *self = user_data;
278 g_autoptr(GError) error = NULL;
279
280 if (!timedate1_call_set_timezone_finish (self->dtm,
281 res,
282 &error))
283 {
284 /* TODO: display any error in a user friendly way */
285 g_warning ("Could not set system timezone: %s", error->message);
286 }
287 }
288
289 static void
290 set_using_ntp_cb (GObject *source,
291 GAsyncResult *res,
292 gpointer user_data)
293 {
294 CcDateTimePage *self = user_data;
295 g_autoptr(GError) error = NULL;
296
297 if (!timedate1_call_set_ntp_finish (self->dtm,
298 res,
299 &error))
300 {
301 /* TODO: display any error in a user friendly way */
302 g_warning ("Could not set system to use NTP: %s", error->message);
303 }
304 else
305 {
306 gtk_switch_set_state (self->network_time_switch, self->pending_ntp_state);
307 }
308 }
309
310 static void
311 queue_set_datetime (CcDateTimePage *self)
312 {
313 gint64 unixtime;
314
315 /* Don't set the time if we are using network time (NTP). */
316 if (gtk_switch_get_active (self->network_time_switch))
317 return;
318
319 /* timedated expects number of microseconds since 1 Jan 1970 UTC */
320 unixtime = g_date_time_to_unix (self->date);
321
322 timedate1_call_set_time (self->dtm,
323 unixtime * 1000000,
324 FALSE,
325 TRUE,
326 self->cancellable,
327 set_time_cb,
328 self);
329 }
330
331 static void
332 queue_set_ntp (CcDateTimePage *self,
333 gboolean using_ntp)
334 {
335 self->pending_ntp_state = using_ntp;
336 timedate1_call_set_ntp (self->dtm,
337 using_ntp,
338 TRUE,
339 self->cancellable,
340 set_using_ntp_cb,
341 self);
342 }
343
344 static void
345 queue_set_timezone (CcDateTimePage *self)
346 {
347 /* for now just do it */
348 if (self->current_location)
349 {
350 timedate1_call_set_timezone (self->dtm,
351 self->current_location->zone,
352 TRUE,
353 self->cancellable,
354 set_timezone_cb,
355 self);
356 }
357 }
358
359 static void
360 change_date (CcDateTimePage *self)
361 {
362 guint y, d;
363 g_autoptr(GDateTime) old_date = NULL;
364
365 y = (guint)adw_spin_row_get_value (self->year_spin_row);
366 d = (guint)adw_spin_row_get_value (self->day_spin_row);
367
368 old_date = self->date;
369 self->date = g_date_time_new_local (y, self->month, d,
370 g_date_time_get_hour (old_date),
371 g_date_time_get_minute (old_date),
372 g_date_time_get_second (old_date));
373 g_signal_handlers_block_by_func (self->time_editor, time_changed_cb, self);
374 cc_time_editor_set_time (self->time_editor,
375 g_date_time_get_hour (self->date),
376 g_date_time_get_minute (self->date));
377 g_signal_handlers_unblock_by_func (self->time_editor, time_changed_cb, self);
378
379 queue_set_datetime (self);
380 }
381
382 static char *
383 translated_city_name (TzLocation *loc)
384 {
385 g_autofree gchar *zone_translated = NULL;
386 g_auto(GStrv) split_translated = NULL;
387 g_autofree gchar *country = NULL;
388 gchar *name;
389 gint length;
390
391 /* Load the translation for it */
392 zone_translated = g_strdup (dgettext (GETTEXT_PACKAGE_TIMEZONES, loc->zone));
393 g_strdelimit (zone_translated, "_", ' ');
394 split_translated = g_regex_split_simple ("[\\x{2044}\\x{2215}\\x{29f8}\\x{ff0f}/]",
395 zone_translated,
396 0, 0);
397
398 length = g_strv_length (split_translated);
399
400 country = gnome_get_country_from_code (loc->country, NULL);
401 /* Translators: "city, country" */
402 name = g_strdup_printf (C_("timezone loc", "%s, %s"),
403 split_translated[length-1],
404 country);
405
406 return name;
407 }
408
409 static void
410 update_timezone (CcDateTimePage *self)
411 {
412 g_autofree gchar *city_country = NULL;
413 g_autofree gchar *label = NULL;
414
415 city_country = translated_city_name (self->current_location);
416
417 /* Update the timezone on the listbow row */
418 /* Translators: "timezone (details)" */
419 label = g_strdup_printf (C_("timezone desc", "%s (%s)"),
420 g_date_time_get_timezone_abbreviation (self->date),
421 city_country);
422 cc_list_row_set_secondary_label (self->timezone_row, label);
423 }
424
425 static void
426 get_initial_timezone (CcDateTimePage *self)
427 {
428 const gchar *timezone;
429
430 timezone = timedate1_get_timezone (self->dtm);
431
432 if (timezone == NULL ||
433 !cc_tz_dialog_set_tz (self->timezone_dialog, timezone))
434 {
435 g_warning ("Timezone '%s' is unhandled, setting %s as default", timezone ? timezone : "(null)", DEFAULT_TZ);
436 cc_tz_dialog_set_tz (self->timezone_dialog, DEFAULT_TZ);
437 }
438
439 self->current_location = cc_tz_dialog_get_selected_location (self->timezone_dialog);
440 update_timezone (self);
441 }
442
443 static void
444 day_changed (CcDateTimePage *self)
445 {
446 change_date (self);
447 }
448
449 static void
450 month_year_changed (CcDateTimePage *self)
451 {
452 guint y;
453 guint num_days;
454 GtkAdjustment *adj;
455
456 y = (guint)adw_spin_row_get_value (self->year_spin_row);
457
458 /* Check the number of days in that month */
459 num_days = g_date_get_days_in_month (self->month, y);
460
461 adj = GTK_ADJUSTMENT (adw_spin_row_get_adjustment (self->day_spin_row));
462 gtk_adjustment_set_upper (adj, num_days + 1);
463
464 if ((guint)adw_spin_row_get_value (self->day_spin_row) > num_days)
465 adw_spin_row_set_value (self->day_spin_row, num_days);
466
467 change_date (self);
468 }
469
470 static void
471 on_date_box_row_activated_cb (CcDateTimePage *self,
472 GtkListBoxRow *row)
473 {
474 g_assert (CC_IS_DATE_TIME_PAGE (self));
475
476 if (row == GTK_LIST_BOX_ROW (self->month_row))
477 gtk_popover_popup (self->month_popover);
478 }
479
480 static void
481 on_month_selection_changed_cb (CcDateTimePage *self)
482 {
483 guint i;
484
485 g_assert (CC_IS_DATE_TIME_PAGE (self));
486
487 i = gtk_single_selection_get_selected (self->month_model);
488 g_assert (i >= 0 && i < 12);
489
490 self->month = i + 1;
491 month_year_changed (self);
492
493 gtk_popover_popdown (self->month_popover);
494 }
495
496 static void
497 on_clock_changed (CcDateTimePage *self,
498 GParamSpec *pspec)
499 {
500 g_date_time_unref (self->date);
501 self->date = g_date_time_new_now_local ();
502 update_time (self);
503 update_timezone (self);
504 }
505
506 static gboolean
507 change_ntp (CcDateTimePage *self,
508 gboolean state)
509 {
510 queue_set_ntp (self, state);
511
512 /* The new state will be visible once we see the reply. */
513 return TRUE;
514 }
515
516 static gboolean
517 is_ntp_available (CcDateTimePage *self)
518 {
519 g_autoptr(GVariant) value = NULL;
520 gboolean ntp_available = TRUE;
521
522 /* We need to access this directly so that we can default to TRUE if
523 * it is not set.
524 */
525 value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self->dtm), "CanNTP");
526 if (value)
527 {
528 if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
529 ntp_available = g_variant_get_boolean (value);
530 }
531
532 return ntp_available;
533 }
534
535 static void
536 on_permission_changed (CcDateTimePage *self)
537 {
538 gboolean allowed, location_allowed, tz_allowed, auto_timezone, using_ntp;
539
540 allowed = (self->permission != NULL && g_permission_get_allowed (self->permission));
541 location_allowed = g_settings_get_boolean (self->location_settings, LOCATION_ENABLED);
542 tz_allowed = (self->tz_permission != NULL && g_permission_get_allowed (self->tz_permission));
543 using_ntp = gtk_switch_get_active (self->network_time_switch);
544 auto_timezone = adw_switch_row_get_active (self->auto_timezone_row);
545
546 /* All the widgets but the lock button and the 24h setting */
547 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_datetime_row), allowed);
548 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_timezone_row), location_allowed && (allowed || tz_allowed));
549 gtk_widget_set_sensitive (GTK_WIDGET (self->datetime_row), allowed && !using_ntp);
550 gtk_widget_set_sensitive (GTK_WIDGET (self->timezone_row), (allowed || tz_allowed) && (!auto_timezone || !location_allowed));
551
552 /* Hide the subdialogs if we no longer have permissions */
553 if (!allowed)
554 gtk_widget_set_visible (GTK_WIDGET (self->datetime_dialog), FALSE);
555 if (!allowed && !tz_allowed)
556 gtk_widget_set_visible (GTK_WIDGET (self->timezone_dialog), FALSE);
557 }
558
559 static void
560 on_location_settings_changed (CcDateTimePage *self)
561 {
562 on_permission_changed (self);
563 }
564
565 static void
566 on_can_ntp_changed (CcDateTimePage *self)
567 {
568 gtk_widget_set_visible (GTK_WIDGET (self->auto_datetime_row), is_ntp_available (self));
569 }
570
571 static void
572 on_timezone_changed (CcDateTimePage *self)
573 {
574 get_initial_timezone (self);
575 }
576
577 static void
578 on_timedated_properties_changed (CcDateTimePage *self,
579 GVariant *changed_properties,
580 const gchar **invalidated_properties)
581 {
582 guint i;
583
584 if (invalidated_properties != NULL)
585 for (i = 0; invalidated_properties[i] != NULL; i++) {
586 g_autoptr(GVariant) variant = NULL;
587 g_autoptr(GError) error = NULL;
588
589 /* See https://bugs.freedesktop.org/show_bug.cgi?id=37632 for the reason why we're doing this */
590 variant = g_dbus_proxy_call_sync (G_DBUS_PROXY (self->dtm),
591 "org.freedesktop.DBus.Properties.Get",
592 g_variant_new ("(ss)", "org.freedesktop.timedate1", invalidated_properties[i]),
593 G_DBUS_CALL_FLAGS_NONE,
594 -1,
595 NULL,
596 &error);
597 if (variant == NULL)
598 g_warning ("Failed to get property '%s': %s", invalidated_properties[i], error->message);
599 else {
600 GVariant *v;
601
602 g_variant_get (variant, "(v)", &v);
603 g_dbus_proxy_set_cached_property (G_DBUS_PROXY (self->dtm), invalidated_properties[i], v);
604 }
605 }
606 }
607
608 static void
609 present_window (CcDateTimePage *self,
610 AdwDialog *window)
611 {
612 adw_dialog_present (window, GTK_WIDGET (self));
613 }
614
615 static gboolean
616 tz_switch_to_row_transform_func (GBinding *binding,
617 const GValue *source_value,
618 GValue *target_value,
619 CcDateTimePage *self)
620 {
621 gboolean active;
622 gboolean allowed;
623 gboolean location_allowed;
624
625 active = g_value_get_boolean (source_value);
626 allowed = (self->permission != NULL && g_permission_get_allowed (self->permission)) ||
627 (self->tz_permission != NULL && g_permission_get_allowed (self->tz_permission));
628 location_allowed = g_settings_get_boolean (self->location_settings, LOCATION_ENABLED);
629
630 g_value_set_boolean (target_value, allowed && (!active || !location_allowed));
631
632 return TRUE;
633 }
634
635 static gboolean
636 switch_to_row_transform_func (GBinding *binding,
637 const GValue *source_value,
638 GValue *target_value,
639 CcDateTimePage *self)
640 {
641 gboolean active;
642 gboolean allowed;
643
644 active = g_value_get_boolean (source_value);
645 allowed = (self->permission != NULL && g_permission_get_allowed (self->permission));
646
647 g_value_set_boolean (target_value, !active && allowed);
648
649 return TRUE;
650 }
651
652 static void
653 bind_switch_to_row (CcDateTimePage *self,
654 GtkSwitch *gtkswitch,
655 GtkWidget *listrow)
656 {
657 g_object_bind_property_full (gtkswitch, "active",
658 listrow, "sensitive",
659 G_BINDING_SYNC_CREATE,
660 (GBindingTransformFunc) switch_to_row_transform_func,
661 NULL, self, NULL);
662 }
663
664 static void
665 panel_tz_selection_changed_cb (CcDateTimePage *self)
666 {
667 g_assert (CC_IS_DATE_TIME_PAGE (self));
668
669 self->current_location = cc_tz_dialog_get_selected_location (self->timezone_dialog);
670 queue_set_timezone (self);
671 }
672
673 static void
674 list_box_row_activated (CcDateTimePage *self,
675 GtkListBoxRow *row)
676
677 {
678 if (row == GTK_LIST_BOX_ROW (self->datetime_row))
679 {
680 present_window (self, self->datetime_dialog);
681 }
682 else if (row == GTK_LIST_BOX_ROW (self->timezone_row))
683 {
684 present_window (self, ADW_DIALOG (self->timezone_dialog));
685 }
686 }
687
688 static void
689 time_changed_cb (CcDateTimePage *self,
690 CcTimeEditor *editor)
691 {
692 g_autoptr(GDateTime) old_date = NULL;
693
694 g_assert (CC_IS_DATE_TIME_PAGE (self));
695 g_assert (CC_IS_TIME_EDITOR (editor));
696
697 old_date = self->date;
698 self->date = g_date_time_new_local (g_date_time_get_year (old_date),
699 g_date_time_get_month (old_date),
700 g_date_time_get_day_of_month (old_date),
701 cc_time_editor_get_hour (self->time_editor),
702 cc_time_editor_get_minute (self->time_editor),
703 g_date_time_get_second (old_date));
704
705 update_time (self);
706 queue_set_datetime (self);
707 }
708
709 static void
710 setup_datetime_dialog (CcDateTimePage *self)
711 {
712 GtkAdjustment *adjustment;
713 GdkDisplay *display;
714 g_autoptr(GtkCssProvider) provider = NULL;
715 guint num_days;
716
717 /* Big time buttons */
718 provider = gtk_css_provider_new ();
719 gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
720 ".gnome-control-center-datetime-setup-time>spinbutton,\n"
721 ".gnome-control-center-datetime-setup-time>label {\n"
722 " font-size: 250%;\n"
723 "}\n"
724 "gridview.month-grid > child {\n"
725 " background: transparent;\n"
726 "}\n"
727 ".gnome-control-center-datetime-setup-time>spinbutton>entry {\n"
728 " padding: 8px 13px;\n"
729 "}", -1);
730 display = gdk_display_get_default ();
731 gtk_style_context_add_provider_for_display (display,
732 GTK_STYLE_PROVIDER (provider),
733 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
734
735 /* Day */
736 num_days = g_date_get_days_in_month (g_date_time_get_month (self->date),
737 g_date_time_get_year (self->date));
738 adjustment = (GtkAdjustment*) gtk_adjustment_new (g_date_time_get_day_of_month (self->date), 1,
739 num_days + 1, 1, 10, 1);
740 adw_spin_row_set_adjustment (self->day_spin_row, adjustment);
741 g_signal_connect_object (G_OBJECT (self->day_spin_row), "changed",
742 G_CALLBACK (day_changed), self, G_CONNECT_SWAPPED);
743
744 /* Year */
745 adjustment = (GtkAdjustment*) gtk_adjustment_new (g_date_time_get_year (self->date),
746 1, G_MAXDOUBLE, 1,
747 10, 1);
748 adw_spin_row_set_adjustment (self->year_spin_row, adjustment);
749 g_signal_connect_object (G_OBJECT (self->year_spin_row), "changed",
750 G_CALLBACK (month_year_changed), self, G_CONNECT_SWAPPED);
751
752 /* Month */
753 self->month = g_date_time_get_month (self->date);
754 g_signal_handlers_block_by_func (self->month_model, on_month_selection_changed_cb, self);
755 gtk_single_selection_set_selected (self->month_model, self->month - 1);
756 g_signal_handlers_unblock_by_func (self->month_model, on_month_selection_changed_cb, self);
757 }
758
759 static int
760 sort_date_box (GtkListBoxRow *a,
761 GtkListBoxRow *b,
762 CcDateTimePage *self)
763 {
764 GtkListBoxRow *day_row, *month_row, *year_row;
765
766 g_assert (CC_IS_DATE_TIME_PAGE (self));
767
768 day_row = GTK_LIST_BOX_ROW (self->day_spin_row);
769 month_row = GTK_LIST_BOX_ROW (self->month_row);
770 year_row = GTK_LIST_BOX_ROW (self->year_spin_row);
771
772 switch (date_endian_get_default (FALSE)) {
773 case DATE_ENDIANESS_BIG:
774 /* year, month, day */
775 if (a == year_row || b == day_row)
776 return -1;
777 if (a == day_row || b == year_row)
778 return 1;
779
780 case DATE_ENDIANESS_LITTLE:
781 /* day, month, year */
782 if (a == day_row || b == year_row)
783 return -1;
784 if (a == year_row || b == day_row)
785 return 1;
786
787 case DATE_ENDIANESS_MIDDLE:
788 /* month, day, year */
789 if (a == month_row || b == year_row)
790 return -1;
791 if (a == year_row || b == month_row)
792 return 1;
793
794 case DATE_ENDIANESS_YDM:
795 /* year, day, month */
796 if (a == year_row || b == month_row)
797 return -1;
798 if (a == month_row || b == year_row)
799 return 1;
800 }
801
802 return 0;
803 }
804
805 static void
806 cc_date_time_page_class_init (CcDateTimePageClass *klass)
807 {
808 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
809 GObjectClass *object_class = G_OBJECT_CLASS (klass);
810
811 object_class->dispose = cc_date_time_page_dispose;
812
813 g_type_ensure (CC_TYPE_LIST_ROW);
814 g_type_ensure (CC_TYPE_TIME_EDITOR);
815 g_type_ensure (CC_TYPE_TZ_DIALOG);
816 g_type_ensure (G_DESKTOP_TYPE_DESKTOP_CLOCK_FORMAT);
817
818 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/datetime/cc-datetime-page.ui");
819
820 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, auto_datetime_row);
821 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, auto_timezone_row);
822 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, auto_timezone_row);
823 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, date_box);
824 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, datetime_row);
825 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, datetime_dialog);
826 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, day_spin_row);
827 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, twentyfour_format_button);
828 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, ampm_format_button);
829 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, weekday_row);
830 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, date_row);
831 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, seconds_row);
832 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, week_numbers_row);
833 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, lock_button);
834 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, month_model);
835 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, month_popover);
836 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, month_row);
837 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, network_time_switch);
838 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, time_editor);
839 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, timezone_row);
840 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, timezone_dialog);
841 gtk_widget_class_bind_template_child (widget_class, CcDateTimePage, year_spin_row);
842
843 gtk_widget_class_bind_template_callback (widget_class, panel_tz_selection_changed_cb);
844 gtk_widget_class_bind_template_callback (widget_class, list_box_row_activated);
845 gtk_widget_class_bind_template_callback (widget_class, change_clock_settings);
846 gtk_widget_class_bind_template_callback (widget_class, on_date_box_row_activated_cb);
847
848 bind_textdomain_codeset (GETTEXT_PACKAGE_TIMEZONES, "UTF-8");
849 }
850
851 static void
852 cc_date_time_page_init (CcDateTimePage *self)
853 {
854 g_autoptr(GError) error = NULL;
855
856 gtk_widget_init_template (GTK_WIDGET (self));
857
858 self->cancellable = g_cancellable_new ();
859 error = NULL;
860 self->dtm = timedate1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
861 G_DBUS_PROXY_FLAGS_NONE,
862 "org.freedesktop.timedate1",
863 "/org/freedesktop/timedate1",
864 self->cancellable,
865 &error);
866 if (self->dtm == NULL) {
867 g_warning ("could not get proxy for DateTimeMechanism: %s", error->message);
868 return;
869 }
870
871 gtk_list_box_set_sort_func (self->date_box,
872 (GtkListBoxSortFunc)sort_date_box,
873 self, NULL);
874 gtk_list_box_invalidate_sort (self->date_box);
875
876 /* add the lock button */
877 self->permission = polkit_permission_new_sync (DATETIME_PERMISSION, NULL, NULL, NULL);
878 self->tz_permission = polkit_permission_new_sync (DATETIME_TZ_PERMISSION, NULL, NULL, NULL);
879 if (self->permission != NULL)
880 {
881 g_signal_connect_object (self->permission, "notify",
882 G_CALLBACK (on_permission_changed), self, G_CONNECT_SWAPPED);
883 }
884 else
885 {
886 g_warning ("Your system does not have the '%s' PolicyKit files installed. Please check your installation",
887 DATETIME_PERMISSION);
888 }
889 gtk_lock_button_set_permission (GTK_LOCK_BUTTON (self->lock_button), self->permission);
890
891 self->location_settings = g_settings_new (LOCATION_SETTINGS);
892 g_signal_connect_object (self->location_settings, "changed",
893 G_CALLBACK (on_location_settings_changed), self, G_CONNECT_SWAPPED);
894 on_location_settings_changed (self);
895
896 self->date = g_date_time_new_now_local ();
897
898 /* Top level windows from GtkBuilder that need to be destroyed explicitly */
899 self->toplevels = g_list_append (self->toplevels, self->datetime_dialog);
900 self->toplevels = g_list_append (self->toplevels, self->timezone_dialog);
901
902 /* setup_timezone_dialog (self); */
903 setup_datetime_dialog (self);
904
905 /* set up network time switch */
906 bind_switch_to_row (self,
907 self->network_time_switch,
908 GTK_WIDGET (self->datetime_row));
909 g_object_bind_property (self->dtm, "ntp",
910 self->network_time_switch, "active",
911 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
912
913 g_signal_connect_object (self->network_time_switch, "state-set",
914 G_CALLBACK (change_ntp), self, G_CONNECT_SWAPPED);
915
916 gtk_widget_set_visible (GTK_WIDGET (self->auto_datetime_row), is_ntp_available (self));
917
918 /* Timezone settings */
919 g_object_bind_property_full (self->auto_timezone_row, "active",
920 self->timezone_row, "sensitive",
921 G_BINDING_SYNC_CREATE,
922 (GBindingTransformFunc) tz_switch_to_row_transform_func,
923 NULL, self, NULL);
924
925 self->datetime_settings = g_settings_new (DATETIME_SCHEMA);
926 g_settings_bind (self->datetime_settings, AUTO_TIMEZONE_KEY,
927 self->auto_timezone_row, "active",
928 G_SETTINGS_BIND_DEFAULT);
929
930 /* Clock settings */
931 self->clock_settings = g_settings_new (CLOCK_SCHEMA);
932
933 /* setup the time itself */
934 self->clock_tracker = g_object_new (GNOME_TYPE_WALL_CLOCK, NULL);
935 g_signal_connect_object (self->clock_tracker, "notify::clock", G_CALLBACK (on_clock_changed), self, G_CONNECT_SWAPPED);
936
937 clock_settings_changed_cb (self, CLOCK_FORMAT_KEY);
938 g_signal_connect_object (self->clock_settings, "changed::" CLOCK_FORMAT_KEY,
939 G_CALLBACK (clock_settings_changed_cb), self, G_CONNECT_SWAPPED);
940
941 /* setup top bar clock setting switches */
942 g_settings_bind (self->clock_settings, CLOCK_SHOW_WEEKDAY_KEY,
943 self->weekday_row, "active",
944 G_SETTINGS_BIND_DEFAULT);
945
946 g_settings_bind (self->clock_settings, CLOCK_SHOW_DATE_KEY,
947 self->date_row, "active",
948 G_SETTINGS_BIND_DEFAULT);
949
950 g_settings_bind (self->clock_settings, CLOCK_SHOW_SECONDS_KEY,
951 self->seconds_row, "active",
952 G_SETTINGS_BIND_DEFAULT);
953
954 /* Calendar settings */
955 self->calendar_settings = g_settings_new (CALENDAR_SCHEMA);
956
957 g_settings_bind (self->calendar_settings, CALENDAR_SHOW_WEEK_NUMBERS_KEY,
958 self->week_numbers_row, "active",
959 G_SETTINGS_BIND_DEFAULT);
960
961 update_time (self);
962
963 /* After the initial setup, so we can be sure that
964 * the model is filled up */
965 get_initial_timezone (self);
966
967 g_signal_connect_object (self->time_editor, "time-changed",
968 G_CALLBACK (time_changed_cb), self, G_CONNECT_SWAPPED);
969 g_signal_connect_object (self->month_model, "selection-changed",
970 G_CALLBACK (on_month_selection_changed_cb), self, G_CONNECT_SWAPPED);
971
972 /* Watch changes of timedated remote service properties */
973 g_signal_connect_object (self->dtm, "g-properties-changed",
974 G_CALLBACK (on_timedated_properties_changed), self, G_CONNECT_SWAPPED);
975 g_signal_connect_object (self->dtm, "notify::can-ntp",
976 G_CALLBACK (on_can_ntp_changed), self, G_CONNECT_SWAPPED);
977 g_signal_connect_object (self->dtm, "notify::timezone",
978 G_CALLBACK (on_timezone_changed), self, G_CONNECT_SWAPPED);
979 /* We ignore UTC <--> LocalRTC changes at the moment */
980
981 self->filechooser_settings = g_settings_new (FILECHOOSER_SCHEMA);
982 }
983