mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 12:02:43 +00:00 
			
		
		
		
	 43a3471298
			
		
	
	
		43a3471298
		
	
	
	
	
		
			
			These are still included in LibUnicode, but this updates their location and the include paths of other files which include them.
		
			
				
	
	
		
			163 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "TimeZoneSettingsWidget.h"
 | |
| #include <AK/Time.h>
 | |
| #include <Applications/ClockSettings/TimeZoneSettingsWidgetGML.h>
 | |
| #include <LibGUI/ComboBox.h>
 | |
| #include <LibGUI/Event.h>
 | |
| #include <LibGUI/ImageWidget.h>
 | |
| #include <LibGUI/ItemListModel.h>
 | |
| #include <LibGUI/Label.h>
 | |
| #include <LibGUI/Layout.h>
 | |
| #include <LibGUI/Margins.h>
 | |
| #include <LibGUI/Painter.h>
 | |
| #include <LibGUI/Process.h>
 | |
| #include <LibGfx/Palette.h>
 | |
| #include <LibLocale/DateTimeFormat.h>
 | |
| #include <LibLocale/Locale.h>
 | |
| #include <LibTimeZone/TimeZone.h>
 | |
| #include <math.h>
 | |
| #include <spawn.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| using StringViewListModel = GUI::ItemListModel<StringView, Span<StringView const>>;
 | |
| 
 | |
| static constexpr auto PI_OVER_180 = M_PIf32 / 180.0f;
 | |
| static constexpr auto PI_OVER_4 = M_PIf32 / 4.0f;
 | |
| static constexpr auto TAU = M_PIf32 * 2.0f;
 | |
| 
 | |
| // The map as stored on disk is a valid Mercadian projected map. But it has quite a bit of dead space that
 | |
| // we can remove. This makes the map non-Mercadian, so we need to adjust our math based on what we removed.
 | |
| static constexpr auto TIME_ZONE_MAP_NORTHERN_TRIM = 78;
 | |
| static constexpr auto TIME_ZONE_MAP_SOUTHERN_TRIM = 50;
 | |
| 
 | |
| static constexpr auto TIME_ZONE_TEXT_WIDTH = 210;
 | |
| static constexpr auto TIME_ZONE_TEXT_HEIGHT = 40;
 | |
| static constexpr auto TIME_ZONE_TEXT_PADDING = 5;
 | |
| static constexpr auto TIME_ZONE_TEXT_COLOR = Gfx::Color::from_rgb(0xeaf688);
 | |
| 
 | |
| TimeZoneSettingsWidget::TimeZoneSettingsWidget()
 | |
| {
 | |
|     load_from_gml(time_zone_settings_widget_gml);
 | |
| 
 | |
|     static auto time_zones = TimeZone::all_time_zones();
 | |
|     m_time_zone = TimeZone::system_time_zone();
 | |
| 
 | |
|     m_time_zone_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("time_zone_input");
 | |
|     m_time_zone_combo_box->set_only_allow_values_from_model(true);
 | |
|     m_time_zone_combo_box->set_model(*StringViewListModel::create(time_zones));
 | |
|     m_time_zone_combo_box->set_text(m_time_zone);
 | |
|     m_time_zone_combo_box->on_change = [&](auto, auto) {
 | |
|         set_modified(true);
 | |
|     };
 | |
| 
 | |
|     auto time_zone_map_bitmap = Gfx::Bitmap::try_load_from_file("/res/graphics/map.png"sv).release_value_but_fixme_should_propagate_errors();
 | |
|     auto time_zone_rect = time_zone_map_bitmap->rect().shrunken(TIME_ZONE_MAP_NORTHERN_TRIM, 0, TIME_ZONE_MAP_SOUTHERN_TRIM, 0);
 | |
|     time_zone_map_bitmap = time_zone_map_bitmap->cropped(time_zone_rect).release_value_but_fixme_should_propagate_errors();
 | |
| 
 | |
|     m_time_zone_map = *find_descendant_of_type_named<GUI::ImageWidget>("time_zone_map");
 | |
|     m_time_zone_map->set_bitmap(time_zone_map_bitmap);
 | |
| 
 | |
|     auto time_zone_marker = Gfx::Bitmap::try_load_from_file("/res/icons/32x32/ladyball.png"sv).release_value_but_fixme_should_propagate_errors();
 | |
|     m_time_zone_marker = time_zone_marker->scaled(0.75f, 0.75f).release_value_but_fixme_should_propagate_errors();
 | |
| 
 | |
|     set_time_zone_location();
 | |
| }
 | |
| 
 | |
| void TimeZoneSettingsWidget::second_paint_event(GUI::PaintEvent& event)
 | |
| {
 | |
|     GUI::Widget::second_paint_event(event);
 | |
| 
 | |
|     if (!m_time_zone_location.has_value())
 | |
|         return;
 | |
| 
 | |
|     GUI::Painter painter(*this);
 | |
|     painter.add_clip_rect(event.rect());
 | |
|     painter.add_clip_rect(m_time_zone_map->relative_rect());
 | |
| 
 | |
|     auto x = m_time_zone_map->x() + m_time_zone_map->parent_widget()->layout()->margins().left();
 | |
|     auto y = m_time_zone_map->y() + m_time_zone_map->parent_widget()->layout()->margins().top();
 | |
| 
 | |
|     auto point = m_time_zone_location->to_type<int>().translated(x, y);
 | |
|     point.translate_by(-m_time_zone_marker->width() / 2, -m_time_zone_marker->height() / 2);
 | |
|     painter.blit(point, *m_time_zone_marker, rect());
 | |
| 
 | |
|     point = m_time_zone_location->to_type<int>().translated(x, y);
 | |
|     point.translate_by(0, -TIME_ZONE_TEXT_HEIGHT / 2);
 | |
| 
 | |
|     if (point.x() <= (m_time_zone_map->width() / 2))
 | |
|         point.translate_by(m_time_zone_marker->width() / 2 + TIME_ZONE_TEXT_PADDING, 0);
 | |
|     else
 | |
|         point.translate_by(-m_time_zone_marker->width() / 2 - TIME_ZONE_TEXT_PADDING - TIME_ZONE_TEXT_WIDTH, 0);
 | |
| 
 | |
|     auto text_area = Gfx::IntRect { point.x(), point.y(), TIME_ZONE_TEXT_WIDTH, TIME_ZONE_TEXT_HEIGHT };
 | |
|     painter.draw_rect(text_area, palette().active_window_border1());
 | |
| 
 | |
|     text_area.shrink(2, 2);
 | |
|     painter.fill_rect(text_area, TIME_ZONE_TEXT_COLOR);
 | |
|     painter.draw_text(text_area, m_time_zone_text, Gfx::TextAlignment::Center);
 | |
| }
 | |
| 
 | |
| void TimeZoneSettingsWidget::reset_default_values()
 | |
| {
 | |
|     m_time_zone = "UTC"sv;
 | |
|     m_time_zone_combo_box->set_text(m_time_zone);
 | |
|     m_time_zone_location.clear();
 | |
| 
 | |
|     set_time_zone();
 | |
|     update();
 | |
| }
 | |
| 
 | |
| void TimeZoneSettingsWidget::apply_settings()
 | |
| {
 | |
|     m_time_zone = m_time_zone_combo_box->text();
 | |
| 
 | |
|     set_time_zone_location();
 | |
|     set_time_zone();
 | |
|     update();
 | |
| }
 | |
| 
 | |
| void TimeZoneSettingsWidget::set_time_zone_location()
 | |
| {
 | |
|     m_time_zone_location = compute_time_zone_location();
 | |
| 
 | |
|     auto locale = Locale::default_locale();
 | |
|     auto now = AK::Time::now_realtime();
 | |
| 
 | |
|     auto name = Locale::format_time_zone(locale, m_time_zone, Locale::CalendarPatternStyle::Long, now);
 | |
|     auto offset = Locale::format_time_zone(locale, m_time_zone, Locale::CalendarPatternStyle::LongOffset, now);
 | |
| 
 | |
|     m_time_zone_text = String::formatted("{}\n({})", name, offset);
 | |
| }
 | |
| 
 | |
| // https://en.wikipedia.org/wiki/Mercator_projection#Derivation
 | |
| Optional<Gfx::FloatPoint> TimeZoneSettingsWidget::compute_time_zone_location() const
 | |
| {
 | |
|     auto location = TimeZone::get_time_zone_location(m_time_zone);
 | |
|     if (!location.has_value())
 | |
|         return {};
 | |
| 
 | |
|     auto latitude = location->latitude.decimal_coordinate();
 | |
|     auto longitude = location->longitude.decimal_coordinate();
 | |
| 
 | |
|     auto rect = m_time_zone_map->bitmap()->rect().to_type<float>();
 | |
| 
 | |
|     latitude = logf(tanf(PI_OVER_4 + (latitude * PI_OVER_180 / 2.0f)));
 | |
| 
 | |
|     auto mercadian_x = (longitude + 180.0f) * (rect.width() / 360.0f);
 | |
|     auto mercadian_y = (rect.height() / 2.0f) - (rect.width() * latitude / TAU);
 | |
| 
 | |
|     mercadian_y -= TIME_ZONE_MAP_NORTHERN_TRIM / 2;
 | |
|     mercadian_y += TIME_ZONE_MAP_SOUTHERN_TRIM / 2;
 | |
| 
 | |
|     return Gfx::FloatPoint { mercadian_x, mercadian_y };
 | |
| }
 | |
| 
 | |
| void TimeZoneSettingsWidget::set_time_zone()
 | |
| {
 | |
|     GUI::Process::spawn_or_show_error(window(), "/bin/timezone"sv, Array { m_time_zone.characters() });
 | |
| }
 |