/*************************************************************************\
*   Copyright (C) 2009 by Ulf Kreissig                                    *
*   udev@gmx.net                                                          *
*                                                                         *
*   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 2 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, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
\*************************************************************************/

//--- LOCAL CLASSES ---
#include "../config.h"
#include "countrymap.h"
#include "ionlistmodel.h"
#include "yawp.h"
#include "yawpday.h"
#include "weatherdataprocessor.h"
#include "weatherservice.h"
#include "utils.h"

#include "logger/streamlogger.h"

//--- QT4 CLASSES ---
#include <QActionGroup>
#include <QFile>
#include <QPainter>
#include <QTimer>

//--- KDE4 CLASSES ---
#include <KAboutData>
#include <KAboutApplicationDialog>
#include <KActionMenu>
#include <KColorScheme>
#include <KConfigDialog>
#include <KDateTime>
#include <KGlobalSettings>
#include <KIcon>
#include <KMenu>
#include <KTimeZone>
#include <KSystemTimeZone>
#include <Plasma/ToolTipManager>
#include <Plasma/Theme>
#if KDE_IS_VERSION(4,3,70)
	#include <KUnitConversion/Value>
#endif

#include <limits.h>
#include <math.h>


//--- change the page, e.g.: preview to details ---
#define PAGE_CHANGED          0

//--- change the city, wich will be displayed at the moment ---
#define CITY_CHANGED          1

//--- change the day backward or forward
#define DAY_CHANGED           2


//	cp lib/plasma_yawp3.so /usr/lib/kde4/
//	cp plasma_yawp3.desktop /usr/share/kde4/services/plasma-yawp3.desktop
//
//	plasmoidviewer -c desktop -f horizontal yaWP3


YaWP::YaWP( QObject * parent, const QVariantList & args )
	: Plasma::Applet(parent, args),
	  m_svg(this),
	  m_customSvg(this),
	  m_pConfigDlg(NULL),
	  m_timeline(500, this),
	  m_bSlidingDayNames(true),
	  m_bBusy(false),
	  m_pPanelLayout(NULL),
	  m_iIdPendingEngineConnection(-1)
{
	Q_INIT_RESOURCE(yawpresource);
	dStartFunct() << "============================================\n\n";

	Plasma::DataEngine * pEngine = dataEngine("weather");
	m_storage.setEngine( pEngine );
;
	WeatherDataProcessor * pDataProcessor = new WeatherDataProcessor( &m_storage );
	m_pWeatherModel = new WeatherServiceModel( &m_storage, this, pDataProcessor );
	m_pWeatherModel->setObjectName("EngineModel");

	m_stateMachine.setServiceModel( m_pWeatherModel );

	m_pCurrLayout = &m_desktopLayout;

	//--- Set Default Configuration Values ---
	m_configData.iCityIndex			= 0;
	m_configData.bUseCustomTheme		= false;
	m_configData.bUseCustomThemeBackground	= false;
	m_configData.bUseCustomFontColor	= false;
	m_configData.bDisableTextShadows		= false;
	m_configData.iUpdateInterval		= 45;	// in minutes
	m_configData.iStartDelay            = 0;	// in minutes
	m_configData.iAnimationDuration		= 500;
	m_configData.sBackgroundName		= QLatin1String("default");

	if( KGlobal::locale()->measureSystem() == KLocale::Metric )
	{
	#if KDE_IS_VERSION(4,3,70)
			m_configData.distanceSystem = KUnitConversion::Kilometer;
			m_configData.pressureSystem = KUnitConversion::Kilopascal;
			m_configData.temperatureSystem	= KUnitConversion::Celsius;
			m_configData.speedSystem	= KUnitConversion::KilometerPerHour;
	#else
			m_configData.distanceSystem = WeatherUtils::Kilometers;
			m_configData.pressureSystem = WeatherUtils::Kilopascals;
			m_configData.temperatureSystem	= WeatherUtils::Celsius;
		#if KDE_VERSION_MINOR >= 3
				m_configData.speedSystem	= WeatherUtils::KilometersPerHour;
		#else
				m_configData.speedSystem	= WeatherUtils::KilometersAnHour;
		#endif
	#endif
	}
	else
	{
	#if KDE_IS_VERSION(4,3,70)
		m_configData.distanceSystem = KUnitConversion::Mile;
		m_configData.pressureSystem = KUnitConversion::InchesOfMercury;
		m_configData.temperatureSystem	= KUnitConversion::Fahrenheit;
		m_configData.speedSystem	= KUnitConversion::MilePerHour;
	#else
		m_configData.distanceSystem = WeatherUtils::Miles;
		m_configData.pressureSystem = WeatherUtils::InchesHG;
		m_configData.temperatureSystem	= WeatherUtils::Fahrenheit;
		#if KDE_VERSION_MINOR >= 3
				m_configData.speedSystem	= WeatherUtils::MilesPerHour;
		#else
				m_configData.speedSystem	= WeatherUtils::MilesAnHour;
		#endif
#endif
}
	m_configData.todaysWeatherPanelFormat	= Yawp::PanelTemperature | Yawp::PanelIcon;
	m_configData.forecastWeatherPanelFormat	= Yawp::PanelTemperature | Yawp::PanelIcon;
	m_configData.iPanelForecastDays		= 3;

	m_configData.pageAnimation		= PageAnimator::RollOutHorizontally;
	m_configData.daynamesAnimation		= PageAnimator::RollOutHorizontally;
	m_configData.detailsAnimation		= PageAnimator::CrossFade;
	m_configData.iconAnimation		= PageAnimator::FlipVertically;

	m_configData.fontColor			= QColor(Qt::white);
	m_configData.lowFontColor		= QColor(Qt::gray);
	m_configData.shadowsFontColor		= QColor(0,0,0,100);

	m_configData.bUseCompactPanelLayout		= false;
	m_configData.bUseExtendedTooltip	= true;
	m_configData.extendedTooltipOptions	= Yawp::PreviewPage | Yawp::SatellitePage;

	m_configData.vDetailsPropertyRankingList
		<< Yawp::WeatherDescription
		<< Yawp::SunriseSunset
		<< Yawp::RealfeelTemperature
		<< Yawp::Pressure
		<< Yawp::Visibility
		<< Yawp::Dewpoint
		<< Yawp::UV;

	m_tAnimationType    = NoAnimation;
	m_iAnimationIndex   = 0;		// index of the day we flip the weathericon

	// Contextual actions - thanks to Ezequiel, ported from stdin plasmoid :-)
	m_pManualUpdate = new QAction ( i18n ("&Refresh"), this );
	m_pManualUpdate->setIcon ( KIcon ( "view-refresh" ) );
	addAction( "refresh", m_pManualUpdate );
	connect ( m_pManualUpdate, SIGNAL(triggered()), m_pWeatherModel, SLOT(reconnectEngine()) );

	QAction * aboutAction = new QAction(i18n("&About"), this);
	aboutAction->setIcon( KIcon("help-about") );
	addAction( "about", aboutAction );
	connect( aboutAction, SIGNAL(triggered()), this, SLOT(about()) );

	QAction * separator1 = new QAction ( this );
	separator1->setSeparator ( true );

	m_pCitySubMenu = new KActionMenu(KIcon("preferences-desktop-locale"), i18n("Show city"), this);
	m_pCitySubMenu->setEnabled( false );
	m_pGrpActionCities = new QActionGroup( this );
	m_pGrpActionCities->setExclusive( true );
	connect( m_pGrpActionCities, SIGNAL(triggered(QAction *)), this, SLOT(changeCity(QAction *)) );

	QAction * separator2 = new QAction ( this );
	separator2->setSeparator ( true );

	m_actions.append( m_pManualUpdate );
	m_actions.append( aboutAction );
	m_actions.append( separator1 );
	m_actions.append( m_pCitySubMenu );
	m_actions.append( separator2 );

	/*** About data ***/
	m_aboutData = new KAboutData ( "plasma_yawp",
		QByteArray (), ki18n( YAWP_NAME ), YAWP_VERSION_STRING,
		ki18n( "Yet Another Weather Applet" ),
		KAboutData::License_GPL,
		ki18n( "Copyright (C) 2008-2010 Ruan Strydom\n"
		       "Copyright (C) 2008-2010 Ezequiel R. Aguerre\n"
		       "Copyright (C) 2008-2010 Pierpaolo Vittorini\n"
		       "Copyright (C) 2008-2010 Marián Kyral\n"
		       "Copyright (C) 2009-2010 Ulf Kreißig" ),
		ki18n( "This plasmoid shows for the selected place "
		       " current weather and forecast for 4 days." ),
		"http://www.kde-look.org/content/show.php?content=94106",
		"plasmafactory@jcell.co.za" );

	//--- Authors ---
	m_aboutData->addAuthor( ki18n("Ulf Kreißig"),
		ki18n("Developer"), "udev@gmx.net" );
	m_aboutData->addAuthor( ki18n("Marián Kyral" ),
		ki18n("Developer"), "mkyral@email.cz" );
	m_aboutData->addAuthor( ki18n("Ezequiel R. Aguerre"),
		ki18n("Developer"), "ezeaguerre@gmail.com" );
	m_aboutData->addAuthor( ki18n("Pierpaolo Vittorini"),
		ki18n("Developer"), "pierpaolo.vittorini@gmail.com" );
	m_aboutData->addAuthor( ki18n("Ruan Strydom"),
		ki18n("Developer"), "ruans@kr8.co.za" );

/*
      Copyright 2007 Thomas Luebking <thomas.luebking@web.de>
      Copyright 2008 Aleix Pol <aleixpol@gmail.com>
      Copyright 2007 Frerich Raabe <raabe@kde.org>
      Copyright 2007 Aaron Seigo <aseigo@kde.org>
*/

	//--- Credits ---
	m_aboutData->addCredit ( ki18n("Ulf Kreißig"),
		ki18n("Complete rewrite!" ), "udev@gmx.net" );
	m_aboutData->addCredit ( ki18n("Ruan Strydom"),
		ki18n("Idea, graphics" ), "ruans@kr8.co.za" );
	m_aboutData->addCredit( ki18n("AccuWeather.com"),
		ki18n("For the weather data"), "", "http://www.accuweather.com"  );
	m_aboutData->addCredit( ki18n("Thomas Luebking"),
		ki18n("Algorithm for animated page-changes is a slightly modified version from Bespin. (Qt4 Style)"),
		"thomas.luebking@web.de" );
	m_aboutData->addCredit( ki18n("das Gnu"),
		ki18n("German translation"), "dasgnu@gmail.com" );
	m_aboutData->addCredit( ki18n("Jesús Vidal Panalés"),
		ki18n("Spanish translation"));
	m_aboutData->addCredit( ki18n("Bribanick Dominique"),
		ki18n("French translation"), "chepioq@gmail.com" );
	m_aboutData->addCredit( ki18n("Mihail Pisanov"),
		ki18n("Russian translation"), "miha@52.ru" );
	m_aboutData->addCredit( ki18n("Richard Frič"),
		ki18n("Slovak translation"), "Richard.Fric@kdemail.net" );
	m_aboutData->addCredit( ki18n("Mihael Simonič"),
		ki18n("Slovenian translation"), "smihael@gmail.com" );
	m_aboutData->addCredit( ki18n("Maciej Bulik"),
		ki18n("Polish translation"), "Maciej.Bulik@post.pl" );
	m_aboutData->addCredit( ki18n("All people reporting bugs, send feedback and new feature requests") );

	m_aboutData->setProgramIconName("weather-clear");

	//--- Translators ---
	m_aboutData->setTranslator( ki18nc("NAME OF THE TRANSLATORS", "Your names"),
		ki18nc("EMAIL OF THE TRANSLATORS", "Your emails") );
	/*** About data end ***/

	connect( &m_timeline,       SIGNAL(frameChanged(int)),  this,   SLOT(animationTimeout(int)) );
	connect( &m_timeline,       SIGNAL(finished()),	        this,   SLOT(animationFinished()) );
	connect( m_pWeatherModel,   SIGNAL(isBusy(bool)),       this,   SLOT(setBusy(bool)) );
	connect( m_pWeatherModel,   SIGNAL(cityUpdated(WeatherServiceModel::ServiceUpdate)),
			 this,              SLOT(slotCityUpdate(WeatherServiceModel::ServiceUpdate)) );
	connect( Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),	this,	SLOT(setDefaultFontColors()) );

	resize( m_desktopLayout.getSize() );
	setHasConfigurationInterface(true);
	setAspectRatioMode(Plasma::KeepAspectRatio);

	dEndFunct();
}

YaWP::~YaWP()
{
	dStartFunct();
	Plasma::ToolTipManager::self()->clearContent(this);
	if( hasFailedToLaunch() )
	{
		// Do some cleanup here
	}
	else
	{
		// Save settings
		saveConfig();
	}
	if( m_pPanelLayout )
		delete m_pPanelLayout;
	delete m_pWeatherModel;
	delete m_aboutData;
	
	dEndFunct();
}

void
YaWP::setBusy( bool bBusy )
{
	Plasma::Applet::setBusy( bBusy );
	m_bBusy = bBusy;
	if( !m_bBusy )
		m_sCityVisualName = createVisualCityName( m_stateMachine.currentCity() );
	update();
}

void
YaWP::init()
{
	dStartFunct();
	m_svg.setImagePath("widgets/yawp_theme15");
	m_svg.setContainsMultipleImages( true );

	loadConfig();
	loadCustomTheme();
	setButtonNames();
	updateCitySubMenu();
	setupAnimationTimeLine();
	setCityIndex( m_configData.iCityIndex );

	const bool bHasCities = (m_pWeatherModel->rowCount() > 0);
	m_pManualUpdate->setEnabled( bHasCities );
	m_pCitySubMenu->setEnabled( bHasCities);

	if( bHasCities )
		m_iIdPendingEngineConnection = startTimer( m_configData.iStartDelay*60*1000 );
	else
		QTimer::singleShot( 20, this, SLOT(slotConfigurationRequired()) );
	dEndFunct();
}

void
YaWP::slotConfigurationRequired()
{
	setConfigurationRequired( true );
}

void
YaWP::loadConfig()
{
	dStartFunct();
	KConfigGroup cfg = config();

	/*** SETTINGS PAGE ***/
	m_configData.iUpdateInterval
		= cfg.readEntry( "update interval", QVariant(m_configData.iUpdateInterval) ).toInt();
	m_configData.iStartDelay
		= cfg.readEntry( "start delay", QVariant(m_configData.iStartDelay) ).toInt();

	m_configData.distanceSystem
		= (YAWP_DISTANCE_UNIT)cfg.readEntry( "system.distance",
			(int)m_configData.distanceSystem );
	m_configData.pressureSystem
		= (YAWP_PRESSURE_UNIT)cfg.readEntry( "system.pressure",
		 	(int)m_configData.pressureSystem );
	m_configData.temperatureSystem
		= (YAWP_TEMPERATURE_UNIT)cfg.readEntry( "system.temperature",
			(int)m_configData.temperatureSystem );
	m_configData.speedSystem
		= (YAWP_SPEED_UNIT)cfg.readEntry( "system.speed",
			(int)m_configData.speedSystem );

	m_configData.daynamesAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.daysnames",
			(int)m_configData.daynamesAnimation );
	m_configData.detailsAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.details",
			(int)m_configData.detailsAnimation );
	m_configData.pageAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.page",
			(int)m_configData.pageAnimation );
	m_configData.iconAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.icon",
			(int)m_configData.iconAnimation );
	m_configData.iAnimationDuration
		= cfg.readEntry( "animation.duration",
			(int)m_configData.iAnimationDuration );

	/*** PANEL PAGE ***/
	m_configData.todaysWeatherPanelFormat
		= (Yawp::PanelDayFormat)cfg.readEntry( "panel.today.format",
			(int)m_configData.todaysWeatherPanelFormat );
	m_configData.forecastWeatherPanelFormat
		= (Yawp::PanelDayFormat)cfg.readEntry( "panel.forecast.format",
			(int)m_configData.forecastWeatherPanelFormat );
	m_configData.iPanelForecastDays
		= cfg.readEntry( "panel.forecast.days",	m_configData.iPanelForecastDays );
	m_configData.bUseCompactPanelLayout
		= cfg.readEntry( "panel.layout.compact", m_configData.bUseCompactPanelLayout );

	/*** PANEL TOOLTIP ***/
	m_configData.bUseExtendedTooltip
		= cfg.readEntry( "panel.tooltip.extended.enabled", m_configData.bUseExtendedTooltip );
	m_configData.extendedTooltipOptions
		= (Yawp::ExtendedTooltipOptions)cfg.readEntry( "panel.tooltip.extended.format",
			(int)m_configData.extendedTooltipOptions );

	/*** THEME PAGE ***/
	m_configData.sBackgroundName
		= cfg.readEntry( "theme", m_configData.sBackgroundName );
	m_configData.sCustomThemeFile
		= cfg.readEntry( "custom.theme.file", m_configData.sCustomThemeFile );
	m_configData.bUseCustomTheme
		= cfg.readEntry( "custom.theme.enabled", m_configData.bUseCustomTheme );
	m_configData.bUseCustomThemeBackground
		= cfg.readEntry( "custom.theme.background.enabled",
			QVariant( m_configData.bUseCustomThemeBackground ) ).toBool();

	m_configData.fontColor
		= cfg.readEntry( "custom.fontcolor.normal", m_configData.fontColor );
	m_configData.lowFontColor
		= cfg.readEntry( "custom.fontcolor.lowtemp", m_configData.lowFontColor );
	m_configData.shadowsFontColor
		= cfg.readEntry( "custom.fontcolor.shadows", m_configData.shadowsFontColor );
	m_configData.bUseCustomFontColor
		= cfg.readEntry( "custom.fontcolor.usage", m_configData.bUseCustomFontColor );
	m_configData.bDisableTextShadows
		= cfg.readEntry( "text.shadows", m_configData.bDisableTextShadows );
	if( !m_configData.bUseCustomFontColor )
		setDefaultFontColors();

	setupWeatherServiceModel();

	/*** LOCATION PAGE ***/
	if( cfg.hasGroup("locations") )
	{
		KConfigGroup	cityCfg = cfg.group("locations");
		QString		sKey;
		QStringList	lst;
		int		iCityIndex = 0;
		CityWeather	cityInfo;

		while( true )
		{
			sKey = QString("city%1").arg(iCityIndex+1, 2, 10, QChar('0'));
			if( !cityCfg.hasKey( sKey ) )
				break;
			lst = cityCfg.readEntry(sKey, QStringList());
			if( lst.count() < 5 )
				break;

			cityInfo.clear();
			cityInfo.setProvider(    QUrl::fromPercentEncoding(lst.at(0).toUtf8()) );
			cityInfo.setCity(        QUrl::fromPercentEncoding(lst.at(1).toUtf8()) );
			cityInfo.setCountry(     QUrl::fromPercentEncoding(lst.at(2).toUtf8()) );
			cityInfo.setCountryCode( QUrl::fromPercentEncoding(lst.at(3).toUtf8()) );
			cityInfo.setExtraData(   QUrl::fromPercentEncoding(lst.at(4).toUtf8()) );
			
			if( lst.count() > 5 )
				cityInfo.setTimeZone( QUrl::fromPercentEncoding(lst.at(5).toUtf8()) );
			if( !cityInfo.timeZone().isValid() )
			{
				QStringList vTimeZones( Utils::GetTimeZones( cityInfo, &m_storage ) );
				if( vTimeZones.count() == 1 )
					cityInfo.setTimeZone( vTimeZones.at(0) );
			}
			if( cityInfo.countryCode().isEmpty() && cityInfo.timeZone().isValid() )
				cityInfo.setCountryCode( cityInfo.timeZone().countryCode() );
			
			dDebug() << cityInfo.provider() << cityInfo.city() << cityInfo.country() << cityInfo.countryCode()
			         << cityInfo.extraData() << cityInfo.timeZone().name() << cityInfo.timeZone().countryCode();
			
			m_pWeatherModel->addCity( cityInfo );

			iCityIndex += 1;
		}
		m_configData.iCityIndex = cityCfg.readEntry("selected", 0);	// currently shown city in the applet
	}
	else
		m_configData.iCityIndex = 0;
	dEndFunct();
}

void
YaWP::saveConfig()
{
	dStartFunct();
	KConfigGroup cfg = config();

	/*** SETTINGS PAGE ***/
	cfg.writeEntry( "update interval",			(int)m_configData.iUpdateInterval );
	cfg.writeEntry( "start delay",				(int)m_configData.iStartDelay );

	cfg.writeEntry( "system.distance",          (int)m_configData.distanceSystem );
	cfg.writeEntry( "system.pressure",          (int)m_configData.pressureSystem );
	cfg.writeEntry( "system.temperature",		(int)m_configData.temperatureSystem );
	cfg.writeEntry( "system.speed",			(int)m_configData.speedSystem );

	cfg.writeEntry( "animation.daysnames",		(int)m_configData.daynamesAnimation );
	cfg.writeEntry( "animation.details",		(int)m_configData.detailsAnimation );
	cfg.writeEntry( "animation.page",		(int)m_configData.pageAnimation );
	cfg.writeEntry( "animation.icon",		(int)m_configData.iconAnimation );
	cfg.writeEntry( "animation.duration",		(int)m_configData.iAnimationDuration );

	/*** PANEL PAGE ***/
	cfg.writeEntry( "panel.today.format",		(int)m_configData.todaysWeatherPanelFormat );
	cfg.writeEntry( "panel.forecast.format",	(int)m_configData.forecastWeatherPanelFormat );
	cfg.writeEntry( "panel.forecast.days",		m_configData.iPanelForecastDays );
	cfg.writeEntry( "panel.layout.compact", 	m_configData.bUseCompactPanelLayout );

	cfg.writeEntry( "panel.tooltip.extended.enabled",	m_configData.bUseExtendedTooltip );
	cfg.writeEntry( "panel.tooltip.extended.format",	(int)m_configData.extendedTooltipOptions );

	/***  THEME PAGE ***/
	cfg.writeEntry( "theme",				m_configData.sBackgroundName );
	cfg.writeEntry( "custom.theme.file",			m_configData.sCustomThemeFile );
	cfg.writeEntry( "custom.theme.enabled",			m_configData.bUseCustomTheme );
	cfg.writeEntry( "custom.theme.background.enabled",	m_configData.bUseCustomThemeBackground );

	cfg.writeEntry( "custom.fontcolor.normal",	m_configData.fontColor );
	cfg.writeEntry( "custom.fontcolor.lowtemp",	m_configData.lowFontColor );
	cfg.writeEntry( "custom.fontcolor.shadows",	m_configData.shadowsFontColor );
	cfg.writeEntry( "custom.fontcolor.usage",	m_configData.bUseCustomFontColor );
	cfg.writeEntry( "text.shadows",	m_configData.bDisableTextShadows );

	/*** LOCATION PAGE ***/
	if( cfg.hasGroup("locations") )
		cfg.group("locations").deleteGroup();	// delete all old entries in this group
	if( m_pWeatherModel->rowCount() > 0 )
	{
		KConfigGroup cityCfg = cfg.group("locations");
		for( int iCityIndex = 0; iCityIndex < m_pWeatherModel->rowCount(); ++iCityIndex )
		{
			QStringList lst;
			const CityWeather * pCity = m_pWeatherModel->getCityInfo( iCityIndex );
			lst << pCity->provider()
			    << pCity->city()
			    << pCity->country()
			    << pCity->countryCode()
			    << pCity->extraData()
				<< pCity->timeZone().name();
			QString sKey = QString("city%1").arg(iCityIndex+1, 2, 10, QChar('0'));
			cityCfg.writeEntry( sKey, lst );
		}
		cityCfg.writeEntry("selected", m_configData.iCityIndex);
	}
	dEndFunct();
}

void
YaWP::loadCustomTheme()
{
	dStartFunct();
	if( !m_configData.bUseCustomTheme )
	{
		dEndFunct();
		return;
	}
	if( !QFile(m_configData.sCustomThemeFile).exists() )
	{
		m_configData.bUseCustomTheme = false;
		dDebug() << "File does not exist: " << m_configData.sCustomThemeFile;
		return;
	}
	/*TODO get the following paramaters from theme:
		rect The rectangle to draw in.
		font The font to use.
		color The font color to use.
	*/
	m_customSvg.setImagePath( m_configData.sCustomThemeFile );
	m_customSvg.setContainsMultipleImages( true );
	dEndFunct();
}

void
YaWP::setButtonNames()
{
	m_vButtonNames.clear();
	const Plasma::Svg * pSvg = (m_configData.bUseCustomTheme && m_customSvg.isValid() ? &m_customSvg : &m_svg);
	if( pSvg->hasElement("actual") )
		m_vButtonNames << "actual" << "info" << "map";
	else	//--- fallback solution for old theme files
		m_vButtonNames << "map" << "map" << "map";
}


void
YaWP::constraintsEvent(Plasma::Constraints constraints)
{
	dStartFunct() << "constraints = " << constraints;
	const bool bPanelMode = (m_pPanelLayout ? true : false);	// Are we in the panel mode, right now?
	const Plasma::FormFactor form(formFactor());
	
	initPanelLayoutObject( form );
	if( m_pPanelLayout )	//--- we are in panelmode ---
	{
		if( constraints.testFlag(Plasma::FormFactorConstraint) )
		{
			if( form == Plasma::Vertical )
			{
				setMinimumWidth(0);
				setMaximumWidth(10000);
			}
			else
			{
				setMinimumHeight(0);
				setMaximumHeight(10000);
			}
		}

		if( constraints.testFlag(Plasma::FormFactorConstraint) ||
		    constraints.testFlag(Plasma::SizeConstraint) )
		{
			qreal top, bottom, left, right;
			getContentsMargins(&left, &top, &right, &bottom);
			qreal borderHeight = top + bottom;
			qreal borderWidth = left + right;

			QSizeF size( m_pPanelLayout->getSize( contentsRect().size() ) );
		  
			if( form == Plasma::Vertical &&
			  qAbs(size.height() - m_pPanelLayout->contentsRect().height()) >= 2.0f )
			{
				setMaximumHeight( size.height() + borderHeight );
				// we need to force to resize, otherwise it might not change size
				setMinimumHeight( size.height() + borderHeight );
				m_pPanelLayout->setContentsRect( QRectF(left,top, size.width()+borderWidth, size.height()+borderHeight) );

			}
			else if( form == Plasma::Horizontal &&
			         qAbs(size.width() - m_pPanelLayout->contentsRect().width()) >= 2 )
			{
				setMaximumWidth( size.width() + borderWidth );
				// we need to force to resize, otherwise it might not change size
				setMinimumWidth( size.width() + borderWidth );
				m_pPanelLayout->setContentsRect( QRectF(left,top, size.width()+borderWidth, size.height()+borderHeight) );
			}
		}
	}
	else	//--- we are in desktopmode ---
	{
		if( constraints.testFlag( Plasma::FormFactorConstraint ) )
		{
			setMinimumWidth(0);
			setMinimumHeight(0);
			setMaximumWidth(10000);
			setMaximumHeight(10000);

			if( m_configData.sBackgroundName == QLatin1String("default") &&
			    !(m_configData.bUseCustomTheme && m_configData.bUseCustomThemeBackground) )
				setBackgroundHints(DefaultBackground);
			else
				// no backround on panel
				setBackgroundHints(NoBackground);

			if( bPanelMode )
			{
				resize( m_desktopLayout.getSize() );	// set the default size
				Plasma::ToolTipManager::self()->clearContent(this);
			}
		}
	}
	dEndFunct();
}

// The paintInterface procedure paints the applet to screen
void
YaWP::paintInterface( QPainter * painter,
                      const QStyleOptionGraphicsItem * option,
                      const QRect & contentsRect )
{
	Q_UNUSED(option);
	dStartFunct();
	const Plasma::FormFactor form = formFactor();

	setRenderOptions( painter, false );

	if( form == Plasma::Horizontal || form == Plasma::Vertical )
	{
		if( m_tAnimationType == PageChange )
		{
			m_pageAnimator.paint( painter,
			                      QRectF(QPointF(0.0,0.0), m_pPanelLayout->getSize()),
			                      m_timeline.currentFrame() );
		}
		else
		{
			initPanelLayoutObject( form );
			m_pPanelLayout->setContentsRect( contentsRect );
			paintPanelInformation( painter );
		}
	}
	else	//--- Draw the applet on the desktop ---
	{
		m_desktopLayout.setContentsRect( contentsRect );
		m_desktopLayout.setUserDefinedDetailsRect( m_desktopLayout.getDetailsRect() );

//		painter->eraseRect(contentsRect);
		
		/*** Now we draw the applet, starting with our svg ***
		*/
		drawBackground(painter, contentsRect);

		if( m_tAnimationType == PageChange )
			m_pageAnimator.paint(painter, m_desktopLayout.getPageRect(), m_timeline.currentFrame() );
		else
			paintCurrentPage( painter );
	}
	dEndFunct();
}

void
YaWP::createConfigurationInterface( KConfigDialog * parent )
{
	dStartFunct();
	if (m_pConfigDlg != NULL)
		delete m_pConfigDlg;
	m_pConfigDlg = new YawpConfigDialog( parent, &m_storage );
	m_pConfigDlg->copyCities( m_pWeatherModel );
	m_pConfigDlg->setData( &m_configData );

	connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
	connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
	dEndFunct();
}

void
YaWP::configAccepted()
{
	if( ! m_pConfigDlg )
		return;

	dStartFunct();
	m_pWeatherModel->disconnectEngine();
	stopPendingEngineConnection();

	dDebug() << "configuration changed..."
	         << "    model changed" << m_pConfigDlg->cityModelChanged();
	if( m_pConfigDlg->cityModelChanged() )
	{
		dDebug() << "update model";
		m_pWeatherModel->copyCities( *m_pConfigDlg->weatherModel() );
		updateCitySubMenu();
	}
	else
		dDebug() << "model did not change...";

	m_pConfigDlg->getData( &m_configData );
	saveConfig();
	emit configNeedsSaving();

	setupAnimationTimeLine();
	setupWeatherServiceModel();

	setCityIndex( m_configData.iCityIndex );
	loadCustomTheme();
	setButtonNames();

	if( !m_configData.bUseCustomFontColor )
		setDefaultFontColors();
	if( m_pPanelLayout )
		m_pPanelLayout->setUseCompactLayout( m_configData.bUseCompactPanelLayout );

	const bool bHasCities = (m_pWeatherModel->rowCount() > 0);
	setConfigurationRequired( !bHasCities );
	m_pManualUpdate->setEnabled( bHasCities);
	m_pCitySubMenu->setEnabled( bHasCities );

	//--- trigger the update-process ---
	if( bHasCities )
		m_iIdPendingEngineConnection = startTimer( m_pConfigDlg->citySettingsChanged() ? 0 : 3 * 60 * 1000 );
		
	// to make sure we will change the theme
	constraintsEvent( Plasma::FormFactorConstraint | Plasma::SizeConstraint );
	update();

	dEndFunct();
}

void
YaWP::mousePressEvent( QGraphicsSceneMouseEvent * event )
{
	dStartFunct() << event->button() << event->button();
	const CityWeather * const pCity = m_stateMachine.currentCity();
	if( !pCity || m_timeline.state() == QTimeLine::Running || event->button() != Qt::LeftButton )
	{
		dEndFunct();
		return;
	}

	m_pCurrLayout->setContentsRect( contentsRect() );
	const Yawp::PageType currPage( m_stateMachine.currentPage() );

	//--- check if the user clicked on a wheathericon ---
	if( m_pPanelLayout || currPage == Yawp::PreviewPage )
	{
		QRectF rect;
		const int iMaxDays( qMin(5, pCity->days().count()) );

		//--- Check if we clicked on todays or on one of the small weatherforecast icons ---
		for( int iDay = 0; iDay < iMaxDays; ++iDay )
		{
			if( !pCity->days().at(iDay)->hasNightValues() )
				continue;

			if( !m_pPanelLayout && iDay == 0 )
				rect = m_pCurrLayout->getTodaysIconRect();
			else
				rect = m_pCurrLayout->getForecastIconRect( iDay );
			if( rect.contains(event->pos()) )
			{
				initWeatherIconAnimation( rect, iDay );
				event->accept();
				dEndFunct();
				return;
			}
		}
	}
	//--- there is nothing to do for panellayouts anymore ---
	if( m_pPanelLayout )
	{
		dEndFunct();
		return;
	}
	const bool bHasPreviewPage( m_stateMachine.hasPage( Yawp::PreviewPage ) );
	const bool bHasDetailsPage( m_stateMachine.hasPage( Yawp::DetailsPage ) );
	const bool bHasSatellitePage( m_stateMachine.hasPage( Yawp::SatellitePage ) );
	const QRectF rectBtnPreview( getButtonRect(0) );
	const QRectF rectBtnDetails( getButtonRect(1) );
	const QRectF rectBtnSatellite( getButtonRect(2) );
	const int iDetailsDayIndex( m_stateMachine.detailsDayIndex() );
	const int iDetailsPageIndex( m_stateMachine.currentPropertyPage() );
	
	//--- one of the first two buttons has been pressed ---
	if( (currPage != Yawp::DetailsPage && bHasDetailsPage && rectBtnDetails.contains(event->pos())) ||
	    (currPage != Yawp::PreviewPage && bHasPreviewPage && rectBtnPreview.contains(event->pos())) )
	{
/*		update(rectBtnPreview);
		update(rectBtnDetails);
		update(rectBtnSatellite);
*/
		if( m_configData.detailsAnimation == PageAnimator::Jump && currPage != Yawp::SatellitePage)
		{
			if( bHasPreviewPage && rectBtnPreview.contains(event->pos()) )
				m_stateMachine.setCurrentPage(Yawp::PreviewPage);
			else
				m_stateMachine.setCurrentPage(Yawp::DetailsPage);
//			update( m_desktopLayout.getUserDefinedDetailsRect() );
			update();
		}
		else if( iDetailsDayIndex > 0 || currPage == Yawp::SatellitePage )
		{
			int iVal = (rectBtnDetails.contains(event->pos()) ? Yawp::DetailsPage : Yawp::PreviewPage);
			initPageChange( PAGE_CHANGED, iVal, currPage < iVal );
		}
		else
				/*  just update the details rectangle since we see the first day
		         *  and the current page is either PreviewPage or DetailsPage.
		         */
		{
			Yawp::PageType newPage( currPage == Yawp::PreviewPage ? Yawp::DetailsPage : Yawp::PreviewPage );
			initDetailsChange( newPage, iDetailsPageIndex );
		}
		event->accept();
		dEndFunct();
		return;
	}
	//--- third button has been clicked - Show Map ---
	else if( bHasSatellitePage &&
	         currPage != Yawp::SatellitePage &&
	         rectBtnSatellite.contains(event->pos()) )
	{
		initPageChange( PAGE_CHANGED, Yawp::SatellitePage, true );
		event->accept();
		dEndFunct();
		return;
	}
	else if( currPage == Yawp::PreviewPage )
	{
		QRectF rect;

		//--- Check if we clicked on the daysnames / daynumbers ---
		rect = m_desktopLayout.getForecastDayNamesRect();
		if( rect.contains(event->pos()) )
		{
			bool bDayNames = m_bSlidingDayNames;
			m_bSlidingDayNames = !bDayNames;

			if( m_configData.daynamesAnimation == PageAnimator::Jump )
			{
//				update( rect );
				update();
			}
			else
			{
				const QRectF rectOrgDetailsRect( m_desktopLayout.getUserDefinedDetailsRect() );
				rect.moveTo(0.0f, 0.0f);
				m_desktopLayout.setUserDefinedDetailsRect( rect );
				m_pageAnimator.resetPages((int)rect.width(), (int)rect.height());

				QPainter painter;
				painter.begin( &m_pageAnimator.vPages[0] );
				setRenderOptions( &painter, true );
				paintForecastDayNames( &painter, bDayNames );
				painter.end();

				painter.begin( &m_pageAnimator.vPages[1] );
				setRenderOptions( &painter, true );
				paintForecastDayNames( &painter, !bDayNames );
				painter.end();

				m_desktopLayout.setUserDefinedDetailsRect( rectOrgDetailsRect );

				m_tAnimationType = SlidingDayNames;
				m_pageAnimator.setTransition(
					getSlidingTransition( m_configData.daynamesAnimation, bDayNames ));
				m_timeline.start();
			}
			event->accept();
			dEndFunct();
			return;
		}
	}
	else if( currPage == Yawp::DetailsPage )
	{
		//--- check if we pressed on the big weather icon on the top ---
		QRectF rect;
		const YawpDay * pDay = m_stateMachine.day( iDetailsDayIndex );
		rect = m_desktopLayout.getTodaysIconRect();
		if( pDay && pDay->hasNightValues() && rect.contains(event->pos()) )
		{
			initWeatherIconAnimation( rect, iDetailsDayIndex );
			event->accept();
			dEndFunct();
			return;
		}

		//--- check backward/foreward buttons day-detail-changes ---
		if( iDetailsDayIndex > 0 &&
		    m_desktopLayout.getDetailsArrowRect(false).contains(event->pos()) )
		{
			initPageChange( DAY_CHANGED, iDetailsDayIndex-1, false );
			event->accept();
			dEndFunct();
			return;
		}
		else if( iDetailsDayIndex+1 < pCity->days().count() &&
		         m_desktopLayout.getDetailsArrowRect(true).contains(event->pos()) )
		{
			initPageChange( DAY_CHANGED, iDetailsDayIndex+1, true );
			event->accept();
			dEndFunct();
			return;
		}

		int iTmp = m_desktopLayout.getForecastDayNamesRect().height();
		rect = m_desktopLayout.getDetailsRect();
		rect.setHeight( m_desktopLayout.getDetailsRect().height()-iTmp );
		rect.moveTo( rect.left(), rect.top()+iTmp );
		int iMaxPages( m_stateMachine.maxPropertyPage() );
		if( rect.contains(event->pos()) && iMaxPages > 1 )
		{
			initDetailsChange( Yawp::DetailsPage, m_stateMachine.currentPropertyPage()+1 );
			event->accept();
			dEndFunct();
			return;
		}
	}

	/*** check backward/foreward buttons for city-changes
	*    the city and the buttons will be shown on all pages (just on different positions)
	*    so we will handle it here
	*/
	if( m_configData.iCityIndex > 0 &&
	    getCityArrowRect(false).contains(event->pos()) )
	{
		initPageChange( CITY_CHANGED, m_configData.iCityIndex-1, false );
		event->accept();
		dEndFunct();
		return;
	}
	else if( m_configData.iCityIndex+1 < m_pWeatherModel->rowCount() &&
	         getCityArrowRect(true).contains(event->pos()) )
	{
		initPageChange( CITY_CHANGED, m_configData.iCityIndex+1, true );
		event->accept();
		dEndFunct();
		return;
	}
	dEndFunct();
}

void
YaWP::timerEvent ( QTimerEvent * event )
{
	if( event->timerId() == m_iIdPendingEngineConnection )
	{
		stopPendingEngineConnection();
		m_pWeatherModel->reconnectEngine();
	}
	Plasma::Applet::timerEvent(event);
}

void
YaWP::slotCityUpdate(WeatherServiceModel::ServiceUpdate updateType)
{
	dStartFunct();
	stopPendingEngineConnection();
	const CityWeather * const pCity = m_stateMachine.currentCity();
	if(!pCity)
	{
		dEndFunct();
		return;
	}
	m_desktopLayout.setForecastDays( pCity->days().count()-1 );
	m_sCityVisualName = createVisualCityName( pCity );
	
	if( updateType.testFlag(WeatherServiceModel::CityInfoUpdate) )
	{
		updateCitySubMenu();
		saveConfig();
		emit configNeedsSaving();
	}

	//--- update the paneltooltip when we are in panel mode only ---
	if( m_pPanelLayout )
		createPanelTooltip();
	update();

	dEndFunct();
}


/***************************************************************************\
*
\***************************************************************************/

void
YaWP::changeCity( QAction * action )
{
	int iSelectedCityIndex = action->data().toInt();
	if( iSelectedCityIndex >= 0 &&
	    iSelectedCityIndex < m_pWeatherModel->rowCount() &&
	    iSelectedCityIndex != m_configData.iCityIndex )
	{
		initPageChange( CITY_CHANGED, iSelectedCityIndex, iSelectedCityIndex > m_configData.iCityIndex );
	}
}

void
YaWP::about() const
{
	KAboutApplicationDialog * aboutDialog = new KAboutApplicationDialog ( m_aboutData );
	connect ( aboutDialog, SIGNAL (finished()), aboutDialog, SLOT(deleteLater()) );
	aboutDialog->show ();
}

void
YaWP::initPageChange( const int iType, const int iNewValue, const bool bForward )
{
	dStartFunct();
	QRectF pageRect;
	pageRect = (m_pPanelLayout ? m_pPanelLayout->contentsRect() : m_desktopLayout.getPageRect());

	/***  when the user selected an animation than draw the current page
	*/
	if( m_configData.pageAnimation != PageAnimator::Jump )
	{
		m_pageAnimator.resetPages( (int)pageRect.width(), (int)pageRect.height() );
		createAnimationPage( 0, iType, pageRect );
	}

	/*** change the page according the selectd type
	*/
	switch( iType )
	{
	case CITY_CHANGED:
		setCityIndex( iNewValue );
		if( m_pPanelLayout )
			createPanelTooltip();
		break;
	case DAY_CHANGED:
		m_stateMachine.setDetailsDayIndex( iNewValue );
		break;
	case PAGE_CHANGED:
		m_stateMachine.setCurrentPage( (Yawp::PageType)iNewValue );
		if( m_stateMachine.currentPage() == Yawp::PreviewPage ||
		    m_stateMachine.currentPage() == Yawp::SatellitePage )
		{
 			m_stateMachine.setDetailsDayIndex( 0 );
			m_stateMachine.setCurrentPropertyPage( 0 );
		}
		break;
	}

	/***  when the user selected an animation than draw the new page
	*     and start the page animation
	*/
	if( m_configData.pageAnimation != PageAnimator::Jump )
	{
		createAnimationPage( 1, iType, pageRect );

		m_pageAnimator.setTransition( getSlidingTransition(m_configData.pageAnimation, bForward) );
		m_tAnimationType = PageChange;
		m_timeline.start();
	}
	else
		update();
	dEndFunct();
}

void
YaWP::initDetailsChange( const Yawp::PageType newPage, const int iNewValue )
{
	dStartFunct();
	const QRectF rectOrgDetailsRect( m_desktopLayout.getUserDefinedDetailsRect() );
	QRectF rect( QPointF(0.0f, 0.0f), m_desktopLayout.getDetailsRect().size() );
	QPainter painter;

	//--- paint current details page on first animation page ---
	if( m_configData.detailsAnimation != PageAnimator::Jump )
	{
		m_desktopLayout.setUserDefinedDetailsRect( rect );
		m_pageAnimator.resetPages((int)rect.width(), (int)rect.height());

		painter.begin( &m_pageAnimator.vPages[0] );
		setRenderOptions( &painter, true );
		if( m_stateMachine.currentPage() == Yawp::PreviewPage )
			paintWeatherPreview( &painter );
		else
			paintWeatherDetails( &painter );
		painter.end();
	}

	//--- Set the new state ---
	m_stateMachine.setCurrentPage( newPage );
	const Yawp::PageType currPage = m_stateMachine.currentPage();
	if( currPage == Yawp::DetailsPage )
		m_stateMachine.setCurrentPropertyPage( iNewValue, true );

	//--- paint new details page on second animation page ---
	if( m_configData.detailsAnimation != PageAnimator::Jump )
	{
		painter.begin( &m_pageAnimator.vPages[1] );
		setRenderOptions( &painter, true );

		//--- paint the new page on the second page ---
		if( currPage == Yawp::PreviewPage )
			paintWeatherPreview( &painter );
		else if( currPage == Yawp::DetailsPage )
			paintWeatherDetails( &painter );
		painter.end();

		//--- we need to restore the original rectangle ---
		m_desktopLayout.setUserDefinedDetailsRect( rectOrgDetailsRect );

		//--- start animation ---
		m_pageAnimator.setTransition(
			getSlidingTransition(m_configData.detailsAnimation, currPage == Yawp::PreviewPage) );
		m_tAnimationType = DetailsChange;
		m_timeline.start();
	}
	else
		update();
	dEndFunct();
}

void
YaWP::createAnimationPage( const int iPage, const int iType, const QRectF & pageRect )
{
	QPainter painter;
	painter.begin( &m_pageAnimator.vPages[iPage] );
	setRenderOptions( &painter, true );

	if( m_pPanelLayout && iType == CITY_CHANGED )	// city changed animation for the panel
	{
		paintPanelInformation( &painter );
	}
	else
	{
		painter.translate( -pageRect.left(), -pageRect.top() );
		paintCurrentPage( &painter, false );
	}
	painter.end();
}

void
YaWP::initWeatherIconAnimation( const QRectF & rect, const int iDay )
{
	const bool bDayTime = m_stateMachine.iconState(iDay);
	m_stateMachine.setIconState( iDay, !bDayTime );

	if( m_configData.iconAnimation == PageAnimator::Jump )
	{
		m_iAnimationIndex    = iDay;
		m_tAnimationType     = IconAnimation;
		animationFinished();
	}
	else
	{
		/*  do not set the AnimationType before painting on the PageAnimator
		 *  otherwise the function guess we are already in the animation sequence and
		 *  paints the current picture from the pageanimatior to the paint device...
		 */
		QRectF rectIcon(0.0f, 0.0f, rect.width(), rect.height());
		m_pageAnimator.resetPages((int)rectIcon.width(), (int)rectIcon.height());

		QPainter	painter;
		const YawpWeather * pWeather = NULL;

		painter.begin( &m_pageAnimator.vPages[0] );
		setRenderOptions( &painter, true );
		pWeather = m_stateMachine.weather(iDay, bDayTime);
		paintWeatherIcon( &painter, rectIcon, iDay, pWeather );
		painter.end();

		painter.begin( &m_pageAnimator.vPages[1] );
		setRenderOptions( &painter, true );
		pWeather = m_stateMachine.weather(iDay, !bDayTime);
		paintWeatherIcon( &painter, rectIcon, iDay, pWeather );
		painter.end();

		m_iAnimationIndex    = iDay;
		m_tAnimationType     = IconAnimation;
		m_pageAnimator.setTransition( m_configData.iconAnimation );
		m_timeline.start();
	}
}


void
YaWP::paintPanelInformation( QPainter * painter )
{
	dStartFunct();
	QFont	font = KGlobalSettings::generalFont();

	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity || pCity->days().count() == 0 )
	{
		dEndFunct();
		return;
	}
	font.setBold(false);

	if( pCity->days().count() >= 1 )
	{
		const YawpWeather * pWeather = m_stateMachine.weather( 0, true );
		if( m_pPanelLayout->useCompactLayout() ||
			m_configData.todaysWeatherPanelFormat.testFlag( Yawp::PanelTemperature ) )
		{
			QRectF rectTemp = m_pPanelLayout->getTodaysTempRect();
			const Qt::Alignment alignment =
				(m_pPanelLayout->form() == Plasma::Horizontal && !m_pPanelLayout->useCompactLayout() ?
				Qt::AlignVCenter|Qt::AlignRight : Qt::AlignHCenter|Qt::AlignVCenter);

			if( m_pPanelLayout->useCompactLayout() )
			{
				if( m_pPanelLayout->form() == Plasma::Horizontal )
					font.setPixelSize( (int)qRound(m_pPanelLayout->contentsRect().height() * 0.36f) );
				else
					font.setPixelSize( (int)qRound(m_pPanelLayout->contentsRect().width() * 0.22f) );
			}
			else
				font.setPixelSize( (int)qRound(rectTemp.height() * 0.90f) );
			painter->setFont( font );

			if( pWeather->currentTemperature() < SHRT_MAX )
				drawText( painter, rectTemp, alignment,
						  QString::number(pWeather->currentTemperature()) + QChar(0xB0) );
			else
				drawText( painter, rectTemp, alignment, QLatin1String("?") );
//			painter->drawRect(rectTemp);
		}
		if( m_pPanelLayout->useCompactLayout() ||
		    m_configData.todaysWeatherPanelFormat.testFlag( Yawp::PanelIcon ) )
		{
			QRectF rectIcon = m_pPanelLayout->getTodaysIconRect();
			rectIcon.adjust( 2.0f, 2.0f, -2.0f, -2.0f );

			if( pWeather->currentIconName().isEmpty() )
				drawImage( painter, rectIcon, pWeather->iconName() );
			else
				drawImage( painter, rectIcon, pWeather->currentIconName() );
//			painter->drawRect(rectIcon);
		}
	}

	float dSize( m_pPanelLayout->form() == Plasma::Horizontal ?
		m_pPanelLayout->contentsRect().height() : m_pPanelLayout->contentsRect().width() );
	if( m_pPanelLayout->form() == Plasma::Horizontal )
		font.setPixelSize( (int)qRound(dSize  * (m_pPanelLayout->useCompactLayout() ? 0.25f : 0.40f)) );
	else
		font.setPixelSize( (int)qRound(dSize * (m_pPanelLayout->useCompactLayout() ? 0.20f : 0.40f)) );
	painter->setFont( font );

	int iMaxDays = qMin( m_configData.iPanelForecastDays, pCity->days().count() );
	for( int i = 0; i < iMaxDays; ++i )
	{
		const YawpWeather * pWeather = m_stateMachine.weather(i);
		QRectF rect = m_pPanelLayout->getForecastTempRect( i );
		rect.adjust( 2.0f, 2.0f, -2.0f, -2.0f );

		if( m_pPanelLayout->useCompactLayout() && m_pPanelLayout->form() == Plasma::Horizontal )
		{
			QString sTemp;
			sTemp += (pWeather->highTemperature() < SHRT_MAX ?
			          QString::number(pWeather->highTemperature()) + QChar(0xB0) : "?");
			sTemp += "/";
			sTemp += (pWeather->lowTemperature() < SHRT_MAX ?
				      QString::number(pWeather->lowTemperature()) + QChar(0xB0) : "?");
			drawText( painter, rect, Qt::AlignHCenter|Qt::AlignVCenter, sTemp );
//			painter->drawRect(rect);
		}
		else if( pWeather && m_configData.forecastWeatherPanelFormat.testFlag( Yawp::PanelTemperature ) )
		{
			paintTemperatures( painter, rect, *pWeather, m_pPanelLayout->form() );
//			painter->drawRect(rect);
		}
		if( m_pPanelLayout->useCompactLayout() ||
			m_configData.forecastWeatherPanelFormat.testFlag( Yawp::PanelIcon ) )
		{
			QRectF rect = m_pPanelLayout->getForecastIconRect( i );
			rect.adjust( 2.0f, 2.0f, -2.0f, -2.0f );
			paintWeatherIcon( painter, rect, i, pWeather );
//			painter->drawRect(rect);
		}
	}
	
/*	painter->setPen( QPen(QColor(Qt::darkGray)) );
	QRectF rect( contentsRect() );
	rect.adjusted(0.0, 0.0, -1.0, -1.0);
	painter->drawRect( rect );
*/
	dEndFunct();
}


/*** DRAW THE BASE OF A PAGE, JUST CONTAINING THE THREE BUTTONS AND THE NAME OF THE CURRENT CITY
*/
void
YaWP::paintSkeletonPage( QPainter * painter )
{
	dStartFunct();
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity )
	{
		dEndFunct();
		return;
	}
	const qreal	dOpacity = painter->opacity();
	Yawp::PageType currPage = m_stateMachine.currentPage();
	const Yawp::PageType vPages[3] = {Yawp::PreviewPage, Yawp::DetailsPage, Yawp::SatellitePage};
	bool vAvailables[3] = {false, false, false};
	int iPageCounter(0);

	//--- get the state off all pages for the current city ---
	for( int i = 0; i < 3; ++i )
	{
		if( m_stateMachine.hasPage(vPages[i]) )
		{
			vAvailables[i] = true;
			iPageCounter += 1;
		}
	}

	/* draw all available buttons, when there are at least two of them,
	 * otherwise the user has no choice, which page he/she wants to see.
	 */
	if( iPageCounter > 1 )
	{
		for( int i = 0; i < 3; ++i )
		{
			if( vAvailables[i] )
			{
				painter->setOpacity( currPage == vPages[i] ? 1.0f : 0.5f );
				drawImage(painter, getButtonRect(i), m_vButtonNames.at(i));
			}
		}
	}
	painter->setOpacity( dOpacity );

	/*** DRAW CITY AND LOCATION NAME ***
	*/
	QFont font = painter->font();
	font.setBold(false);
	font.setPixelSize(15.0f * m_desktopLayout.getScalingFactor());
	QFontMetrics		  fontMetrics(font);
	painter->setFont(font);

	QRectF rectArrowPrev = getCityArrowRect(false);
	QRectF rectArrowNext = getCityArrowRect(true);
	QRectF rectCity( qRound(rectArrowPrev.right() + 2.0f*m_desktopLayout.getScalingFactor()),
	                 qRound(rectArrowPrev.top()   - 2.0f*m_desktopLayout.getScalingFactor()),
	                 qRound(rectArrowNext.left() - rectArrowPrev.right()
	                    - 4.0f*m_desktopLayout.getScalingFactor()),
	                 qRound(fontMetrics.height()) );

	if( m_bBusy )
		drawText(painter, rectCity, Qt::AlignHCenter | Qt::AlignVCenter, tr2i18n("Connecting..."));
	else
		drawText(painter, rectCity, Qt::AlignHCenter | Qt::AlignVCenter, m_sCityVisualName);

	if( m_pWeatherModel->rowCount() > 1 )
	{
		painter->setOpacity( m_configData.iCityIndex == 0 ? 0.5 : 1.0 );
		drawImage( painter, rectArrowPrev, QLatin1String("arrow-left") );
		painter->setOpacity( m_configData.iCityIndex+1 == m_pWeatherModel->rowCount() ? 0.5 : 1.0 );
		drawImage( painter, rectArrowNext, QLatin1String("arrow-right") );
	}
	painter->setOpacity( dOpacity );
	dEndFunct();
}

void
YaWP::paintTopWeatherInfos( QPainter * painter, int iDayIndex, bool bShowFancyTemp )
{
	dStartFunct();
	QFont		font = painter->font();
	QRectF		rect;
	QFontMetrics	fontMetrics(font);
	const YawpWeather * pWeather = m_stateMachine.weather( iDayIndex );

	if( pWeather )
	{
		font.setBold(false);
		
		/*** PAINT WEATHERICON ***
		*/
		paintWeatherIcon( painter, m_desktopLayout.getTodaysIconRect(), iDayIndex, pWeather, true );

		/*** DRAW CURRENT TEMPERATURE ***
		*/
		if( pWeather->currentTemperature() < SHRT_MAX )
		{
			font.setPixelSize(40.0f * m_desktopLayout.getScalingFactor());
			font.setBold(false);
			fontMetrics = QFontMetrics(font);
			painter->setFont(font);
			drawText( painter,
			          m_desktopLayout.getCurrentTempRect(),
			          Qt::AlignCenter,
			          QString::number( pWeather->currentTemperature() ) + QChar(0xB0) );
		}

		/*** DRAW TEMPERATURE (HIGH/LOW) OF THE SELECTED DAY ***
		*/
		if( bShowFancyTemp )
		{
			QRectF rect = m_desktopLayout.contentsRect();
			rect.setRect( qRound(rect.left() + (5.0 * m_desktopLayout.getScalingFactor())),
			              qRound(rect.top() + ( 1.0 * m_desktopLayout.getScalingFactor())),
			              qRound(70.0 * m_desktopLayout.getScalingFactor()),
			              qRound(95.0 * m_desktopLayout.getScalingFactor()) );
			paintTooltipIconTemp( painter, rect, m_stateMachine.day( iDayIndex ) );
		}
		else
		{
			font.setBold(true);
			font.setPixelSize(18.0f * m_desktopLayout.getScalingFactor());
			painter->setFont(font);
			paintTemperatures( painter, m_desktopLayout.getTodaysTempRect(), *pWeather );
		}

		/*** DRAW WIND CONDITIONS ***
		*/
		font.setBold(false);
		font.setPixelSize(13.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);
		fontMetrics = QFontMetrics(font);

		QString sText;
		if( pWeather->windSpeed() < SHRT_MAX )
		{
			sText.append( QString("%1 %2 ")
				.arg( pWeather->windSpeed() )
				.arg( Utils::GetUnitString( m_configData.speedSystem ) ) );
			sText.append( i18nc("Wind direction", pWeather->windDirection().toUtf8().constData()) );
		}
		if( pWeather->humidity() < SHRT_MAX && pWeather->humidity() > 0 )
			sText.append( QString("\n%1%").arg(pWeather->humidity()) );

		/*** DRAW PRESSURE CONDITIONS ***
		*/
		if( pWeather->pressure() < SHRT_MAX )
		{
			if( !pWeather->pressureTendency().isEmpty() )
			{
				// Pressure tendency arrow
				QString sPressureSymbol;
				QChar sPressureShort = pWeather->pressureTendency()[0].toLower();
				if (sPressureShort == QChar('s'))
					sPressureSymbol = QChar(0x279C); // steady
				if (sPressureShort == QChar('r') || sPressureShort == QChar('i'))
					sPressureSymbol = QChar(0x279A); // rising
				if (sPressureShort == QChar('f') || sPressureShort == QChar('d') )
					sPressureSymbol = QChar(0x2798); // falling
				sText.append( QString("\n%1 %2 %3")
					.arg( pWeather->pressure() )
					.arg( Utils::GetUnitString( m_configData.pressureSystem ) )
					.arg( sPressureSymbol ) );
			}
			else
			{
				sText.append( QString("\n%1 %2")
					.arg( pWeather->pressure() )
					.arg( Utils::GetUnitString(m_configData.pressureSystem) ));
			}
		}
		if( !sText.isEmpty() )
		{
			rect = m_desktopLayout.getCurrentTempRect();
			rect.setTop( qRound(rect.bottom() - 2.0f * m_desktopLayout.getScalingFactor()) );
			rect.setHeight( qRound(3.0f * fontMetrics.height()) );
			drawText(painter, rect, Qt::AlignHCenter | Qt::AlignTop, sText );
		}
	}
	dEndFunct();
}

void
YaWP::paintSatellitePage( QPainter * painter )
{
	dStartFunct();
	if( m_stateMachine.hasPage(Yawp::SatellitePage) )
	{
		const CityWeather * pCity = m_stateMachine.currentCity();
		painter->drawImage( m_desktopLayout.getSateliteRect(), pCity->satelliteImage(),
		                     QRect(QPoint(0, 0), pCity->satelliteImage().size()) );
	}
	dEndFunct();
}

void
YaWP::animationTimeout(int frame)
{
	Q_UNUSED( frame );

	dStartFunct();
	update();
	dEndFunct();
}

void
YaWP::animationFinished()
{
	dStartFunct();
	m_pageAnimator.vPages[0] = m_pageAnimator.vPages[1] = QPixmap();
	m_tAnimationType = NoAnimation;
	m_iAnimationIndex = -1;
	dEndFunct();
}

void
YaWP::paintCurrentPage( QPainter * painter, bool bAnimation )
{
	//--- draw the page-buttons on the left top edge and the city name ---
	paintSkeletonPage( painter );
	
	const Yawp::PageType currPage = m_stateMachine.currentPage();
	if( currPage == Yawp::SatellitePage )
			paintSatellitePage( painter );
	else
	{
		paintTopWeatherInfos( painter, m_stateMachine.detailsDayIndex() );

		if( m_tAnimationType == DetailsChange && bAnimation )
		{
			m_pageAnimator.paint( painter,
				m_desktopLayout.getUserDefinedDetailsRect(),
				m_timeline.currentFrame() );
		}
		else if( currPage == Yawp::PreviewPage )
			paintWeatherPreview( painter );
		else
			paintWeatherDetails( painter );
	}
}

/***   paint the temperature H: ?? / L: ??
*/
void
YaWP::paintTemperatures( QPainter                  * painter,
                         const QRectF              & rect,
                         const YawpWeather         & weather,
                         const Plasma::FormFactor    form )
{
	dStartFunct();

	Qt::AlignmentFlag hAlignment = Qt::AlignHCenter;
	QString sTemplateHigh;
	QString sTemplateLow;
	if( form == Plasma::Horizontal || form == Plasma::Vertical )
	{
		hAlignment = ( (form == Plasma::Horizontal) ? Qt::AlignRight : Qt::AlignHCenter);
		if( weather.highTemperature() < SHRT_MAX )
			sTemplateHigh = QString::number(weather.highTemperature());
		if( weather.lowTemperature() < SHRT_MAX )
			sTemplateLow  = QString::number(weather.lowTemperature());
	}
	else
	{
		if( weather.highTemperature() < SHRT_MAX )
			sTemplateHigh = i18nc("High degrees", "H: %1", weather.highTemperature());
		if( weather.lowTemperature() < SHRT_MAX )
			sTemplateLow  = i18nc("Low degrees",  "L: %1", weather.lowTemperature());
	}
	
	QRectF tempRect(rect.x(), rect.y(), rect.width(), qRound(rect.height() / 2.0));
	if( weather.highTemperature() < SHRT_MAX )
	{
		drawText( painter, tempRect, hAlignment | Qt::AlignVCenter, sTemplateHigh + QChar(0xB0) );
	}
	if( weather.lowTemperature() < SHRT_MAX )
	{
		tempRect.moveTop( tempRect.top() + tempRect.height() );
		drawGreyText( painter, tempRect, hAlignment | Qt::AlignVCenter, sTemplateLow + QChar(0xB0) );
	}
	dEndFunct();
}

void
YaWP::paintWeatherIcon( QPainter          * painter,
                        const QRectF      & rect,
                        int                 iDayIndex,
                        const YawpWeather * pWeather,
                        bool                bToolTip )
{
	dStartFunct();
	if( m_tAnimationType == IconAnimation && iDayIndex == m_iAnimationIndex )
	{
		m_pageAnimator.paint( painter, rect, m_timeline.currentFrame() );
	}
	else if( pWeather )
	{
		//--- GET THE ICON NAME ---
		QString sIcon;
		if (! m_pPanelLayout || bToolTip ) sIcon = pWeather->currentIconName(); // Always show forecast on panel
		if( sIcon.isEmpty() || sIcon.compare("unknown") == 0 )
			sIcon = pWeather->iconName();

		//--- PAINT THE ICON ---
		painter->save();
		painter->setOpacity( pWeather->dayTime() ? 1.0 : 0.5 );
		drawImage(painter, rect, sIcon );
		painter->restore();
	}
	dEndFunct();
}

void
YaWP::paintTooltipIconTemp( QPainter * painter, const QRectF & iconRect, const YawpDay * pDay )
{
	dStartFunct();
	QFont font = painter->font();
	font.setPixelSize(17.0 * m_desktopLayout.getScalingFactor());
	QFontMetrics fm( font );
	painter->setFont( font );

	const float dSpace = 5.0f*m_desktopLayout.getScalingFactor();
	QRectF rect = iconRect.adjusted( dSpace, dSpace, -dSpace, -dSpace );
	short iHighTemp(SHRT_MAX), iLowTemp(SHRT_MAX);

	if( pDay->hasNightValues() )
	{
		const qreal dIconSize = 46.0 * m_desktopLayout.getScalingFactor();
		const qreal dOpacity = painter->opacity();
		const qreal dMid = rect.top() + rect.height() * 0.5;
		const qreal dOffset = 0.25*dIconSize;
		const YawpWeather * pWeather = NULL;

		//--- draw night icon ---
		pWeather = &pDay->nightWeather();
		iHighTemp = pWeather->highTemperature();
		iLowTemp = pWeather->lowTemperature();
		painter->setOpacity( 0.5 );
		drawImage( painter,
		           QRect(rect.right() - dIconSize, dMid - dOffset, dIconSize, dIconSize),
		           pWeather->iconName() );
		painter->setOpacity( dOpacity );

		//--- draw day icon ---
		pWeather = &pDay->weather();
		if( pWeather->highTemperature() < SHRT_MAX )
			iHighTemp = (iHighTemp == SHRT_MAX || pWeather->highTemperature() > iHighTemp ? pWeather->highTemperature() : iHighTemp);
		iLowTemp = qMin(iLowTemp, pWeather->lowTemperature());
		drawImage( painter,
		           QRect(rect.left(), dMid + dOffset-dIconSize, dIconSize, dIconSize),
		           pDay->weather().iconName() );
	}
	else
	{
		const qreal dIconSize = 50.0 * m_desktopLayout.getScalingFactor();
		const YawpWeather * pWeather = &pDay->weather();
		iHighTemp = pWeather->highTemperature();
		iLowTemp = pWeather->lowTemperature();
		drawImage( painter,
		           QRect(rect.left() + (rect.width()-dIconSize) / 2.0,
		                 rect.top() + (rect.height() - dIconSize) / 2.0,
		                 dIconSize,
		                 dIconSize),
		           pWeather->iconName() );
	}

	//--- draw the high/low temperature ---
	if( iHighTemp < SHRT_MAX )
		drawText( painter, QRect(rect.left(), rect.top(), rect.width(), fm.height()),
			Qt::AlignRight|Qt::AlignVCenter, QString::number(iHighTemp) + QChar(0xB0) );
	if( iLowTemp < SHRT_MAX )
		drawGreyText( painter, QRect(rect.left(), rect.bottom()-fm.height(), rect.width(), fm.height()),
			Qt::AlignLeft|Qt::AlignVCenter, QString::number(iLowTemp) + QChar(0xB0) );
	dEndFunct();
}

/*** PAINT THE WEATHER PREVIEW FOR THE NEXT FEW DAYS IN THE BOTTOM OF THE APPLET ***
 */
void
YaWP::paintWeatherPreview( QPainter * painter )
{
	dStartFunct();
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity || pCity->days().count() == 0 )
	{
		dEndFunct();
		return;
	}
	const int	iMaxDays = qMin(5, pCity->days().count());
	QFont		font( painter->font() );
	font.setBold(false);
	font.setPixelSize(15.0f * m_desktopLayout.getScalingFactor());
	painter->setFont(font);

	/*  When we have only one forecast day, than we show this forecast day using the entire details rectangle.
	 */
	if( pCity->days().count() == 2 )
	{
		/* This might be a little bit nasty.
		 * We adjust the top of the content rect to paint the top in the details rect.
		 * After that we have to restore the original value.
		 */
		const QRectF rectOrgDesktopLayout( m_desktopLayout.contentsRect() );
		QRectF rect( rectOrgDesktopLayout );

		float dX( m_desktopLayout.getUserDefinedDetailsRect().topLeft().x() );
		float dY( m_desktopLayout.getUserDefinedDetailsRect().topLeft().y() );
		dY += m_desktopLayout.getForecastDayNamesRect().height();
		rect.moveTo( dX - 2.0f*m_desktopLayout.getScalingFactor(), dY );
		m_desktopLayout.setContentsRect( rect );
		paintTopWeatherInfos( painter, 1, true );

		m_desktopLayout.setContentsRect( rectOrgDesktopLayout );
	}
	else if( pCity->days().count() > 2 )
	{
		m_desktopLayout.setForecastDays( iMaxDays-1 );
		QRectF rect;
		for( int i = 1; i < iMaxDays; ++i )
		{
			if( m_pPanelLayout )
			{
				const YawpDay * pDay = pCity->days().at(i);

				rect = m_desktopLayout.getForecastTempRect( i );
				rect.setTop( m_desktopLayout.getUserDefinedDetailsRect().top()
					+ 28.0 * m_desktopLayout.getScalingFactor() );
				paintTooltipIconTemp( painter, rect, pDay );
			}
			else
			{
				const YawpWeather * pWeather = m_stateMachine.weather(i);
				if( pWeather )
				{
					rect = m_desktopLayout.getForecastIconRect( i );
					paintWeatherIcon(painter, rect, i, pWeather);

					rect = m_desktopLayout.getForecastTempRect( i );
					paintTemperatures(painter, rect, *pWeather);
				}
			}
		}
	}

	if( m_pPanelLayout )	// we are painting on the tooltip
		paintForecastDayNames( painter, true );
	else
		paintForecastDayNames( painter, m_bSlidingDayNames );
	dEndFunct();
}

void
YaWP::paintWeatherDetails( QPainter * painter )
{
	dStartFunct();
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity || pCity->days().count() == 0 )
	{
		dEndFunct();
		return;
	}
	
	QRectF		rect = m_desktopLayout.getForecastDayNamesRect();
	QFont		font(painter->font());
	const int   iDetailsDayIndex = m_stateMachine.detailsDayIndex();
	
	font.setBold(true);
	font.setPixelSize(17.0f * m_desktopLayout.getScalingFactor());
	painter->setFont(font);
	drawText( painter, rect, Qt::AlignHCenter | Qt::AlignVCenter, tr2i18n("Forecast details") );

	//--- when we are going to paint this as panel-tooltip (e.g.: for noaa) than we do not want to see the arrows ---
	if( !m_pPanelLayout )
	{
		painter->setOpacity( iDetailsDayIndex == 0 ? 0.5f : 1.0f );
		drawImage( painter, m_desktopLayout.getDetailsArrowRect( false ), QLatin1String("arrow-left") );
		painter->setOpacity( iDetailsDayIndex+1 >= pCity->days().count() ? 0.5f : 1.0f );
		drawImage( painter, m_desktopLayout.getDetailsArrowRect( true ), QLatin1String("arrow-right") );
	}
	painter->setOpacity( 1.0f );

	const YawpDay * pDay = m_stateMachine.day( iDetailsDayIndex );
	const YawpWeather * pWeather = m_stateMachine.weather( iDetailsDayIndex );

	if( !pDay || !pWeather )
	{
		dEndFunct();
		return;
	}

	/*   determine the position of the details text, and draw a dark-opaque rectangle
	 *   to see the text better on light backrounds
	 */
	int iTmp = rect.height();
	rect.setHeight( m_desktopLayout.getDetailsRect().height()-iTmp );
	rect.moveTo( rect.left(), rect.top()+iTmp );
	painter->setBrush( QColor(Qt::black) );
	painter->setPen( Qt::NoPen );
	painter->setOpacity( 0.4 );
	painter->drawRect(rect);


	const int iMaxPages( m_stateMachine.maxPropertyPage() );
	const int iPage( m_stateMachine.currentPropertyPage() );

	painter->setBrush(QColor(Qt::white));
	painter->setOpacity(1.0f);
	font.setBold(false);

	int iTextOffset = (int)qRound(5.0f * m_desktopLayout.getScalingFactor());
	rect.adjust(iTextOffset, iTextOffset, -iTextOffset, -iTextOffset);
	
	if( iPage+1 == iMaxPages )	// we are paintng the credit page
	{
		/***   D R A W   T H E   C R E D I T   ***
		 */
		QRectF creditRect( rect );
		creditRect.setHeight( (qreal)creditRect.height() - 30.0f*m_desktopLayout.getScalingFactor() );
		font.setPixelSize(15.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);

		QString sCredit( pCity->credit() );
		QFontMetrics fm(font);
		if( !pCity->creditURL().isEmpty() && fm.width(pCity->creditURL()) < creditRect.width() )
			sCredit += "\n" + pCity->creditURL();

		drawText( painter, creditRect, Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextWordWrap, sCredit );

		/****   D R A W   T H E   U P D A T E   T I M E    ***
		 */
		font.setPixelSize(13.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);
		
		QString sUpdateString;
		if( pCity->lastUpdate().isValid() )
		{
			sUpdateString.append( i18n("Last update:") + " " +
				KGlobal::locale()->formatDateTime(pCity->lastUpdate(), KLocale::FancyShortDate, false));
		}
		else
			sUpdateString.append(i18n("*** Not updated yet ***"));
		
		if( pCity->observationPeriode().isValid() )
		{
			QDateTime obsTime( pCity->observationPeriode() );
			if( pCity->timeZone().isValid() )
				obsTime = pCity->timeZone().convert(KSystemTimeZones::local(), obsTime);
			
			sUpdateString.append( QString("\n%1 %2")
				.arg(i18n("Observation Periode:"))
				.arg(KGlobal::locale()->formatDateTime(obsTime, KLocale::FancyShortDate, false)) );
		}
		drawGreyText(painter, rect, Qt::AlignBottom|Qt::AlignHCenter, sUpdateString);
	}
	else
	{
		QString sSummary;
		
		if(pWeather->dayTime())
			sSummary = i18n(pDay->date().toString("dddd").toUtf8().constData()) + "\n\n";
		else
			sSummary = i18n(pDay->date().toString("dddd").toUtf8().constData())
							+ QString(" ") + i18n("night") + "\n\n";

		int iLine (iPage * 3);
		int iMaxLine(iLine+3 <= pWeather->propertyTextLines().count() ? iLine+3 : pWeather->propertyTextLines().count());

		for( ; iLine <iMaxLine; ++iLine )
			sSummary += pWeather->propertyTextLines().at(iLine) + "\n";
		
		/****   D R A W   T H E   D E T A I L S   P R O P E R T I E S   ***
		 */
		font.setPixelSize(15.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);
		drawText( painter, rect, Qt::AlignLeft | Qt::AlignTop, sSummary );

		/***   D R A W   P A G E   I N D E X   ***
		 */
		if( !m_pPanelLayout )
			drawGreyText( painter, rect, Qt::AlignRight|Qt::AlignTop, QString("%1/%2").arg(iPage+1).arg(iMaxPages) );
	}
	dEndFunct();
}

void
YaWP::paintForecastDayNames( QPainter * painter, const bool bDayNames )
{
	dStartFunct();
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity || pCity->days().count() <= 1 )
	{
		dEndFunct();
		return;
	}

	QRectF	rectDay = m_desktopLayout.getForecastDayNamesRect();

	if( m_tAnimationType == SlidingDayNames )
	{
		m_pageAnimator.paint( painter, rectDay, m_timeline.currentFrame() );
	}
	else if( pCity->days().count() == 2 )
	{
		QFont font( painter->font() );
		font.setBold(true);
		font.setPixelSize(17.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);

		const YawpDay * pDay = pCity->days().at(1);
		QString str( i18n("Forecast") + " " );

		if( bDayNames )
			str += i18n(pDay->date().toString("dddd").toUtf8().constData());
		else
			str += KGlobal::locale()->formatDate(pDay->date(), KLocale::ShortDate);
		drawText( painter, rectDay, Qt::AlignHCenter | Qt::AlignVCenter, str );
	}
	else
	{
		const int	iMaxDays = qMin(5, pCity->days().count());
		m_desktopLayout.setForecastDays( iMaxDays-1 );

		const YawpDay *	pDay = NULL;
		const float	dColWidth = (m_desktopLayout.getUserDefinedDetailsRect().width())
			/ (float)m_desktopLayout.forecastDays();
		const QString	sFormat = (bDayNames ? QLatin1String("ddd") : QLatin1String("M/d"));
		rectDay.setWidth( dColWidth );

		QFont font( painter->font() );
		font.setBold(true);
		font.setPixelSize(17.0f * m_desktopLayout.getScalingFactor());
		painter->setFont(font);

		for( int iDay= 1; iDay < iMaxDays; ++iDay )
		{
			pDay = pCity->days().at(iDay);
			rectDay.moveLeft( m_desktopLayout.getUserDefinedDetailsRect().left()
				+ ((qreal)(iDay-1))*dColWidth );
			drawText( painter,
			          rectDay,
			          Qt::AlignHCenter | Qt::AlignVCenter,
			          i18n(pDay->date().toString(sFormat).toUtf8().constData()) );
		}
	}
	dEndFunct();
}

void
YaWP::setCityIndex( int iIndex )
{
	dStartFunct();
	m_stateMachine.setCurrentCityIndex( iIndex );
	m_configData.iCityIndex = m_stateMachine.currentCityIndex();
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( pCity )
	{
		m_desktopLayout.setForecastDays( pCity->days().count()-1 );
		m_sCityVisualName = createVisualCityName( pCity );

		//--- select the right menuitem in the city-submenu ---
		QList<QAction *> list = m_pCitySubMenu->menu()->actions();
		int iCityIndex = m_stateMachine.currentCityIndex();
		if( list.count() > iCityIndex )
			list.at(iCityIndex )->setChecked( true );
	}
	else
		m_sCityVisualName.clear();
	dEndFunct();
}

QString
YaWP::createVisualCityName( const CityWeather * pCity ) const
{
	if( !pCity )
		return QString();

	//--- determine the cityname for visualisation, just make sure the name fits on the applet ---
	QFont font = KGlobalSettings::generalFont();
	font.setPixelSize(15.0f);
	QFontMetrics fm( font );
	QString sCached;
	int iCacheWidth( 0 );
	QString sVisualName;

	/* we check if the update intervall is still valid, i added 1 minute to give the
	 * update job some time to update. Because the job will be normaly started when the update time
	 * has been past.
	 */
	if( pCity->days().count() > 0 &&
		( !pCity->lastUpdate().isValid() ||
		  (m_configData.iUpdateInterval > 0 &&
	       pCity->lastUpdate().addMSecs((m_configData.iUpdateInterval+1)*60*1000) < QDateTime::currentDateTime()) ) )
	{
		sCached = " (" + i18n("Cached") + ")";
		iCacheWidth = fm.width(sCached);
	}
	sVisualName = fm.elidedText( pCity->localizedCityString(), Qt::ElideRight, 230 - iCacheWidth );
	sVisualName.append( sCached );
	return sVisualName;
}

void
YaWP::initPanelLayoutObject( const Plasma::FormFactor form )
{
	if( m_pPanelLayout && m_pPanelLayout->form() != form )
	{
		delete m_pPanelLayout;
		m_pPanelLayout = NULL;
		m_pCurrLayout = &m_desktopLayout;
	}
	if( !m_pPanelLayout )
	{
		if( form == Plasma::Horizontal )
			m_pPanelLayout = new HorizontalPanelLayout();
		else if( form == Plasma::Vertical )
			m_pPanelLayout = new VerticalPanelLayout();
	}
	if( m_pPanelLayout )
	{
		m_pPanelLayout->setForecastFormat( m_configData.forecastWeatherPanelFormat );
		m_pPanelLayout->setTodaysFormat( m_configData.todaysWeatherPanelFormat );
		m_pPanelLayout->setForecastDays( m_configData.iPanelForecastDays );
		m_pPanelLayout->setUseCompactLayout( m_configData.bUseCompactPanelLayout );
		m_pCurrLayout = m_pPanelLayout;
	}
}

void
YaWP::drawBackground(QPainter * painter, QRectF rect)
{
	if( m_configData.bUseCustomTheme && m_configData.bUseCustomThemeBackground )
	{
		m_customSvg.paint(painter, rect, QLatin1String("back-default"));
	}
	else
	{
		QString sTheme( QString("back-%1").arg(m_configData.sBackgroundName) );
//		dDebug() << "sTheme: " << sTheme << " | contentsRect: " << rect.height() << "x" << rect.width();
		m_svg.paint(painter, rect, sTheme);
	}
}

void
YaWP::createPanelTooltip()
{
	dStartFunct();
	Plasma::ToolTipManager::self()->clearContent(this);

	//--- we have to switch the layout, otherwise the shadow does not fit the desktop text ---
	m_pCurrLayout = &m_desktopLayout;
	const CityWeather * pCity = m_stateMachine.currentCity();
	if( !pCity || pCity->days().count() == 0 )
	{
		dEndFunct();
		return;
	}

	Plasma::ToolTipContent	toolTipData;
	toolTipData.setMainText( pCity->localizedCityString() );

	if( m_configData.bUseExtendedTooltip )
	{
		QString sSubtext;
		if( m_configData.extendedTooltipOptions.testFlag( Yawp::SatellitePage ) &&
		    !pCity->satelliteImage().isNull() )
			sSubtext += "<table><tr><td>";

		if( m_configData.extendedTooltipOptions.testFlag( Yawp::PreviewPage ) )
		{
			//--- get Size of the desktoplayout for width ---
			QSizeF prevSize = m_desktopLayout.getSize( QSizeF(218.0, 210.0) );

			int iSpacer = 0;
			if (m_configData.extendedTooltipOptions.testFlag( Yawp::ThemeBackground ))
				iSpacer = 3;

			//--- init desktoplayout object ---
			const QRectF rectOrgContentsRect( m_desktopLayout.contentsRect() );
			const QRectF rectOrgDetailsRect( m_desktopLayout.getUserDefinedDetailsRect() );

			m_desktopLayout.setContentsRect( QRectF(iSpacer, iSpacer, prevSize.width(), prevSize.height()) );
			QRectF rectDetails = m_desktopLayout.getDetailsRect();
			rectDetails.setRect( rectDetails.left(), 90.0 + iSpacer,
			                     rectDetails.width(), rectDetails.height() );
			m_desktopLayout.setUserDefinedDetailsRect( rectDetails );

			QPixmap pix( (int)prevSize.width()+2*iSpacer, (int)qRound(prevSize.height() * 0.93)+2*iSpacer );
			pix.fill( Qt::transparent );
			QPainter p;
			p.begin( &pix );
			setRenderOptions( &p, true );
			if (m_configData.extendedTooltipOptions.testFlag( Yawp::ThemeBackground ))
				drawBackground( &p, QRectF(0.0, 0.0, pix.width(), pix.height()) );

			/*  In case the user played a little bit with the icons and than we got the update event
			 *  and paint the tooltip...
			 *  Or had this applet in desktop mode once before and moved to another day or
			 *  switched to the detailspage and so on...
			 *  In this cases, we will see a wrong page, therefore we need to set the statemachine to
			 *  the default and reset it to its previous state, when we are done.
			 */
			bool bSlidingDayNames = m_bSlidingDayNames;
			m_bSlidingDayNames = true;
			QByteArray states( m_stateMachine.saveStates() );
			m_stateMachine.reset();

			/*  just to center the top a little bit
			 *  and get rid of the space where we normaly print the cityname
			 */
			p.translate(0.0, 3.0f*m_desktopLayout.getScalingFactor());
			paintTopWeatherInfos( &p, 0, true );
			p.translate(0.0, -3.0f*m_desktopLayout.getScalingFactor());
			if( m_stateMachine.hasPage( Yawp::PreviewPage ) )
				paintWeatherPreview( &p );
			else
				paintWeatherDetails( &p );
			p.end();
			toolTipData.setImage( pix );

			m_bSlidingDayNames = bSlidingDayNames;
			m_stateMachine.loadStates( states );

			m_desktopLayout.setContentsRect( rectOrgContentsRect );
			m_desktopLayout.setUserDefinedDetailsRect( rectOrgDetailsRect );
		}
		else	//--- show the plasmoid icon instead ---
		{
			KIcon appIcon( icon() );
			toolTipData.setImage( appIcon.pixmap(48,48) );
		}

		int iMax = qMin( 3, pCity->days().count() );
		for( int iDay = 0; iDay < iMax; iDay++ )
		{
			const YawpDay * pDay = pCity->days().at(iDay);
			// QDate returns translated nominative case of day
			// name which is grammatically wrong at least in
			// Russian. So fetch base English form and translate
			// into correct one with own catalog
			const KDateTime kDate(pDay->date());
			sSubtext += "<u>"
							 + i18n("Forecast for %1 (%2)",
											i18nc("Forecast for day", kDate.toString("%:A").toUtf8()),
											KGlobal::locale()->formatDate(pDay->date(), KLocale::ShortDate ))
			         + "</u><br />";
			sSubtext += i18n("Day: ") + Utils::LocalizedWeatherString(pDay->weather().description())
			         + "<br />";
			if( pDay->hasNightValues() )
				sSubtext += i18n("Night: ")
				         + Utils::LocalizedWeatherString(pDay->nightWeather().description())
				         + "<br />";

			// Sunset, sunrise and realfeal only for today, to limit tooltip size
			if( iDay == 0 )
			{
				// Show the sunrise/sunset when we have it
				//    (Google does not have include this informations)
				// Format time according to KDE Local setting
				if( !pDay->sunrise().isNull() && !pDay->sunset().isNull() )
				{
					sSubtext += i18n("Sunrise at %1, sunset at %2",
						KGlobal::locale()->formatTime(pDay->sunrise()),
						KGlobal::locale()->formatTime(pDay->sunset())) + "<br />";
				}

				//--- Show the realfeel temperature (when we have it) ---
				const YawpWeather & weather = pDay->weather();
				if( weather.temperatureRealFeelHigh() < SHRT_MAX &&
				    weather.temperatureRealFeelLow() < SHRT_MAX )
				{
					sSubtext += i18n("Real feel: %1 / %2",
						QString::number(weather.temperatureRealFeelHigh()) + QChar(0xB0),
						QString::number(weather.temperatureRealFeelLow()) + QChar(0xB0))
						+ "<br />";
				}
			}
			if( iDay+1 < iMax )
				sSubtext += "<br />";
			else	// removes the last <br />; because this is the end of text
				sSubtext.remove( sSubtext.length()-6, 6 );
		}

		if( m_configData.extendedTooltipOptions.testFlag( Yawp::SatellitePage ) &&
		    !pCity->satelliteImage().isNull() )
		{
			toolTipData.addResource(Plasma::ToolTipContent::ImageResource, QUrl("wicon://satelite"),
				QVariant(pCity->satelliteImage().scaledToWidth(218, Qt::SmoothTransformation)));
			sSubtext += "</td><td valign=\"top\">";
			sSubtext += "<img src=\"wicon://satelite\"/>";
			sSubtext += "</td></tr></table>";
		}
		toolTipData.setSubText(sSubtext);
	}
	else	//--- we show only the topinfo of today as simple tooltip ---
	{
		QPixmap pix( 218, 75 );
		m_desktopLayout.setContentsRect( QRectF(0.0, 0.0, 218.0, 203.6) );
		pix.fill( Qt::transparent );
		QPainter p;
		p.begin( &pix );
		setRenderOptions( &p, true );
		paintTopWeatherInfos( &p, 0, true );
		p.end();
		toolTipData.setImage( pix );
	}

	/*  switch back to the panellayout, this function only gets called in panelmode,
	 *  so we are sure that the original layout is panellayout.
 	 */
	m_pCurrLayout = m_pPanelLayout;
	toolTipData.setAutohide(false);
	Plasma::ToolTipManager::self()->setContent(this, toolTipData);
	dEndFunct();
}

void
YaWP::setDefaultFontColors()
{
	if( m_configData.bUseCustomFontColor )
		return;

	if( m_pPanelLayout ||
	    m_configData.sBackgroundName.compare( QLatin1String("default") ) == 0 ||
	    m_configData.sBackgroundName.compare( QLatin1String("naked") ) == 0 )
	{
		m_configData.fontColor = KColorScheme(QPalette::Active, KColorScheme::View,
			Plasma::Theme::defaultTheme()->colorScheme()).foreground().color();
		m_configData.lowFontColor = KColorScheme(QPalette::Active, KColorScheme::View,
			Plasma::Theme::defaultTheme()->colorScheme()).foreground(KColorScheme::InactiveText).color();

		if( m_configData.fontColor.red() < 25 &&
		    m_configData.fontColor.green() < 25 &&
		    m_configData.fontColor.blue() < 25 )
		{
			m_configData.lowFontColor = m_configData.fontColor.lighter();
			m_configData.shadowsFontColor = QColor(255,255,255,100);
		}
		else
		{
			m_configData.lowFontColor = m_configData.fontColor.darker(125);
			m_configData.shadowsFontColor = QColor(0,0,0,100);
		}
	}
	else
	{
		m_configData.fontColor = QColor(Qt::white);
		m_configData.lowFontColor = QColor(Qt::gray);
		m_configData.shadowsFontColor = QColor(0,0,0,100);
	}
}

void
YaWP::updateCitySubMenu()
{
	//--- first of all we delete all old actions ---
	m_pCitySubMenu->menu()->clear();

	const int iMaxCities = m_pWeatherModel->rowCount();
	for( int i = 0; i < iMaxCities; ++i )
	{
		const CityWeather * cityInfo = m_pWeatherModel->getCityInfo( i );
		QIcon flag = m_storage.countryMap()->getPixmapForCountryCode( cityInfo->countryCode() );
		QAction * action = new QAction( flag, cityInfo->localizedCityString(), this );
		action->setCheckable( true );
		action->setData( QVariant(i) );
		m_pGrpActionCities->addAction( action );
		m_pCitySubMenu->addAction( action );
		if( i == m_configData.iCityIndex )
			action->setChecked( true );
	}
	m_pCitySubMenu->setEnabled( iMaxCities > 0 );
}


/***********************************************************************************
*/

/***  if the selected Transition is one of the Rolling, Sliding or Open/Close Effekt,
*     than get the opponet of this transition depending on forward == true/false
*/
inline PageAnimator::Transition
YaWP::getSlidingTransition( const PageAnimator::Transition tr, const bool forward ) const
{
	if( tr <= 4 || forward )
		return tr;
	return (PageAnimator::Transition)(tr % 2 == 1 ? tr+1 : tr-1 );
}

inline void
YaWP::drawImage( QPainter * painter, const QRectF & rect, const QString & name )
{
	if( m_configData.bUseCustomTheme && m_customSvg.isValid() )
		m_customSvg.paint(painter, rect, name );
	else if ( m_svg.hasElement(name) )
		m_svg.paint(painter, rect, name );
	else
		m_svg.paint(painter, rect, name.mid(0, name.lastIndexOf('-')));
}

inline void
YaWP::drawText( QPainter * painter, const QRectF & rect, int align, const QString & text )
{
	if (! m_configData.bDisableTextShadows)
	{
		float dOffset = 1.0f; // * m_pCurrLayout->getScalingFactor();
		painter->setPen(m_configData.shadowsFontColor);
		painter->drawText(rect.translated(dOffset, dOffset), align, text);
	}
	painter->setPen(m_configData.fontColor);
	painter->drawText(rect, align, text);
}

inline void
YaWP::drawGreyText( QPainter * painter, const QRectF & rect, int align, const QString & text )
{
	if (! m_configData.bDisableTextShadows)
	{
		float dOffset = 1.0f; //m_pCurrLayout->getScalingFactor();
		painter->setPen(m_configData.shadowsFontColor);
		painter->drawText(rect.translated(dOffset, dOffset), align, text);
	}
	painter->setPen(m_configData.lowFontColor);
	painter->drawText(rect, align, text);
}

inline QRectF
YaWP::getButtonRect( int button ) const
{
	const float dScale = m_desktopLayout.getScalingFactor();
	return QRectF( qRound(m_desktopLayout.contentsRect().left()+(5.0f+(27.0f*button))*dScale),
	               qRound(m_desktopLayout.contentsRect().top()+5.0f*dScale),
	               qRound(25.0f*dScale),
	               qRound(25.0f*dScale) );
}

inline QRectF
YaWP::getCityArrowRect( const bool bForward ) const
{
	const qreal dScale = m_desktopLayout.getScalingFactor();
	const qreal dButtonSize = qRound(16.0f * dScale);
	const qreal dYPos = (m_stateMachine.currentPage() == Yawp::SatellitePage ? 35.0f : 100.0f);
	qreal dXPos;
	if( bForward )
		dXPos = m_desktopLayout.contentsRect().right()-(4.0f * dScale + dButtonSize);
	else
		dXPos = m_desktopLayout.contentsRect().left()+(4.0 * dScale);
	return QRectF( qRound(dXPos),
	               qRound(m_desktopLayout.contentsRect().top() + dYPos * dScale), dButtonSize, dButtonSize );
}

inline void
YaWP::setRenderOptions( QPainter * painter, const bool bUse )
{
	painter->setRenderHint( QPainter::SmoothPixmapTransform, bUse );
	painter->setRenderHint( QPainter::Antialiasing,          bUse );
	painter->setRenderHint( QPainter::TextAntialiasing,      bUse );
}

inline void
YaWP::setupAnimationTimeLine()
{
	m_timeline.stop();
	m_timeline.setDuration( m_configData.iAnimationDuration );
	m_timeline.setFrameRange( 0, m_configData.iAnimationDuration );
	m_pageAnimator.setDuration( m_configData.iAnimationDuration );
}

inline void
YaWP::setupWeatherServiceModel()
{
	m_pWeatherModel->setUpdateInterval( m_configData.iUpdateInterval );
	m_pWeatherModel->setDetailsPropertyList( m_configData.vDetailsPropertyRankingList );

	WeatherDataProcessor * pDataProcessor = m_pWeatherModel->dataProcessor();
	if( pDataProcessor )
	{
		pDataProcessor->setDistanceSystem( m_configData.distanceSystem );
		pDataProcessor->setPressureSystem( m_configData.pressureSystem );
		pDataProcessor->setTemperatureSystem( m_configData.temperatureSystem );
		pDataProcessor->setSpeedSystem( m_configData.speedSystem );
	}
}

inline void
YaWP::stopPendingEngineConnection()
{
	if( m_iIdPendingEngineConnection > 0 )
	{
		killTimer(m_iIdPendingEngineConnection);
		m_iIdPendingEngineConnection = -1;
	}
}

// This is the command that links your applet to the .desktop file
K_EXPORT_PLASMA_APPLET(yawp, YaWP);
