gnome-control-center/panels/datetime/cc-datetime-panel.c
Mohammed Sadiq 4b2d32e53f datetime: Fix crash when loading the panel
When the month row value is changed, the signal callback uses
the year value from the year spin button.  So set year value
before month is set so that the year is set to a valid value
thus avoiding a crash with invalid year.
2022-02-01 13:59:33 +00:00

1098 lines
36 KiB
C

/*
* Copyright (C) 2010 Intel, Inc
* Copyright (C) 2013 Kalev Lember <kalevlember@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Thomas Wood <thomas.wood@intel.com>
*
*/
#include "config.h"
#include "cc-time-editor.h"
#include "cc-datetime-panel.h"
#include "cc-datetime-resources.h"
#include <langinfo.h>
#include <sys/time.h>
#include "cc-timezone-map.h"
#include "timedated.h"
#include "date-endian.h"
#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <gdesktop-enums.h>
#include <string.h>
#include <stdlib.h>
#include <libintl.h>
#include <glib/gi18n.h>
#include <libgnome-desktop/gnome-languages.h>
#include <libgnome-desktop/gnome-wall-clock.h>
#include <polkit/polkit.h>
/* FIXME: This should be "Etc/GMT" instead */
#define DEFAULT_TZ "Europe/London"
#define GETTEXT_PACKAGE_TIMEZONES GETTEXT_PACKAGE "-timezones"
enum {
CITY_COL_CITY_HUMAN_READABLE,
CITY_COL_ZONE,
CITY_NUM_COLS
};
#define DATETIME_PERMISSION "org.gnome.controlcenter.datetime.configure"
#define DATETIME_TZ_PERMISSION "org.freedesktop.timedate1.set-timezone"
#define LOCATION_SETTINGS "org.gnome.system.location"
#define LOCATION_ENABLED "enabled"
#define CLOCK_SCHEMA "org.gnome.desktop.interface"
#define CLOCK_FORMAT_KEY "clock-format"
#define FILECHOOSER_SCHEMA "org.gtk.Settings.FileChooser"
#define DATETIME_SCHEMA "org.gnome.desktop.datetime"
#define AUTO_TIMEZONE_KEY "automatic-timezone"
struct _CcDateTimePanel
{
CcPanel parent_instance;
GtkWidget *map;
GList *toplevels;
TzLocation *current_location;
GtkTreeModelFilter *city_filter;
GDateTime *date;
GSettings *clock_settings;
GSettings *datetime_settings;
GSettings *filechooser_settings;
GDesktopClockFormat clock_format;
GtkFrame *aspectmap;
GtkWidget *auto_datetime_row;
GtkWidget *auto_timezone_row;
GtkWidget *auto_timezone_switch;
GtkListStore *city_liststore;
GtkTreeModelSort *city_modelsort;
GtkWidget *date_grid;
GtkWidget *datetime_button;
GtkWidget *datetime_dialog;
GtkWidget *datetime_label;
GtkWidget *day_spinbutton;
GtkWidget *timeformat_row;
GtkWidget *h_spinbutton;
GtkLockButton *lock_button;
GtkListBox *date_box;
GtkListBoxRow *day_row;
AdwComboRow *month_row;
GtkListBoxRow *year_row;
GtkWidget *network_time_switch;
GtkWidget *time_editor;
GtkWidget *timezone_button;
GtkWidget *timezone_dialog;
GtkWidget *timezone_label;
GtkWidget *timezone_searchentry;
GtkWidget *year_spinbutton;
GnomeWallClock *clock_tracker;
Timedate1 *dtm;
GCancellable *cancellable;
GPermission *permission;
GPermission *tz_permission;
GSettings *location_settings;
int month; /* index starts from 1 */
};
CC_PANEL_REGISTER (CcDateTimePanel, cc_date_time_panel)
static void update_time (CcDateTimePanel *self);
static void
cc_date_time_panel_dispose (GObject *object)
{
CcDateTimePanel *panel = CC_DATE_TIME_PANEL (object);
if (panel->cancellable)
{
g_cancellable_cancel (panel->cancellable);
g_clear_object (&panel->cancellable);
}
if (panel->toplevels)
{
g_list_free_full (panel->toplevels, (GDestroyNotify) gtk_window_destroy);
panel->toplevels = NULL;
}
g_clear_object (&panel->clock_tracker);
g_clear_object (&panel->dtm);
g_clear_object (&panel->permission);
g_clear_object (&panel->tz_permission);
g_clear_object (&panel->location_settings);
g_clear_object (&panel->clock_settings);
g_clear_object (&panel->datetime_settings);
g_clear_object (&panel->filechooser_settings);
g_clear_pointer (&panel->date, g_date_time_unref);
G_OBJECT_CLASS (cc_date_time_panel_parent_class)->dispose (object);
}
static const char *
cc_date_time_panel_get_help_uri (CcPanel *panel)
{
return "help:gnome-help/clock";
}
static void clock_settings_changed_cb (CcDateTimePanel *panel,
gchar *key);
static char *
format_clock_name_cb (AdwEnumListItem *item,
gpointer user_data)
{
switch (adw_enum_list_item_get_value (item))
{
case G_DESKTOP_CLOCK_FORMAT_24H:
return g_strdup (_("24-hour"));
case G_DESKTOP_CLOCK_FORMAT_12H:
return g_strdup (_("AM / PM"));
default:
return NULL;
}
}
static void
change_clock_settings (GObject *gobject,
GParamSpec *pspec,
CcDateTimePanel *self)
{
GDesktopClockFormat value;
AdwEnumListItem *item;
g_signal_handlers_block_by_func (self->clock_settings, clock_settings_changed_cb,
self);
item = ADW_ENUM_LIST_ITEM (adw_combo_row_get_selected_item (ADW_COMBO_ROW (self->timeformat_row)));
value = adw_enum_list_item_get_value (item);
g_settings_set_enum (self->clock_settings, CLOCK_FORMAT_KEY, value);
g_settings_set_enum (self->filechooser_settings, CLOCK_FORMAT_KEY, value);
self->clock_format = value;
update_time (self);
g_signal_handlers_unblock_by_func (self->clock_settings, clock_settings_changed_cb,
self);
}
static void
clock_settings_changed_cb (CcDateTimePanel *self,
gchar *key)
{
GDesktopClockFormat value;
value = g_settings_get_enum (self->clock_settings, CLOCK_FORMAT_KEY);
self->clock_format = value;
g_signal_handlers_block_by_func (self->timeformat_row, change_clock_settings, self);
adw_combo_row_set_selected (ADW_COMBO_ROW (self->timeformat_row), value);
cc_time_editor_set_am_pm (CC_TIME_EDITOR (self->time_editor),
value == G_DESKTOP_CLOCK_FORMAT_12H);
update_time (self);
g_signal_handlers_unblock_by_func (self->timeformat_row, change_clock_settings, self);
}
/* Update the widgets based on the system time */
static void
update_time (CcDateTimePanel *self)
{
g_autofree gchar *label = NULL;
gboolean use_ampm;
if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_12H)
use_ampm = TRUE;
else
use_ampm = FALSE;
cc_time_editor_set_time (CC_TIME_EDITOR (self->time_editor),
g_date_time_get_hour (self->date),
g_date_time_get_minute (self->date));
/* Update the time on the listbow row */
if (use_ampm)
{
/* Translators: This is the full date and time format used in 12-hour mode. */
label = g_date_time_format (self->date, _("%e %B %Y, %l:%M %p"));
}
else
{
/* Translators: This is the full date and time format used in 24-hour mode. */
label = g_date_time_format (self->date, _("%e %B %Y, %R"));
}
self->month = g_date_time_get_month (self->date);
adw_combo_row_set_selected (self->month_row, self->month - 1);
gtk_label_set_text (GTK_LABEL (self->datetime_label), label);
}
static void
set_time_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
CcDateTimePanel *self = user_data;
g_autoptr(GError) error = NULL;
if (!timedate1_call_set_time_finish (self->dtm,
res,
&error))
{
/* TODO: display any error in a user friendly way */
g_warning ("Could not set system time: %s", error->message);
}
else
{
update_time (self);
}
}
static void
set_timezone_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
CcDateTimePanel *self = user_data;
g_autoptr(GError) error = NULL;
if (!timedate1_call_set_timezone_finish (self->dtm,
res,
&error))
{
/* TODO: display any error in a user friendly way */
g_warning ("Could not set system timezone: %s", error->message);
}
}
static void
set_using_ntp_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
CcDateTimePanel *self = user_data;
g_autoptr(GError) error = NULL;
if (!timedate1_call_set_ntp_finish (self->dtm,
res,
&error))
{
/* TODO: display any error in a user friendly way */
g_warning ("Could not set system to use NTP: %s", error->message);
}
}
static void
queue_set_datetime (CcDateTimePanel *self)
{
gint64 unixtime;
/* timedated expects number of microseconds since 1 Jan 1970 UTC */
unixtime = g_date_time_to_unix (self->date);
timedate1_call_set_time (self->dtm,
unixtime * 1000000,
FALSE,
TRUE,
self->cancellable,
set_time_cb,
self);
}
static void
queue_set_ntp (CcDateTimePanel *self)
{
gboolean using_ntp;
/* for now just do it */
using_ntp = gtk_switch_get_active (GTK_SWITCH (self->network_time_switch));
timedate1_call_set_ntp (self->dtm,
using_ntp,
TRUE,
self->cancellable,
set_using_ntp_cb,
self);
}
static void
queue_set_timezone (CcDateTimePanel *self)
{
/* for now just do it */
if (self->current_location)
{
timedate1_call_set_timezone (self->dtm,
self->current_location->zone,
TRUE,
self->cancellable,
set_timezone_cb,
self);
}
}
static void
change_date (CcDateTimePanel *self)
{
guint y, d;
g_autoptr(GDateTime) old_date = NULL;
y = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (self->year_spinbutton));
d = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (self->day_spinbutton));
old_date = self->date;
self->date = g_date_time_new_local (y, self->month, d,
g_date_time_get_hour (old_date),
g_date_time_get_minute (old_date),
g_date_time_get_second (old_date));
cc_time_editor_set_time (CC_TIME_EDITOR (self->time_editor),
g_date_time_get_hour (self->date),
g_date_time_get_minute (self->date));
queue_set_datetime (self);
}
static gboolean
city_changed_cb (CcDateTimePanel *self,
GtkTreeModel *model,
GtkTreeIter *iter,
GtkEntryCompletion *completion)
{
GtkWidget *entry;
g_autofree gchar *zone = NULL;
gtk_tree_model_get (model, iter,
CITY_COL_ZONE, &zone, -1);
cc_timezone_map_set_timezone (CC_TIMEZONE_MAP (self->map), zone);
entry = gtk_entry_completion_get_entry (completion);
gtk_editable_set_text (GTK_EDITABLE (entry), "");
return TRUE;
}
static char *
translated_city_name (TzLocation *loc)
{
g_autofree gchar *zone_translated = NULL;
g_auto(GStrv) split_translated = NULL;
g_autofree gchar *country = NULL;
gchar *name;
gint length;
/* Load the translation for it */
zone_translated = g_strdup (dgettext (GETTEXT_PACKAGE_TIMEZONES, loc->zone));
g_strdelimit (zone_translated, "_", ' ');
split_translated = g_regex_split_simple ("[\\x{2044}\\x{2215}\\x{29f8}\\x{ff0f}/]",
zone_translated,
0, 0);
length = g_strv_length (split_translated);
country = gnome_get_country_from_code (loc->country, NULL);
/* Translators: "city, country" */
name = g_strdup_printf (C_("timezone loc", "%s, %s"),
split_translated[length-1],
country);
return name;
}
static void
update_timezone (CcDateTimePanel *self)
{
g_autofree gchar *bubble_text = NULL;
g_autofree gchar *city_country = NULL;
g_autofree gchar *label = NULL;
g_autofree gchar *time_label = NULL;
g_autofree gchar *utc_label = NULL;
g_autofree gchar *tz_desc = NULL;
gboolean use_ampm;
if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_12H)
use_ampm = TRUE;
else
use_ampm = FALSE;
city_country = translated_city_name (self->current_location);
/* Update the timezone on the listbow row */
/* Translators: "timezone (details)" */
label = g_strdup_printf (C_("timezone desc", "%s (%s)"),
g_date_time_get_timezone_abbreviation (self->date),
city_country);
gtk_label_set_text (GTK_LABEL (self->timezone_label), label);
/* Translators: UTC here means the Coordinated Universal Time.
* %:::z will be replaced by the offset from UTC e.g. UTC+02 */
utc_label = g_date_time_format (self->date, _("UTC%:::z"));
if (use_ampm)
{
/* Translators: This is the time format used in 12-hour mode. */
time_label = g_date_time_format (self->date, _("%l:%M %p"));
}
else
{
/* Translators: This is the time format used in 24-hour mode. */
time_label = g_date_time_format (self->date, _("%R"));
}
/* Update the text bubble in the timezone map */
/* Translators: "timezone (utc shift)" */
tz_desc = g_strdup_printf (C_("timezone map", "%s (%s)"),
g_date_time_get_timezone_abbreviation (self->date),
utc_label);
bubble_text = g_strdup_printf ("<b>%s</b>\n"
"<small>%s</small>\n"
"<b>%s</b>",
tz_desc,
city_country,
time_label);
cc_timezone_map_set_bubble_text (CC_TIMEZONE_MAP (self->map), bubble_text);
}
static void
location_changed_cb (CcDateTimePanel *self,
TzLocation *location)
{
g_autoptr(GDateTime) old_date = NULL;
g_autoptr(GTimeZone) timezone = NULL;
g_debug ("location changed to %s/%s", location->country, location->zone);
self->current_location = location;
timezone = g_time_zone_new_identifier (location->zone);
if (!timezone)
{
g_warning ("Could not find timezone \"%s\", using UTC instead", location->zone);
timezone = g_time_zone_new_utc ();
}
old_date = self->date;
self->date = g_date_time_to_timezone (old_date, timezone);
cc_time_editor_set_time (CC_TIME_EDITOR (self->time_editor),
g_date_time_get_hour (self->date),
g_date_time_get_minute (self->date));
update_timezone (self);
queue_set_timezone (self);
}
static void
get_initial_timezone (CcDateTimePanel *self)
{
const gchar *timezone;
timezone = timedate1_get_timezone (self->dtm);
if (timezone == NULL ||
!cc_timezone_map_set_timezone (CC_TIMEZONE_MAP (self->map), timezone))
{
g_warning ("Timezone '%s' is unhandled, setting %s as default", timezone ? timezone : "(null)", DEFAULT_TZ);
cc_timezone_map_set_timezone (CC_TIMEZONE_MAP (self->map), DEFAULT_TZ);
}
self->current_location = cc_timezone_map_get_location (CC_TIMEZONE_MAP (self->map));
update_timezone (self);
}
static void
load_cities (TzLocation *loc,
GtkListStore *city_store)
{
g_autofree gchar *human_readable = NULL;
human_readable = translated_city_name (loc);
gtk_list_store_insert_with_values (city_store, NULL, 0,
CITY_COL_CITY_HUMAN_READABLE, human_readable,
CITY_COL_ZONE, loc->zone,
-1);
}
static void
load_regions_model (GtkListStore *cities)
{
g_autoptr(TzDB) db = NULL;
db = tz_load_db ();
g_ptr_array_foreach (db->locations, (GFunc) load_cities, cities);
}
static void
day_changed (CcDateTimePanel *panel)
{
change_date (panel);
}
static void
month_year_changed (CcDateTimePanel *self)
{
guint y;
guint num_days;
GtkAdjustment *adj;
GtkSpinButton *day_spin;
y = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (self->year_spinbutton));
/* Check the number of days in that month */
num_days = g_date_get_days_in_month (self->month, y);
day_spin = GTK_SPIN_BUTTON (self->day_spinbutton);
adj = GTK_ADJUSTMENT (gtk_spin_button_get_adjustment (day_spin));
gtk_adjustment_set_upper (adj, num_days + 1);
if (gtk_spin_button_get_value_as_int (day_spin) > num_days)
gtk_spin_button_set_value (day_spin, num_days);
change_date (self);
}
static void
on_month_row_selected_changed_cb (AdwComboRow *month_row,
GParamSpec *pspec,
CcDateTimePanel *self)
{
unsigned int i;
g_assert (CC_IS_DATE_TIME_PANEL (self));
g_assert (ADW_IS_COMBO_ROW (month_row));
i = adw_combo_row_get_selected (month_row);
g_assert (i >= 0 && i < 12);
self->month = i + 1;
month_year_changed (self);
}
static void
on_clock_changed (CcDateTimePanel *panel,
GParamSpec *pspec)
{
g_date_time_unref (panel->date);
panel->date = g_date_time_new_now_local ();
update_time (panel);
update_timezone (panel);
}
static gboolean
change_ntp (CcDateTimePanel *self)
{
queue_set_ntp (self);
/* The new state will be visible once we see the reply. */
return TRUE;
}
static void
on_ntp_changed (CcDateTimePanel *self)
{
gboolean ntp_on;
g_object_get (self->dtm, "ntp", &ntp_on, NULL);
g_signal_handlers_block_by_func (self->network_time_switch, change_ntp, self);
g_object_set (self->network_time_switch,
"state", ntp_on,
NULL);
g_signal_handlers_unblock_by_func (self->network_time_switch, change_ntp, self);
}
static gboolean
is_ntp_available (CcDateTimePanel *self)
{
g_autoptr(GVariant) value = NULL;
gboolean ntp_available = TRUE;
/* We need to access this directly so that we can default to TRUE if
* it is not set.
*/
value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self->dtm), "CanNTP");
if (value)
{
if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
ntp_available = g_variant_get_boolean (value);
}
return ntp_available;
}
static void
on_permission_changed (CcDateTimePanel *self)
{
gboolean allowed, location_allowed, tz_allowed, auto_timezone, using_ntp;
allowed = (self->permission != NULL && g_permission_get_allowed (self->permission));
location_allowed = g_settings_get_boolean (self->location_settings, LOCATION_ENABLED);
tz_allowed = (self->tz_permission != NULL && g_permission_get_allowed (self->tz_permission));
using_ntp = gtk_switch_get_active (GTK_SWITCH (self->network_time_switch));
auto_timezone = gtk_switch_get_active (GTK_SWITCH (self->auto_timezone_switch));
/* All the widgets but the lock button and the 24h setting */
gtk_widget_set_sensitive (self->auto_datetime_row, allowed);
gtk_widget_set_sensitive (self->auto_timezone_row, location_allowed && (allowed || tz_allowed));
gtk_widget_set_sensitive (self->datetime_button, allowed && !using_ntp);
gtk_widget_set_sensitive (self->timezone_button, (allowed || tz_allowed) && (!auto_timezone || !location_allowed));
/* Hide the subdialogs if we no longer have permissions */
if (!allowed)
gtk_widget_hide (GTK_WIDGET (self->datetime_dialog));
if (!allowed && !tz_allowed)
gtk_widget_hide (GTK_WIDGET (self->timezone_dialog));
}
static void
on_location_settings_changed (CcDateTimePanel *panel)
{
on_permission_changed (panel);
}
static void
on_can_ntp_changed (CcDateTimePanel *self)
{
gtk_widget_set_visible (self->auto_datetime_row, is_ntp_available (self));
}
static void
on_timezone_changed (CcDateTimePanel *self)
{
g_signal_handlers_block_by_func (self->map, location_changed_cb, self);
get_initial_timezone (self);
g_signal_handlers_unblock_by_func (self->map, location_changed_cb, self);
}
static void
on_timedated_properties_changed (CcDateTimePanel *self,
GVariant *changed_properties,
const gchar **invalidated_properties)
{
guint i;
if (invalidated_properties != NULL)
for (i = 0; invalidated_properties[i] != NULL; i++) {
g_autoptr(GVariant) variant = NULL;
g_autoptr(GError) error = NULL;
/* See https://bugs.freedesktop.org/show_bug.cgi?id=37632 for the reason why we're doing this */
variant = g_dbus_proxy_call_sync (G_DBUS_PROXY (self->dtm),
"org.freedesktop.DBus.Properties.Get",
g_variant_new ("(ss)", "org.freedesktop.timedate1", invalidated_properties[i]),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (variant == NULL)
g_warning ("Failed to get property '%s': %s", invalidated_properties[i], error->message);
else {
GVariant *v;
g_variant_get (variant, "(v)", &v);
g_dbus_proxy_set_cached_property (G_DBUS_PROXY (self->dtm), invalidated_properties[i], v);
}
}
}
static void
run_dialog (CcDateTimePanel *self,
GtkWidget *dialog)
{
GtkWidget *parent;
parent = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
gtk_window_present (GTK_WINDOW (dialog));
}
static gboolean
tz_switch_to_row_transform_func (GBinding *binding,
const GValue *source_value,
GValue *target_value,
CcDateTimePanel *self)
{
gboolean active;
gboolean allowed;
gboolean location_allowed;
active = g_value_get_boolean (source_value);
allowed = (self->permission != NULL && g_permission_get_allowed (self->permission)) ||
(self->tz_permission != NULL && g_permission_get_allowed (self->tz_permission));
location_allowed = g_settings_get_boolean (self->location_settings, LOCATION_ENABLED);
g_value_set_boolean (target_value, allowed && (!active || !location_allowed));
return TRUE;
}
static gboolean
switch_to_row_transform_func (GBinding *binding,
const GValue *source_value,
GValue *target_value,
CcDateTimePanel *self)
{
gboolean active;
gboolean allowed;
active = g_value_get_boolean (source_value);
allowed = (self->permission != NULL && g_permission_get_allowed (self->permission));
g_value_set_boolean (target_value, !active && allowed);
return TRUE;
}
static void
bind_switch_to_row (CcDateTimePanel *self,
GtkWidget *gtkswitch,
GtkWidget *listrow)
{
g_object_bind_property_full (gtkswitch, "active",
listrow, "sensitive",
G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) switch_to_row_transform_func,
NULL, self, NULL);
}
static void
list_box_row_activated (CcDateTimePanel *self,
GtkListBoxRow *row)
{
if (row == GTK_LIST_BOX_ROW (self->datetime_button))
{
run_dialog (self, self->datetime_dialog);
}
else if (row == GTK_LIST_BOX_ROW (self->timezone_button))
{
run_dialog (self, self->timezone_dialog);
}
}
static void
time_changed_cb (CcDateTimePanel *self,
CcTimeEditor *editor)
{
g_autoptr(GDateTime) old_date = NULL;
g_assert (CC_IS_DATE_TIME_PANEL (self));
g_assert (CC_IS_TIME_EDITOR (editor));
old_date = self->date;
self->date = g_date_time_new_local (g_date_time_get_year (old_date),
g_date_time_get_month (old_date),
g_date_time_get_day_of_month (old_date),
cc_time_editor_get_hour (CC_TIME_EDITOR (self->time_editor)),
cc_time_editor_get_minute (CC_TIME_EDITOR (self->time_editor)),
g_date_time_get_second (old_date));
update_time (self);
queue_set_datetime (self);
}
static void
setup_timezone_dialog (CcDateTimePanel *self)
{
g_autoptr(GtkEntryCompletion) completion = NULL;
/* set up timezone map */
self->map = (GtkWidget *) cc_timezone_map_new ();
gtk_frame_set_child (self->aspectmap, self->map);
/* Create the completion object */
completion = gtk_entry_completion_new ();
gtk_entry_set_completion (GTK_ENTRY (self->timezone_searchentry), completion);
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (self->city_modelsort));
gtk_entry_completion_set_text_column (completion, CITY_COL_CITY_HUMAN_READABLE);
}
static void
setup_datetime_dialog (CcDateTimePanel *self)
{
GtkAdjustment *adjustment;
GdkDisplay *display;
g_autoptr(GtkCssProvider) provider = NULL;
guint num_days;
/* Big time buttons */
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
".gnome-control-center-datetime-setup-time>spinbutton,\n"
".gnome-control-center-datetime-setup-time>label {\n"
" font-size: 250%;\n"
"}\n"
".gnome-control-center-datetime-setup-time>spinbutton>entry {\n"
" padding: 8px 13px;\n"
"}", -1);
display = gdk_display_get_default ();
gtk_style_context_add_provider_for_display (display,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
/* Day */
num_days = g_date_get_days_in_month (g_date_time_get_month (self->date),
g_date_time_get_year (self->date));
adjustment = (GtkAdjustment*) gtk_adjustment_new (g_date_time_get_day_of_month (self->date), 1,
num_days + 1, 1, 10, 1);
gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (self->day_spinbutton),
adjustment);
g_signal_connect_object (G_OBJECT (self->day_spinbutton), "value-changed",
G_CALLBACK (day_changed), self, G_CONNECT_SWAPPED);
/* Year */
adjustment = (GtkAdjustment*) gtk_adjustment_new (g_date_time_get_year (self->date),
1, G_MAXDOUBLE, 1,
10, 1);
gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (self->year_spinbutton),
adjustment);
g_signal_connect_object (G_OBJECT (self->year_spinbutton), "value-changed",
G_CALLBACK (month_year_changed), self, G_CONNECT_SWAPPED);
/* Month */
self->month = g_date_time_get_month (self->date);
adw_combo_row_set_selected (self->month_row, self->month - 1);
}
static int
sort_date_box (GtkListBoxRow *a,
GtkListBoxRow *b,
CcDateTimePanel *self)
{
GtkListBoxRow *month_row;
g_assert (CC_IS_DATE_TIME_PANEL (self));
month_row = GTK_LIST_BOX_ROW (self->month_row);
switch (date_endian_get_default (FALSE)) {
case DATE_ENDIANESS_BIG:
/* year, month, day */
if (a == self->year_row || b == self->day_row)
return -1;
if (a == self->day_row || b == self->year_row)
return 1;
case DATE_ENDIANESS_LITTLE:
/* day, month, year */
if (a == self->day_row || b == self->year_row)
return -1;
if (a == self->year_row || b == self->day_row)
return 1;
case DATE_ENDIANESS_MIDDLE:
/* month, day, year */
if (a == month_row || b == self->year_row)
return -1;
if (a == self->year_row || b == month_row)
return 1;
case DATE_ENDIANESS_YDM:
/* year, day, month */
if (a == self->year_row || b == month_row)
return -1;
if (a == month_row || b == self->year_row)
return 1;
}
return 0;
}
static void
cc_date_time_panel_class_init (CcDateTimePanelClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
object_class->dispose = cc_date_time_panel_dispose;
panel_class->get_help_uri = cc_date_time_panel_get_help_uri;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/datetime/cc-datetime-panel.ui");
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, aspectmap);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, auto_datetime_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, auto_timezone_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, auto_timezone_switch);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, city_liststore);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, city_modelsort);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, date_box);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, datetime_button);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, datetime_dialog);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, datetime_label);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, day_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, day_spinbutton);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, timeformat_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, lock_button);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, month_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, network_time_switch);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, time_editor);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, timezone_button);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, timezone_dialog);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, timezone_label);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, timezone_searchentry);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, year_row);
gtk_widget_class_bind_template_child (widget_class, CcDateTimePanel, year_spinbutton);
gtk_widget_class_bind_template_callback (widget_class, list_box_row_activated);
gtk_widget_class_bind_template_callback (widget_class, time_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, change_clock_settings);
gtk_widget_class_bind_template_callback (widget_class, format_clock_name_cb);
gtk_widget_class_bind_template_callback (widget_class, on_month_row_selected_changed_cb);
bind_textdomain_codeset (GETTEXT_PACKAGE_TIMEZONES, "UTF-8");
g_type_ensure (CC_TYPE_TIME_EDITOR);
}
static void
cc_date_time_panel_init (CcDateTimePanel *self)
{
g_autoptr(GError) error = NULL;
g_resources_register (cc_datetime_get_resource ());
gtk_widget_init_template (GTK_WIDGET (self));
self->cancellable = g_cancellable_new ();
error = NULL;
self->dtm = timedate1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
self->cancellable,
&error);
if (self->dtm == NULL) {
g_warning ("could not get proxy for DateTimeMechanism: %s", error->message);
return;
}
gtk_list_box_set_sort_func (self->date_box,
(GtkListBoxSortFunc)sort_date_box,
self, NULL);
gtk_list_box_invalidate_sort (self->date_box);
/* add the lock button */
self->permission = polkit_permission_new_sync (DATETIME_PERMISSION, NULL, NULL, NULL);
self->tz_permission = polkit_permission_new_sync (DATETIME_TZ_PERMISSION, NULL, NULL, NULL);
if (self->permission != NULL)
{
g_signal_connect_object (self->permission, "notify",
G_CALLBACK (on_permission_changed), self, G_CONNECT_SWAPPED);
}
else
{
g_warning ("Your system does not have the '%s' PolicyKit files installed. Please check your installation",
DATETIME_PERMISSION);
}
gtk_lock_button_set_permission (GTK_LOCK_BUTTON (self->lock_button), self->permission);
self->location_settings = g_settings_new (LOCATION_SETTINGS);
g_signal_connect_object (self->location_settings, "changed",
G_CALLBACK (on_location_settings_changed), self, G_CONNECT_SWAPPED);
on_location_settings_changed (self);
self->date = g_date_time_new_now_local ();
/* Top level windows from GtkBuilder that need to be destroyed explicitly */
self->toplevels = g_list_append (self->toplevels, self->datetime_dialog);
self->toplevels = g_list_append (self->toplevels, self->timezone_dialog);
setup_timezone_dialog (self);
setup_datetime_dialog (self);
/* set up network time switch */
bind_switch_to_row (self,
self->network_time_switch,
self->datetime_button);
g_signal_connect_object (self->dtm, "notify::ntp",
G_CALLBACK (on_ntp_changed), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->network_time_switch, "state-set",
G_CALLBACK (change_ntp), self, G_CONNECT_SWAPPED);
on_ntp_changed (self);
gtk_widget_set_visible (self->auto_datetime_row, is_ntp_available (self));
/* Timezone settings */
g_object_bind_property_full (self->auto_timezone_switch, "active",
self->timezone_button, "sensitive",
G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) tz_switch_to_row_transform_func,
NULL, self, NULL);
self->datetime_settings = g_settings_new (DATETIME_SCHEMA);
g_settings_bind (self->datetime_settings, AUTO_TIMEZONE_KEY,
self->auto_timezone_switch, "active",
G_SETTINGS_BIND_DEFAULT);
/* Clock settings */
self->clock_settings = g_settings_new (CLOCK_SCHEMA);
/* setup the time itself */
self->clock_tracker = g_object_new (GNOME_TYPE_WALL_CLOCK, NULL);
g_signal_connect_object (self->clock_tracker, "notify::clock", G_CALLBACK (on_clock_changed), self, G_CONNECT_SWAPPED);
clock_settings_changed_cb (self, CLOCK_FORMAT_KEY);
g_signal_connect_object (self->clock_settings, "changed::" CLOCK_FORMAT_KEY,
G_CALLBACK (clock_settings_changed_cb), self, G_CONNECT_SWAPPED);
update_time (self);
load_regions_model (GTK_LIST_STORE (self->city_liststore));
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->city_modelsort), CITY_COL_CITY_HUMAN_READABLE,
GTK_SORT_ASCENDING);
/* After the initial setup, so we can be sure that
* the model is filled up */
get_initial_timezone (self);
g_signal_connect_object (gtk_entry_get_completion (GTK_ENTRY (self->timezone_searchentry)),
"match-selected", G_CALLBACK (city_changed_cb), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->map, "location-changed",
G_CALLBACK (location_changed_cb), self, G_CONNECT_SWAPPED);
/* Watch changes of timedated remote service properties */
g_signal_connect_object (self->dtm, "g-properties-changed",
G_CALLBACK (on_timedated_properties_changed), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->dtm, "notify::can-ntp",
G_CALLBACK (on_can_ntp_changed), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->dtm, "notify::timezone",
G_CALLBACK (on_timezone_changed), self, G_CONNECT_SWAPPED);
/* We ignore UTC <--> LocalRTC changes at the moment */
self->filechooser_settings = g_settings_new (FILECHOOSER_SCHEMA);
}