diff --git a/src/Config.vala.in b/src/Config.vala.in
index 184e5218..550688d7 100644
--- a/src/Config.vala.in
+++ b/src/Config.vala.in
@@ -1,7 +1,7 @@
namespace Build {
- public const string GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@";
- public const string LANG_LIST = "@LANG_LIST@";
- public const string PREFERRED_LANG_LIST = "@PREFERRED_LANG_LIST@";
- public const string XKB_BASE = "@XKB_BASE@";
- public const string ISO_CODES_LOCATION = "@ISO_CODES_LOCATION@";
+ public const string GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@";
+ public const string LANG_LIST = "@LANG_LIST@";
+ public const string PREFERRED_LANG_LIST = "@PREFERRED_LANG_LIST@";
+ public const string XKB_BASE = "@XKB_BASE@";
+ public const string ISO_CODES_LOCATION = "@ISO_CODES_LOCATION@";
}
diff --git a/src/Helpers/AccountsServiceInterface.vala b/src/Helpers/AccountsServiceInterface.vala
index 0178288e..075d73be 100644
--- a/src/Helpers/AccountsServiceInterface.vala
+++ b/src/Helpers/AccountsServiceInterface.vala
@@ -7,6 +7,7 @@ public interface Installer.AccountsService : Object {
public abstract KeyboardLayout[] keyboard_layouts { owned get; set; }
public abstract uint active_keyboard_layout { get; set; }
+ public abstract string clock_format { owned get; set; }
public abstract bool left_handed { get; set; }
}
diff --git a/src/Helpers/LocationHelper.vala b/src/Helpers/LocationHelper.vala
new file mode 100644
index 00000000..155c9c76
--- /dev/null
+++ b/src/Helpers/LocationHelper.vala
@@ -0,0 +1,164 @@
+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
+/*-
+ * Copyright (c) 2014 Pantheon Developers (http://launchpad.net/switchboard-plug-datetime)
+ *
+ * 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 3 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 .
+ *
+ * Authored by: Corentin Noël
+ */
+
+public class LocationHelper : GLib.Object {
+ [DBus (name = "org.freedesktop.timedate1")]
+ public interface DateTime1 : Object {
+ public abstract string Timezone {public owned get;}
+ public abstract bool LocalRTC {public get;}
+ public abstract bool CanNTP {public get;}
+ public abstract bool NTP {public get;}
+
+ //usec_utc expects number of microseconds since 1 Jan 1970 UTC
+ public abstract void set_time (int64 usec_utc, bool relative, bool user_interaction) throws GLib.Error;
+ public abstract void set_timezone (string timezone, bool user_interaction) throws GLib.Error;
+ public abstract void SetLocalRTC (bool local_rtc, bool fix_system, bool user_interaction) throws GLib.Error; //vala-lint=naming-convention
+ public abstract void SetNTP (bool use_ntp, bool user_interaction) throws GLib.Error; //vala-lint=naming-convention
+ }
+
+ private static List lines;
+ private static LocationHelper? helper = null;
+
+ public static LocationHelper get_default () {
+ if (helper == null)
+ helper = new LocationHelper ();
+ return helper;
+ }
+ private LocationHelper () {
+ var file = File.new_for_path ("/usr/share/zoneinfo/zone.tab");
+ if (!file.query_exists ()) {
+ critical ("/usr/share/zoneinfo/zone.tab doesn't exist !");
+ return;
+ }
+
+ lines = new List ();
+ try {
+ var dis = new DataInputStream (file.read ());
+ string line;
+ while ((line = dis.read_line (null)) != null) {
+ if (line.has_prefix ("#")) {
+ continue;
+ }
+
+ lines.append (line);
+ }
+ } catch (Error e) {
+ critical (e.message);
+ }
+#if GENERATE
+ generate_translation_template ();
+#endif
+ }
+
+ public HashTable get_timezones_from_continent (string continent) {
+ var timezones = new HashTable (str_hash, str_equal);
+ foreach (var line in lines) {
+ var items = line.split ("\t", 4);
+ string value = items[2];
+ if (value.has_prefix (continent) == false)
+ continue;
+
+ string tz_name_field;
+ // Take the original English string if there is something wrong with the translation
+ if (_(items[2]) == null || _(items[2]) == "") {
+ tz_name_field = items[2];
+ } else {
+ tz_name_field = _(items[2]);
+ }
+
+ string city = tz_name_field.split ("/", 2)[1];
+ if (city != null && city != "") {
+ string key = format_city (city);
+ if (items[3] != null && items[3] != "") {
+ if (items[3] != "mainland" && items[3] != "most locations" && _(items[3]) != key) {
+ key = "%s - %s".printf (key, format_city (_(items[3])));
+ }
+ }
+
+ timezones.set (key, value);
+ }
+ }
+
+ return timezones;
+ }
+
+ public string? get_countrycode_from_timezone (string timezone) {
+ foreach (var line in lines) {
+ var items = line.split ("\t", 4);
+ string value = items[2];
+
+ if (value == timezone) {
+ return items[0];
+ }
+ }
+
+ return null;
+ }
+
+ public HashTable get_locations () {
+ var locations = new HashTable (str_hash, str_equal);
+ foreach (var line in lines) {
+ var items = line.split ("\t", 4);
+ string key = items[1];
+ string value = items[2];
+ locations.set (key, value);
+ }
+
+ return locations;
+ }
+
+ public static string format_city (string city) {
+ return city.replace ("_", " ").replace ("/", ", ");
+ }
+
+ public static string get_clock_format () {
+ var t_fmt = Posix.nl_langinfo (Posix.NLItem.T_FMT);
+
+ if (t_fmt.contains ("%r") || t_fmt.contains ("%l") || t_fmt.contains ("%I")) {
+ return "12h";
+ }
+
+ return "24h";
+ }
+
+#if GENERATE
+ public void generate_translation_template () {
+ var file = GLib.File.new_for_path (GLib.Environment.get_home_dir () + "/Translations.vala");
+ try {
+ var dos = new GLib.DataOutputStream (file.create (GLib.FileCreateFlags.REPLACE_DESTINATION));
+ dos.put_string ("#if 0\n");
+ foreach (var line in lines) {
+ var items = line.split ("\t", 4);
+ string key = items[2];
+ string comment = items[3];
+ dos.put_string ("///Translators: Secondary \"/\" and all \"_\" will be replaced by \", \" and \" \".\n");
+ dos.put_string ("_(\""+ key + "\");\n");
+ if (comment != null && comment != "") {
+ dos.put_string ("///Translators: Comment for Timezone %s\n".printf (key));
+ dos.put_string ("_(\""+ comment + "\");\n");
+ }
+ }
+ dos.put_string ("#endif\n");
+ } catch (Error e) {
+ critical (e.message);
+ }
+ }
+#endif
+}
diff --git a/src/MainWindow.vala b/src/MainWindow.vala
index 0123fb1d..907ff5c1 100644
--- a/src/MainWindow.vala
+++ b/src/MainWindow.vala
@@ -22,6 +22,7 @@ public class Installer.MainWindow : Hdy.Window {
private AccountView account_view;
private LanguageView language_view;
+ private LocationView location_view;
private KeyboardLayoutView keyboard_layout_view;
private NetworkView network_view;
@@ -82,20 +83,69 @@ public class Installer.MainWindow : Hdy.Window {
deck.add (network_view);
deck.visible_child = network_view;
- network_view.next_step.connect (load_account_view);
+ network_view.next_step.connect (load_location_view);
} else {
- load_account_view ();
+ load_location_view ();
}
}
+ private void load_location_view () {
+ if (location_view != null) {
+ location_view.destroy ();
+ }
+
+ location_view = new LocationView ();
+ deck.add (location_view);
+ deck.visible_child = location_view;
+
+ location_view.next_step.connect (() => load_account_view ());
+ }
+
private void load_account_view () {
if (account_view != null) {
account_view.destroy ();
}
account_view = new AccountView ();
-
deck.add (account_view);
deck.visible_child = account_view;
}
+
+ private async void set_timezone () {
+ try {
+ LocationHelper.DateTime1 datetime1 = yield Bus.get_proxy (BusType.SYSTEM, "org.freedesktop.timedate1", "/org/freedesktop/timedate1");
+ unowned Configuration configuration = Configuration.get_default ();
+ datetime1.set_timezone (configuration.timezone, true);
+ } catch (Error e) {
+ warning (e.message);
+ }
+ }
+
+ private async void set_clock_format () {
+ AccountsService accounts_service = null;
+
+ try {
+ var act_service = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM,
+ "org.freedesktop.Accounts",
+ "/org/freedesktop/Accounts");
+ var user_path = act_service.find_user_by_name (account_view.created.user_name);
+
+ accounts_service = yield GLib.Bus.get_proxy (GLib.BusType.SYSTEM,
+ "org.freedesktop.Accounts",
+ user_path,
+ GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
+ } catch (Error e) {
+ warning ("Unable to get AccountsService proxy, clock format on new user may be incorrect: %s", e.message);
+ }
+
+ if (accounts_service != null) {
+ accounts_service.clock_format = Configuration.get_default ().clock_format;
+ }
+ }
+
+ private async void set_settings () {
+ yield set_accounts_service_settings ();
+ yield set_timezone ();
+ yield set_clock_format ();
+ }
}
diff --git a/src/Objects/Configuration.vala b/src/Objects/Configuration.vala
index dd8c74a9..d73d9ec3 100644
--- a/src/Objects/Configuration.vala
+++ b/src/Objects/Configuration.vala
@@ -32,5 +32,7 @@ public class Configuration : GLib.Object {
public string? country { get; set; default = null; }
public InitialSetup.KeyboardLayout keyboard_layout { get; set; }
public InitialSetup.KeyboardVariant? keyboard_variant { get; set; default = null; }
+ public string timezone { get; set; }
+ public string clock_format { get; set; default = "24h"; }
public bool left_handed { get; set; }
}
diff --git a/src/Objects/LocationLayout.vala b/src/Objects/LocationLayout.vala
new file mode 100644
index 00000000..c36bf046
--- /dev/null
+++ b/src/Objects/LocationLayout.vala
@@ -0,0 +1,87 @@
+/*-
+ * Copyright 2019 elementary, Inc (https://elementary.io)
+ *
+ * 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 3 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 .
+ *
+ * Authored by: Corentin Noël
+ */
+
+public class InitialSetup.LocationLayout : GLib.Object {
+ public string name { get; construct; }
+ public string original_name { get; construct; }
+ public string display_name {
+ get {
+ return name;
+ }
+ }
+
+ private GLib.ListStore variants_store;
+
+ public LocationLayout (string name, string original_name) {
+ Object (name: name, original_name: original_name);
+ }
+
+ construct {
+ variants_store = new GLib.ListStore (typeof (LocationVariant));
+ variants_store.append (new LocationVariant (this, null, null));
+ }
+
+ public void add_variant (string name, string original_name) {
+ var variant = new LocationVariant (this, name, original_name);
+ variants_store.insert_sorted (variant, (GLib.CompareDataFunc) LocationVariant.compare);
+ }
+
+ public bool has_variants () {
+ return variants_store.get_n_items () > 1;
+ }
+
+ public unowned GLib.ListStore get_variants () {
+ return variants_store;
+ }
+
+ public GLib.Variant to_gsd_variant () {
+ return new GLib.Variant ("(ss)", "xkb", name);
+ }
+
+ public static int compare (LocationLayout a, LocationLayout b) {
+ return a.display_name.collate (b.display_name);
+ }
+
+ public static GLib.ListStore get_all () {
+ var layout_store = new GLib.ListStore (typeof (LocationLayout));
+
+ var continents = new List ();
+ continents.append ("Africa");
+ continents.append ("America");
+ continents.append ("Antarctica");
+ continents.append ("Asia");
+ continents.append ("Atlantic");
+ continents.append ("Australia");
+ continents.append ("Europe");
+ continents.append ("Indian");
+ continents.append ("Pacific");
+
+ continents.foreach ((continent) => {
+ var layout = new LocationLayout (_(continent), continent);
+ layout_store.insert_sorted (layout, (GLib.CompareDataFunc) LocationLayout.compare);
+
+ var timezones = LocationHelper.get_default ().get_timezones_from_continent (continent);
+ timezones.foreach ((city, timezone) => {
+ layout.add_variant (_(city), timezone);
+ });
+ });
+
+ return layout_store;
+ }
+}
diff --git a/src/Objects/LocationVariant.vala b/src/Objects/LocationVariant.vala
new file mode 100644
index 00000000..4672453c
--- /dev/null
+++ b/src/Objects/LocationVariant.vala
@@ -0,0 +1,53 @@
+/*-
+ * Copyright 2019 elementary, Inc (https://elementary.io)
+ *
+ * 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 3 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 .
+ *
+ * Authored by: Corentin Noël
+ */
+
+public class InitialSetup.LocationVariant : GLib.Object {
+ public unowned LocationLayout layout { get; construct; }
+ public string? name { get; construct; }
+ public string? original_name { get; construct; }
+ public string display_name {
+ get {
+ return name;
+ }
+ }
+
+ public LocationVariant (LocationLayout layout, string? name, string? original_name) {
+ Object (layout: layout, name: name, original_name: original_name);
+ }
+
+ public GLib.Variant to_gsd_variant () {
+ if (name == null) {
+ return layout.to_gsd_variant ();
+ } else {
+ return new GLib.Variant ("(ss)", "xkb", "%s+%s".printf (layout.name, name));
+ }
+ }
+
+ public static int compare (LocationVariant a, LocationVariant b) {
+ if (a.name == null) {
+ return -1;
+ }
+
+ if (b.name == null) {
+ return 1;
+ }
+
+ return a.display_name.collate (b.display_name);
+ }
+}
diff --git a/src/Views/LocationView.vala b/src/Views/LocationView.vala
new file mode 100644
index 00000000..fcf60d5f
--- /dev/null
+++ b/src/Views/LocationView.vala
@@ -0,0 +1,132 @@
+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
+/*-
+ * Copyright (c) 2017 elementary LLC. (https://elementary.io)
+ *
+ * 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 3 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 .
+ */
+
+public class LocationView : AbstractInstallerView {
+ private VariantWidget location_variant_widget;
+
+ construct {
+ var image = new Gtk.Image.from_icon_name ("preferences-system-time", Gtk.IconSize.DIALOG);
+ image.valign = Gtk.Align.END;
+
+ var title_label = new Gtk.Label (_("Location"));
+ title_label.get_style_context ().add_class (Granite.STYLE_CLASS_H2_LABEL);
+ title_label.valign = Gtk.Align.START;
+
+ location_variant_widget = new VariantWidget ();
+
+ var helper = LocationHelper.get_default ();
+
+ content_area.attach (image, 0, 0, 1, 1);
+ content_area.attach (title_label, 0, 1, 1, 1);
+ content_area.attach (location_variant_widget, 1, 0, 1, 2);
+
+ var back_button = new Gtk.Button.with_label (_("Back"));
+
+ var next_button = new Gtk.Button.with_label (_("Select"));
+ next_button.sensitive = false;
+ next_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
+
+ action_area.add (back_button);
+ action_area.add (next_button);
+
+ location_variant_widget.variant_listbox.row_activated.connect (() => {
+ next_button.activate ();
+ });
+
+ location_variant_widget.variant_listbox.row_selected.connect (() => {
+ unowned Gtk.ListBoxRow row = location_variant_widget.main_listbox.get_selected_row ();
+ if (row != null) {
+ var layout = ((LayoutRow) row).layout;
+ unowned Configuration configuration = Configuration.get_default ();
+ GLib.Variant? layout_variant = null;
+ string second_variant = layout.name;
+
+ unowned Gtk.ListBoxRow vrow = location_variant_widget.variant_listbox.get_selected_row ();
+ if (vrow != null) {
+ unowned InitialSetup.LocationVariant variant = ((VariantRow) vrow).variant;
+ configuration.timezone = variant.original_name;
+ if (variant != null) {
+ layout_variant = variant.to_gsd_variant ();
+ }
+ }
+
+ if (layout_variant == null) {
+ layout_variant = layout.to_gsd_variant ();
+ }
+ }
+ });
+
+ back_button.clicked.connect (() => ((Hdy.Deck) get_parent ()).navigate (Hdy.NavigationDirection.BACK));
+
+ next_button.clicked.connect (() => {
+ next_step ();
+ });
+
+ location_variant_widget.main_listbox.row_activated.connect ((row) => {
+ unowned InitialSetup.LocationLayout layout = ((LayoutRow) row).layout;
+ if (!layout.has_variants ()) {
+ return;
+ }
+
+ location_variant_widget.variant_listbox.bind_model (layout.get_variants (), (variant) => { return new VariantRow (variant as InitialSetup.LocationVariant); });
+ location_variant_widget.variant_listbox.select_row (location_variant_widget.variant_listbox.get_row_at_index (0));
+
+ location_variant_widget.show_variants (_("Continent"), "%s".printf (layout.display_name));
+ });
+
+ location_variant_widget.main_listbox.row_selected.connect ((row) => {
+ next_button.sensitive = true;
+ });
+
+ location_variant_widget.main_listbox.bind_model (InitialSetup.LocationLayout.get_all (), (layout) => { return new LayoutRow (layout as InitialSetup.LocationLayout); });
+
+ show_all ();
+ }
+
+ private class LayoutRow : Gtk.ListBoxRow {
+ public unowned InitialSetup.LocationLayout layout;
+ public LayoutRow (InitialSetup.LocationLayout layout) {
+ this.layout = layout;
+
+ string layout_description = layout.display_name;
+ if (layout.has_variants ()) {
+ layout_description = _("%s…").printf (layout_description);
+ }
+
+ var label = new Gtk.Label (layout_description);
+ label.margin = 6;
+ label.xalign = 0;
+ label.get_style_context ().add_class (Granite.STYLE_CLASS_H3_LABEL);
+ add (label);
+ show_all ();
+ }
+ }
+
+ private class VariantRow : Gtk.ListBoxRow {
+ public unowned InitialSetup.LocationVariant variant;
+ public VariantRow (InitialSetup.LocationVariant variant) {
+ this.variant = variant;
+ var label = new Gtk.Label (variant.display_name);
+ label.margin = 6;
+ label.xalign = 0;
+ label.get_style_context ().add_class (Granite.STYLE_CLASS_H3_LABEL);
+ add (label);
+ show_all ();
+ }
+ }
+}
diff --git a/src/meson.build b/src/meson.build
index 7c80dfe2..454287d2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,14 +4,18 @@ vala_files = [
'MainWindow.vala',
'Helpers/AccountsServiceInterface.vala',
'Helpers/LocaleHelper.vala',
+ 'Helpers/LocationHelper.vala',
'Objects/Configuration.vala',
'Objects/KeyboardLayout.vala',
'Objects/KeyboardVariant.vala',
+ 'Objects/LocationLayout.vala',
+ 'Objects/LocationVariant.vala',
'Utils.vala',
'Views/AbstractInstallerView.vala',
'Views/AccountView.vala',
'Views/KeyboardLayoutView.vala',
'Views/LanguageView.vala',
+ 'Views/LocationView.vala',
'Views/NetworkView.vala',
'Widgets/LayoutWidget.vala',
'Widgets/VariantWidget.vala'