You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
8.8 KiB
C++

/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
*
* Originally from the Manjaro Installation Framework
* by Roland Singer <roland@manjaro.org>
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* Calamares 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.
*
* Calamares 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cmath>
#include "utils/Logger.h"
#include "timezonewidget.h"
static constexpr double MAP_Y_OFFSET = 0.125;
static constexpr double MAP_X_OFFSET = -0.0370;
constexpr static double MATH_PI = 3.14159265;
#ifdef DEBUG_TIMEZONES
// Adds a label to the timezone with this name
constexpr static QLatin1Literal ZONE_NAME( "zone" );
#endif
TimeZoneWidget::TimeZoneWidget( QWidget* parent ) :
QWidget( parent )
{
setMouseTracking( false );
setCursor( Qt::PointingHandCursor );
// Font
font.setPointSize( 12 );
font.setBold( false );
// Images
background = QImage( ":/images/bg.png" ).scaled( X_SIZE, Y_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
pin = QImage( ":/images/pin.png" );
// Set size
setMinimumSize( background.size() );
setMaximumSize( background.size() );
// Zone images
QStringList zones = QString( ZONES ).split( " ", QString::SkipEmptyParts );
for ( int i = 0; i < zones.size(); ++i )
{
timeZoneImages.append( QImage( ":/images/timezone_" + zones.at( i ) + ".png" ).scaled( X_SIZE, Y_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
#ifdef DEBUG_TIMEZONES
timeZoneImages.last().setText( ZONE_NAME, zones.at( i ) );
#endif
}
}
void TimeZoneWidget::setCurrentLocation( QString region, QString zone )
{
QHash<QString, QList<LocaleGlobal::Location> > hash = LocaleGlobal::getLocations();
if ( !hash.contains( region ) )
return;
QList<LocaleGlobal::Location> locations = hash.value( region );
for ( int i = 0; i < locations.size(); ++i )
{
if ( locations.at( i ).zone == zone )
{
setCurrentLocation( locations.at( i ) );
break;
}
}
}
void TimeZoneWidget::setCurrentLocation( LocaleGlobal::Location location )
{
currentLocation = location;
// Set zone
QPoint pos = getLocationPosition( currentLocation.longitude, currentLocation.latitude );
#ifdef DEBUG_TIMEZONES
cDebug() << "Setting location" << location.region << location.zone << location.country;
cDebug() << " .. long" << location.longitude << "lat" << location.latitude;
cDebug() << " .. x" << pos.x() << "y" << pos.y();
bool found = false;
#endif
for ( int i = 0; i < timeZoneImages.size(); ++i )
{
QImage zone = timeZoneImages[i];
// If not transparent set as current
if ( zone.pixel( pos ) != RGB_TRANSPARENT )
{
#ifdef DEBUG_TIMEZONES
// Log *all* the zones that contain this point,
// but only pick the first.
if ( !found )
{
currentZoneImage = zone;
found = true;
cDebug() << " .. First zone found" << i << zone.text( ZONE_NAME );
}
else
cDebug() << " .. Also in zone" << i << zone.text( ZONE_NAME );
#else
currentZoneImage = zone;
break;
#endif
}
}
// Repaint widget
repaint();
}
//###
//### Private
//###
QPoint TimeZoneWidget::getLocationPosition( double longitude, double latitude )
{
const int width = this->width();
const int height = this->height();
double x = ( width / 2.0 + ( width / 2.0 ) * longitude / 180.0 ) + MAP_X_OFFSET * width;
double y = ( height / 2.0 - ( height / 2.0 ) * latitude / 90.0 ) + MAP_Y_OFFSET * height;
// Far north, the MAP_Y_OFFSET no longer holds, cancel the Y offset; it's noticeable
// from 62 degrees north, so scale those 28 degrees as if the world is flat south
// of there, and we have a funny "rounded" top of the world. In practice the locations
// of the different cities / regions looks ok -- at least Thule ends up in the right
// country, and Inuvik isn't in the ocean.
if ( latitude > 70.0 )
y -= sin( MATH_PI * ( latitude - 70.0 ) / 56.0 ) * MAP_Y_OFFSET * height * 0.8;
if ( latitude > 74.0 )
y += 4;
if ( latitude > 69.0 )
y -= 2;
if ( latitude > 59.0 )
y -= 4 * int( ( latitude - 54.0 ) / 5.0 );
if ( latitude > 54.0 )
y -= 2;
if ( latitude > 49.0 )
y -= int ( (latitude - 44.0) / 5.0 );
// Far south, some stretching occurs as well, but it is less pronounced.
// Move down by 1 pixel per 5 degrees past 10 south
if ( latitude < 0 )
y += int( (-latitude) / 5.0 );
// Antarctica isn't shown on the map, but you could try clicking there
if ( latitude < -60 )
y = height - 1;
if ( x < 0 )
x = width+x;
if ( x >= width )
x -= width;
if ( y < 0 )
y = height+y;
if ( y >= height )
y -= height;
return QPoint( int(x), int(y) );
}
void TimeZoneWidget::paintEvent( QPaintEvent* )
{
const int width = this->width();
const int height = this->height();
QFontMetrics fontMetrics( font );
QPainter painter( this );
painter.setRenderHint( QPainter::Antialiasing );
painter.setFont( font );
// Draw background
painter.drawImage( 0, 0, background );
// Draw zone image
painter.drawImage( 0, 0, currentZoneImage );
#ifdef DEBUG_TIMEZONES
QPoint point = getLocationPosition( currentLocation.longitude, currentLocation.latitude );
// Draw latitude lines
for ( int y_lat = -50; y_lat < 80 ; y_lat+=5 )
{
QPen p( y_lat ? Qt::black : Qt::red );
p.setWidth( 0 );
painter.setPen( p );
QPoint latLine0( getLocationPosition( 0, y_lat ) );
int llx = latLine0.x() + ((y_lat & 1) ? -10 : 0);
int lly = latLine0.y();
for ( int c = 0 ; c < width ; ++c )
painter.drawPoint( c, lly );
}
// Just a dot in the selected location, no label
painter.setPen( Qt::red );
painter.drawPoint( point );
#else
// Draw pin at current location
QPoint point = getLocationPosition( currentLocation.longitude, currentLocation.latitude );
painter.drawImage( point.x() - pin.width()/2, point.y() - pin.height()/2, pin );
// Draw text and box
const int textWidth = fontMetrics.width( LocaleGlobal::Location::pretty( currentLocation.zone ) );
const int textHeight = fontMetrics.height();
QRect rect = QRect( point.x() - textWidth/2 - 5, point.y() - textHeight - 8, textWidth + 10, textHeight - 2 );
if ( rect.x() <= 5 )
rect.moveLeft( 5 );
if ( rect.right() >= width-5 )
rect.moveRight( width - 5 );
if ( rect.y() <= 5 )
rect.moveTop( 5 );
if ( rect.y() >= height-5 )
rect.moveBottom( height-5 );
painter.setPen( QPen() ); // no pen
painter.setBrush( QColor( 40, 40, 40 ) );
painter.drawRoundedRect( rect, 3, 3 );
painter.setPen( Qt::white );
painter.drawText( rect.x() + 5, rect.bottom() - 4, LocaleGlobal::Location::pretty( currentLocation.zone ) );
#endif
painter.end();
}
void TimeZoneWidget::mousePressEvent( QMouseEvent* event )
{
if ( event->button() != Qt::LeftButton )
return;
// Set nearest location
int nX = 999999, mX = event->pos().x();
int nY = 999999, mY = event->pos().y();
QHash<QString, QList<LocaleGlobal::Location> > hash = LocaleGlobal::getLocations();
QHash<QString, QList<LocaleGlobal::Location> >::iterator iter = hash.begin();
while ( iter != hash.end() )
{
QList<LocaleGlobal::Location> locations = iter.value();
for ( int i = 0; i < locations.size(); ++i )
{
LocaleGlobal::Location loc = locations[i];
QPoint locPos = getLocationPosition( loc.longitude, loc.latitude );
if ( ( abs( mX - locPos.x() ) + abs( mY - locPos.y() ) < abs( mX - nX ) + abs( mY - nY ) ) )
{
currentLocation = loc;
nX = locPos.x();
nY = locPos.y();
}
}
++iter;
}
// Set zone image and repaint widget
setCurrentLocation( currentLocation );
// Emit signal
emit locationChanged( currentLocation );
}