2 # -*- coding: utf-8 -*-
6 # Copyright © 2013-2018 Antergos
8 # This file is part of Cnchi.
10 # Cnchi is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
15 # Cnchi is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # The following additional terms are in effect as per Section 7 of the license:
22 # The preservation of all legal notices and author attributions in
23 # the material or in the Appropriate Legal Notices displayed
24 # by works containing it is required.
26 # You should have received a copy of the GNU General Public License
27 # along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
29 """ Timezone screen """
34 import multiprocessing
42 import misc.extra as misc
43 import widgets.timezonemap as timezonemap
44 from pages.gtkbasebox import GtkBaseBox
48 # When testing, no _() is available
51 except NameError as err:
56 class Timezone(GtkBaseBox):
57 """ Timezone screen """
59 def __init__(self, params, prev_page="location", next_page="keymap"):
60 super().__init__(self, params, "timezone", prev_page, next_page)
62 self.map_window = self.gui.get_object('timezone_map_window')
64 self.combobox_zone = self.gui.get_object('comboboxtext_zone')
65 self.combobox_region = self.gui.get_object('comboboxtext_region')
67 # Show regions in three columns
68 self.combobox_region.set_wrap_width(3)
70 self.tzdb = tz.Database()
73 # This is for populate_cities
76 # Autotimezone process will store detected coords in this queue
77 self.auto_timezone_coords = multiprocessing.Queue()
79 # Process to try to determine timezone.
80 self.autodetected_coords = None
81 self.start_auto_timezone_process()
84 self.tzmap = timezonemap.TimezoneMap()
85 self.tzmap.connect('location-changed', self.on_location_changed)
87 # Strip .UTF-8 from locale, icu doesn't parse it
88 self.locale = os.environ['LANG'].rsplit('.', 1)[0]
89 self.map_window.add(self.tzmap)
92 def translate_ui(self):
93 """ Translates all ui elements """
94 label = self.gui.get_object('label_zone')
98 label = self.gui.get_object('label_region')
100 label.set_markup(txt)
102 label = self.gui.get_object('label_ntp')
103 txt = _("Use Network Time Protocol (NTP) for clock synchronization")
104 label.set_markup(txt)
106 self.header.set_subtitle(_("Select Your Timezone"))
108 def on_location_changed(self, _tzmap, tz_location):
109 """ User changed its location """
110 # loc = self.tzdb.get_loc(self.timezone)
113 self.forward_button.set_sensitive(False)
115 self.timezone = tz_location.get_property('zone')
116 logging.info("Location changed to : %s", self.timezone)
117 self.update_comboboxes(self.timezone)
118 self.forward_button.set_sensitive(True)
120 def update_comboboxes(self, timezone):
121 """ Location has changed, update comboboxes """
122 zone, region = timezone.split('/', 1)
123 self.select_combobox_item(self.combobox_zone, zone)
124 self.populate_cities(zone)
125 self.select_combobox_item(self.combobox_region, region)
128 def select_combobox_item(combobox, item):
129 """ Make combobox select an item """
130 tree_model = combobox.get_model()
131 tree_iter = tree_model.get_iter_first()
133 while tree_iter is not None:
134 value = tree_model.get_value(tree_iter, 0)
136 combobox.set_active_iter(tree_iter)
139 tree_iter = tree_model.iter_next(tree_iter)
141 def set_timezone(self, timezone):
142 """ Set timezone in tzmap """
144 self.timezone = timezone
145 res = self.tzmap.set_timezone(timezone)
146 # res will be False if the timezone is unrecognised
147 self.forward_button.set_sensitive(res)
149 def on_zone_combobox_changed(self, _widget):
151 new_zone = self.combobox_zone.get_active_text()
152 if new_zone is not None:
153 self.populate_cities(new_zone)
155 def on_region_combobox_changed(self, _widget):
156 """ Region changed """
157 new_zone = self.combobox_zone.get_active_text()
158 new_region = self.combobox_region.get_active_text()
159 if new_zone is not None and new_region is not None:
160 new_timezone = "{0}/{1}".format(new_zone, new_region)
161 # Only set timezone if it has changed :p
162 if self.timezone != new_timezone:
163 self.set_timezone(new_timezone)
165 def populate_zones(self):
166 """ Get all zones and fill our model """
168 for loc in self.tzdb.locations:
169 zone = loc.zone.split('/', 1)[0]
170 if zone not in zones:
173 tree_model = self.combobox_zone.get_model()
176 tree_model.append([zone, zone])
178 def populate_cities(self, selected_zone):
179 """ Get all cities and populate our model """
180 if self.old_zone != selected_zone:
182 for loc in self.tzdb.locations:
183 zone, region = loc.zone.split('/', 1)
184 if zone == selected_zone:
185 regions.append(region)
187 tree_model = self.combobox_region.get_model()
189 for region in regions:
190 tree_model.append([region, region])
191 self.old_zone = selected_zone
193 def prepare(self, direction):
194 """ Prepare screen before showing it """
196 self.populate_zones()
198 self.forward_button.set_sensitive(False)
200 if self.autodetected_coords is None:
202 self.autodetected_coords = self.auto_timezone_coords.get(
205 logging.warning("Can't autodetect timezone coordinates")
207 if self.autodetected_coords:
208 coords = self.autodetected_coords
210 latitude = float(coords[0])
211 longitude = float(coords[1])
212 timezone = self.tzmap.get_timezone_at_coords(
214 self.set_timezone(timezone)
215 self.forward_button.set_sensitive(True)
216 except ValueError as value_error:
217 self.autodetected_coords = None
219 "Can't autodetect timezone coordinates: %s", value_error)
223 def start_auto_timezone_process(self):
224 """ Starts timezone thread """
225 proc = AutoTimezoneProcess(self.auto_timezone_coords, self.settings)
227 proc.name = "timezone"
231 def log_location(loc):
232 """ Log selected location """
233 logging.debug("timezone human zone: %s", loc.human_zone)
234 logging.debug("timezone country: %s", loc.country)
235 logging.debug("timezone zone: %s", loc.zone)
236 logging.debug("timezone human country: %s", loc.human_country)
239 logging.debug("timezone comment: %s", loc.comment)
242 logging.debug("timezone latitude: %s", loc.latitude)
245 logging.debug("timezone longitude: %s", loc.longitude)
247 def store_values(self):
248 """ The user clicks 'next' """
249 loc = self.tzdb.get_loc(self.timezone)
252 self.settings.set("timezone_zone", loc.zone)
253 self.settings.set("timezone_human_zone", loc.human_zone)
254 self.settings.set("timezone_country", loc.country)
255 self.settings.set("timezone_human_country", loc.human_country)
258 self.settings.set("timezone_comment", loc.comment)
260 self.settings.set("timezone_comment", "")
263 self.settings.set("timezone_latitude", loc.latitude)
265 self.settings.set("timezone_latitude", "")
268 self.settings.set("timezone_longitude", loc.longitude)
270 self.settings.set("timezone_longitude", "")
273 self.log_location(loc)
275 # This way process.py will know that all info has been entered
276 self.settings.set("timezone_done", True)
278 if self.settings.get('use_timesyncd'):
280 "Cnchi will setup network time using systemd-timesyncd")
282 logging.debug("Cnchi won't setup network time")
286 def on_switch_ntp_activate(self, ntp_switch, _data):
287 """ activated/deactivated ntp switch """
288 self.settings.set('use_timesyncd', ntp_switch.get_active())
291 class AutoTimezoneProcess(multiprocessing.Process):
292 """ Thread that asks our server for user's location """
294 def __init__(self, coords_queue, settings):
295 super(AutoTimezoneProcess, self).__init__()
296 self.coords_queue = coords_queue
297 self.settings = settings
300 """ main thread method """
301 # Do not start looking for our timezone until we've reached the
302 # language screen (welcome.py sets timezone_start to true when
304 while not self.settings.get('timezone_start'):
307 coords = self.use_geoip()
309 msg = "Could not detect your timezone using GeoIP database. Let's use another method."
311 coords = self.use_geo_antergos()
315 _("Timezone (latitude %s, longitude %s) detected."),
318 self.coords_queue.put(coords)
320 logging.warning("Could not detect your timezone!")
324 """ Determine our location using GeoIP database """
325 logging.debug("Getting your location using GeoIP database")
326 location = geoip.GeoIP().get_location()
328 return [location.latitude, location.longitude]
332 def maybe_wait_for_network():
333 """ Waits until there is an Internet connection available """
334 if not misc.has_connection():
336 "Can't get network status. Cnchi will try again in a moment")
337 while not misc.has_connection():
338 time.sleep(4) # Wait 4 seconds and try again
339 logging.debug("A working network connection has been detected.")
342 def use_geo_antergos(self):
343 """ Determine our location using geo.antergos.com """
344 # Calculate logo hash
345 logo = "data/images/antergos/antergos-logo-mini2.png"
346 logo_path = os.path.join(self.settings.get("cnchi"), logo)
347 with open(logo_path, "rb") as logo_file:
348 logo_bytes = logo_file.read()
349 logo_hasher = hashlib.sha1()
350 logo_hasher.update(logo_bytes)
351 logo_digest = logo_hasher.digest()
353 # Wait until there is an Internet connection available
354 self.maybe_wait_for_network()
356 # OK, now get our timezone
357 logging.debug("We have connection. Let's get our timezone")
360 url = urllib.request.Request(
361 url="http://geo.antergos.com",
363 headers={"User-Agent": "RebornOS Installer", "Connection": "close"})
364 with urllib.request.urlopen(url) as conn:
365 coords = conn.read().decode('utf-8').strip()
367 # Sometimes server returns 0 0, we treat it as an error
370 coords = coords.split()
371 except (OSError, urllib.error.HTTPError, http.client.HTTPException) as err:
372 template = "Error getting timezone coordinates. " \
373 "An exception of type {0} occured. Arguments:\n{1!r}"
374 message = template.format(type(err).__name__, err.args)
375 logging.error(message)