// // This file is part of the Marble Desktop Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Bastian Holst // // Self #include "WeatherItem.h" // Marble #include "GeoPainter.h" #include "MarbleDirs.h" #include "WeatherData.h" #include "weatherGlobal.h" #include "FrameGraphicsItem.h" #include "LabelGraphicsItem.h" #include "MarbleGraphicsGridLayout.h" #include "TinyWebBrowser.h" #include "MarbleDebug.h" // Qt #include #include #include #include #include #include #include #include #include namespace Marble { const QSize borderSpacing( 4, 4 ); const QSize imageSize( 28, 28 ); const double imageSizeRatio = ( (double) imageSize.width()) / ( (double) imageSize.height() ); const qint32 horizontalSpacing = 4; const qint32 verticalSpacing = 2; class WeatherItemPrivate { Q_DECLARE_TR_FUNCTIONS(WeatherItemPrivate) public: WeatherItemPrivate( WeatherItem *parent ) : m_priority( 0 ), m_action( new QAction( tr( "Weather" ), parent ) ), m_browser( 0 ), m_parent( parent ), m_frameItem( new FrameGraphicsItem( m_parent ) ), m_conditionLabel( new LabelGraphicsItem( m_frameItem ) ), m_temperatureLabel( new LabelGraphicsItem( m_frameItem ) ), m_windDirectionLabel( new LabelGraphicsItem( m_frameItem ) ), m_windSpeedLabel( new LabelGraphicsItem( m_frameItem ) ) { // Setting minimum sizes m_temperatureLabel->setMinimumSize( QSizeF( 0, imageSize.height() ) ); m_windSpeedLabel->setMinimumSize( QSizeF( 0, imageSize.height() ) ); // Layouting the item MarbleGraphicsGridLayout *topLayout = new MarbleGraphicsGridLayout( 1, 1 ); parent->setLayout( topLayout ); topLayout->addItem( m_frameItem, 0, 0 ); MarbleGraphicsGridLayout *gridLayout = new MarbleGraphicsGridLayout( 2, 2 ); gridLayout->setAlignment( Qt::AlignCenter ); gridLayout->setSpacing( 4 ); m_frameItem->setLayout( gridLayout ); m_frameItem->setFrame( FrameGraphicsItem::RoundedRectFrame ); gridLayout->addItem( m_conditionLabel, 0, 0 ); gridLayout->addItem( m_temperatureLabel, 0, 1 ); gridLayout->setAlignment( m_temperatureLabel, Qt::AlignRight | Qt::AlignVCenter ); gridLayout->addItem( m_windDirectionLabel, 1, 0 ); gridLayout->addItem( m_windSpeedLabel, 1, 1 ); gridLayout->setAlignment( m_windSpeedLabel, Qt::AlignRight | Qt::AlignVCenter ); updateLabels(); } ~WeatherItemPrivate() { delete m_browser; } void updateToolTip() { QLocale locale = QLocale::system(); QString toolTip; toolTip += tr( "Station: %1\n" ).arg( m_parent->stationName() ); if ( m_currentWeather.hasValidCondition() && m_currentWeather.hasValidTemperature() ) toolTip += QString( "%2, %3\n" ).arg( m_currentWeather.conditionString() ) .arg( temperatureString() ); else if ( m_currentWeather.hasValidCondition() ) toolTip += QString( "%2\n" ).arg( m_currentWeather.conditionString() ); else if ( m_currentWeather.hasValidTemperature() ) toolTip += QString( "%2\n" ).arg( temperatureString() ); if ( m_currentWeather.hasValidWindSpeed() && m_currentWeather.hasValidWindDirection() ) toolTip += tr( "Wind: %4, %5\n", "Wind: WindSpeed, WindDirection" ) .arg( windSpeedString( ) ) .arg( m_currentWeather.windDirectionString() ); else if ( m_currentWeather.hasValidWindSpeed() ) toolTip += tr( "Wind: %4\n", "Wind: WindSpeed" ) .arg( m_currentWeather.windSpeedString() ); else if ( m_currentWeather.hasValidWindDirection() ) toolTip += tr( "Wind: %4\n", "Wind: WindDirection" ) .arg( m_currentWeather.windDirectionString() ); if ( m_currentWeather.hasValidPressure() && m_currentWeather.hasValidPressureDevelopment() ) toolTip += tr( "Pressure: %6, %7", "Pressure: Pressure, Development" ) .arg( pressureString() ) .arg( m_currentWeather.pressureDevelopmentString() ); else if ( m_currentWeather.hasValidPressure() ) toolTip += tr( "Pressure: %6", "Pressure: Pressure" ) .arg( pressureString() ); else if ( m_currentWeather.hasValidPressureDevelopment() ) toolTip += tr( "Pressure %7", "Pressure Development" ) .arg( m_currentWeather.pressureDevelopmentString() ); if ( !m_forecastWeather.isEmpty() ) { toolTip += '\n'; QDate minDate = QDate::currentDate(); minDate.addDays( -1 ); foreach( const WeatherData& data, m_forecastWeather ) { QDate date = data.dataDate(); if( date >= minDate && data.hasValidCondition() && data.hasValidMinTemperature() && data.hasValidMaxTemperature() ) { toolTip += '\n'; toolTip += tr( "%1: %2, %3 to %4", "DayOfWeek: Condition, MinTemp to MaxTemp" ) .arg( locale.standaloneDayName( date.dayOfWeek() ) ) .arg( data.conditionString() ) .arg( data.minTemperatureString( temperatureUnit() ) ) .arg( data.maxTemperatureString( temperatureUnit() ) ); } } } m_parent->setToolTip( toolTip ); } void updateLabels() { if ( isConditionShown() ) { m_conditionLabel->setImage( m_currentWeather.icon(), imageSize ); } else { m_conditionLabel->clear(); } if ( isTemperatureShown() ) { m_temperatureLabel->setText( temperatureString() ); } else { m_temperatureLabel->clear(); } if ( isWindDirectionShown() ) { QString windDirectionString = m_currentWeather.windDirectionString(); QSizeF windDirectionImageSize; QSizeF windDirectionSizeF = s_windIcons.boundsOnElement( windDirectionString ).size(); double windDirectionRatio = windDirectionSizeF.width() / windDirectionSizeF.height(); if ( windDirectionRatio >= imageSizeRatio ) { windDirectionImageSize.setWidth( imageSize.width() ); windDirectionImageSize.setHeight( imageSize.width() / windDirectionRatio ); } else { windDirectionImageSize.setHeight( imageSize.height() ); windDirectionImageSize.setWidth( imageSize.height() * windDirectionRatio ); } QImage windArrow( windDirectionImageSize.toSize(), QImage::Format_ARGB32 ); windArrow.fill( Qt::transparent ); QPainter painter( &windArrow ); s_windIcons.render( &painter, windDirectionString ); m_windDirectionLabel->setImage( windArrow ); } else { m_windDirectionLabel->clear(); } if ( isWindSpeedShown() ) { m_windSpeedLabel->setText( windSpeedString() ); } else { m_windSpeedLabel->clear(); } m_parent->update(); } bool isConditionShown() { return m_currentWeather.hasValidCondition() && m_settings.value( "showCondition", showConditionDefault ).toBool(); } bool isTemperatureShown() { return m_currentWeather.hasValidTemperature() && m_settings.value( "showTemperature", showTemperatureDefault ).toBool(); } bool isWindDirectionShown() { return m_currentWeather.hasValidWindDirection() && m_settings.value( "showWindDirection", showWindDirectionDefault ).toBool(); } bool isWindSpeedShown() { return m_currentWeather.hasValidWindSpeed() && m_settings.value( "showWindSpeed", showWindSpeedDefault ).toBool(); } QString temperatureString() { WeatherData::TemperatureUnit tUnit = temperatureUnit(); return m_currentWeather.temperatureString( tUnit ); } WeatherData::TemperatureUnit temperatureUnit() { WeatherData::TemperatureUnit tUnit = (WeatherData::TemperatureUnit) m_settings.value( "temperatureUnit", WeatherData::Celsius ).toInt(); return tUnit; } QString windSpeedString() { return m_currentWeather.windSpeedString( speedUnit() ); } WeatherData::SpeedUnit speedUnit() { return (WeatherData::SpeedUnit) m_settings.value( "windSpeedUnit", WeatherData::kph ).toInt(); } QString pressureString() { return m_currentWeather.pressureString( pressureUnit() ); } WeatherData::PressureUnit pressureUnit() { return (WeatherData::PressureUnit) m_settings.value( "pressureUnit", WeatherData::HectoPascal ).toInt(); } WeatherData m_currentWeather; QMap m_forecastWeather; int m_priority; QAction *m_action; TinyWebBrowser *m_browser; WeatherItem *m_parent; QString m_stationName; QHash m_settings; static QFont s_font; static QSvgRenderer s_windIcons; // Labels and Layout // We are not the owner of these items. FrameGraphicsItem *m_frameItem; LabelGraphicsItem *m_conditionLabel; LabelGraphicsItem *m_temperatureLabel; LabelGraphicsItem *m_windDirectionLabel; LabelGraphicsItem *m_windSpeedLabel; }; // FIXME: Fonts to be defined globally #ifdef Q_OS_MACX QFont WeatherItemPrivate::s_font = QFont( "Sans Serif", 10 ); #else QFont WeatherItemPrivate::s_font = QFont( "Sans Serif", 8 ); #endif QSvgRenderer WeatherItemPrivate::s_windIcons( MarbleDirs::path( "weather/wind-arrows.svgz" ) ); WeatherItem::WeatherItem( QObject *parent ) : AbstractDataPluginItem( parent ), d( new WeatherItemPrivate( this ) ) { setCacheMode( MarbleGraphicsItem::ItemCoordinateCache ); } WeatherItem::~WeatherItem() { delete d; } QAction *WeatherItem::action() { disconnect( d->m_action, SIGNAL( triggered() ), this, SLOT( openBrowser() ) ); connect( d->m_action, SIGNAL( triggered() ), this, SLOT( openBrowser() ) ); return d->m_action; } QString WeatherItem::itemType() const { return QString( "weatherItem" ); } bool WeatherItem::request( const QString& type ) { Q_UNUSED( type ) return false; } bool WeatherItem::initialized() { WeatherData current = currentWeather(); return d->isConditionShown() || d->isTemperatureShown() || d->isWindDirectionShown() || d->isWindSpeedShown(); } bool WeatherItem::operator<( const AbstractDataPluginItem *other ) const { const WeatherItem *weatherItem = qobject_cast(other); if( weatherItem ) { return ( priority() > ( (WeatherItem *) other )->priority() ); } else { return false; } } QString WeatherItem::stationName() const { return d->m_stationName; } void WeatherItem::setStationName( const QString& name ) { d->m_action->setText( name ); d->m_stationName = name; d->updateToolTip(); d->updateLabels(); } WeatherData WeatherItem::currentWeather() const { return d->m_currentWeather; } void WeatherItem::setCurrentWeather( const WeatherData &weather ) { d->m_currentWeather = weather; d->updateToolTip(); d->updateLabels(); emit updated(); } QMap WeatherItem::forecastWeather() const { return d->m_forecastWeather; } void WeatherItem::setForecastWeather( const QMap& forecasts ) { d->m_forecastWeather = forecasts; d->updateToolTip(); emit updated(); } void WeatherItem::addForecastWeather( const QList& forecasts ) { foreach( const WeatherData& data, forecasts ) { QDate date = data.dataDate(); WeatherData other = d->m_forecastWeather.value( date ); if ( !other.isValid() ) { d->m_forecastWeather.insert( date, data ); } else if ( other.publishingTime() < data.publishingTime() ) { d->m_forecastWeather.remove( date ); d->m_forecastWeather.insert( date, data ); } } // Remove old items QDate minDate = QDate::currentDate(); minDate.addDays( -1 ); QMap::iterator it = d->m_forecastWeather.begin(); while( it != d->m_forecastWeather.end() ) { if ( it.key() < minDate ) { d->m_forecastWeather.remove( it.key() ); } it++; } d->updateToolTip(); emit updated(); } quint8 WeatherItem::priority() const { return d->m_priority; } void WeatherItem::setPriority( quint8 priority ) { d->m_priority = priority; } QString WeatherItem::creditHtml() const { return QString(); } void WeatherItem::setSettings( QHash settings ) { if ( d->m_settings == settings ) { return; } d->m_settings = settings; d->updateToolTip(); d->updateLabels(); } void WeatherItem::openBrowser() { QLocale locale = QLocale::system(); if( !d->m_browser ) { d->m_browser = new TinyWebBrowser(); } QString html; html += ""; html += ""; html += "

" + tr( "Weather for %1" ).arg( stationName() ) + "

"; if ( d->m_currentWeather.isValid() ) { html += "

" + tr( "Current Observation" ) + "

"; html += d->m_currentWeather.toHtml( d->temperatureUnit(), d->speedUnit(), d->pressureUnit() ); } if ( !d->m_forecastWeather.isEmpty() ) { html += "

" + tr( "Forecasts" ) + "

"; } foreach ( const WeatherData& data, d->m_forecastWeather ) { QDate date = data.dataDate(); html += "

" + locale.standaloneDayName( date.dayOfWeek() ) + "

"; html += data.toHtml( d->temperatureUnit(), d->speedUnit(), d->pressureUnit() ); } QString credit = creditHtml(); if ( !credit.isEmpty() ) { html += "

"; html += creditHtml(); html += "

"; } html += ""; html += ""; d->m_browser->setHtml( html ); d->m_browser->show(); } } // namespace Marble #include "WeatherItem.moc"