summaryrefslogtreecommitdiffstats
path: root/weather/src
diff options
context:
space:
mode:
authorAnselmo Lacerda S. de Melo <anselmo.melo@openbossa.org>2010-05-29 21:08:09 -0300
committerAnselmo Lacerda S. de Melo <anselmo.melo@openbossa.org>2010-05-30 11:04:33 -0300
commit75400545f25b394b9c6925ab80ab9fd48e2920e2 (patch)
tree192b41c59d8e37dff0d486087210a19f37142283 /weather/src
parent4d86ebb587dc4f822569ecab3d4d25a5b3ca3939 (diff)
weather: code moved to src/
Signed-off-by: Anselmo Lacerda S. de Melo <anselmo.melo@openbossa.org>
Diffstat (limited to 'weather/src')
-rw-r--r--weather/src/addcitytool.cpp401
-rw-r--r--weather/src/addcitytool.h153
-rw-r--r--weather/src/bootmanager.cpp69
-rw-r--r--weather/src/bootmanager.h31
-rw-r--r--weather/src/carroussel.h156
-rw-r--r--weather/src/citycarroussel.cpp472
-rw-r--r--weather/src/citycarroussel.h168
-rw-r--r--weather/src/cityinfodisplay.cpp323
-rw-r--r--weather/src/cityinfodisplay.h163
-rw-r--r--weather/src/citylist.cpp465
-rw-r--r--weather/src/citylist.h169
-rw-r--r--weather/src/citymanager.cpp146
-rw-r--r--weather/src/citymanager.h97
-rw-r--r--weather/src/contentlist.cpp441
-rw-r--r--weather/src/contentlist.h261
-rw-r--r--weather/src/demoforecastsource.cpp142
-rw-r--r--weather/src/demoforecastsource.h44
-rw-r--r--weather/src/fakecontentscreen.cpp104
-rw-r--r--weather/src/fakecontentscreen.h30
-rw-r--r--weather/src/forecast.h72
-rw-r--r--weather/src/forecastdata.cpp78
-rw-r--r--weather/src/forecastdata.h44
-rw-r--r--weather/src/forecasthungitem.cpp159
-rw-r--r--weather/src/forecasthungitem.h92
-rw-r--r--weather/src/forecastprovider.cpp54
-rw-r--r--weather/src/forecastprovider.h37
-rw-r--r--weather/src/forecastrain.cpp122
-rw-r--r--weather/src/forecastrain.h76
-rw-r--r--weather/src/forecastsnow.cpp200
-rw-r--r--weather/src/forecastsnow.h96
-rw-r--r--weather/src/forecastsource.h18
-rw-r--r--weather/src/forecaststars.cpp200
-rw-r--r--weather/src/forecaststars.h83
-rw-r--r--weather/src/forecastview.cpp643
-rw-r--r--weather/src/forecastview.h133
-rw-r--r--weather/src/gesturebox.cpp392
-rw-r--r--weather/src/gesturebox.h64
-rw-r--r--weather/src/gesturebox_p.h120
-rw-r--r--weather/src/loading.cpp77
-rw-r--r--weather/src/loading.h33
-rw-r--r--weather/src/main.cpp84
-rw-r--r--weather/src/mainview.cpp170
-rw-r--r--weather/src/mainview.h85
-rw-r--r--weather/src/networkforecastsource.cpp234
-rw-r--r--weather/src/networkforecastsource.h75
-rw-r--r--weather/src/painttextitem.cpp67
-rw-r--r--weather/src/painttextitem.h49
-rw-r--r--weather/src/pixmapbutton.cpp30
-rw-r--r--weather/src/pixmapbutton.h29
-rw-r--r--weather/src/pixmaploader.cpp125
-rw-r--r--weather/src/pixmaploader.h60
-rw-r--r--weather/src/scrollbar.cpp100
-rw-r--r--weather/src/scrollbar.h65
-rw-r--r--weather/src/settings.cpp122
-rw-r--r--weather/src/settings.h73
-rw-r--r--weather/src/symbiannetwork.h156
-rw-r--r--weather/src/titlebar.cpp75
-rw-r--r--weather/src/titlebar.h53
-rw-r--r--weather/src/xoapweatherresponse.cpp50
-rw-r--r--weather/src/xoapweatherresponse.h43
-rw-r--r--weather/src/yahooweatherresponse.cpp165
-rw-r--r--weather/src/yahooweatherresponse.h181
62 files changed, 8719 insertions, 0 deletions
diff --git a/weather/src/addcitytool.cpp b/weather/src/addcitytool.cpp
new file mode 100644
index 0000000..219d92a
--- /dev/null
+++ b/weather/src/addcitytool.cpp
@@ -0,0 +1,401 @@
+#include "addcitytool.h"
+#include "settings.h"
+#include "forecastprovider.h"
+#include "painttextitem.h"
+#include "pixmaploader.h"
+
+#include <QPainter>
+#include <QInputDialog>
+#include <QEvent>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QApplication>
+#include <QDebug>
+
+#define ADD_BACKGROUND (PixmapLoader::getPic("background_add_city"))
+#define ADD_ERROR_BACKGROUND (PixmapLoader::getPic("background_error_adding"))
+
+#define CLOSE_BUTTON_PIXMAP (PixmapLoader::getPic("button_list_delete"))
+#define ADD_BUTTON_PIXMAP (PixmapLoader::getPic("button_city_send"))
+#define BUTTON_LEFT (Settings::scaleWidth(408.0))
+
+#define ADD_TEXT_BACKGROUND (PixmapLoader::getPic("textfield_add_city"))
+#define ADD_TEXT_LEFT (Settings::scaleWidth(38.0))
+
+#define ADD_SCREEN_FONT_SIZE (Settings::scaleHeight(40.0))
+
+#define CITY_NAME_FONT_SIZE (Settings::scaleHeight(40.0))
+
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *parent, QGraphicsItem *item)
+{
+ const qreal top = (parent->boundingRect().height() - item->boundingRect().height()) / 2;
+ return top - parent->boundingRect().top() - item->boundingRect().top();
+}
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *item)
+{
+ return getCenterVerticalPos(item->parentItem(), item);
+}
+
+// AddCityScreen
+
+AddCityScreen::AddCityScreen(const QRectF &boundingRect, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(boundingRect)
+{
+}
+
+QRectF AddCityScreen::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void AddCityScreen::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+//
+
+#define ADD_A_CITY_TEXT "Add a city"
+
+AddCityLineEdit::AddCityLineEdit(QWidget *parent)
+ : QLineEdit(parent)
+ , m_clean(true)
+{
+ editReset();
+ connect(this, SIGNAL(textEdited(QString)), this, SLOT(textEditedSlot(QString)));
+}
+
+void AddCityLineEdit::editReset()
+{
+ m_clean = true;
+ setText(ADD_A_CITY_TEXT);
+}
+
+void AddCityLineEdit::focusOutEvent(QFocusEvent *event)
+{
+ if (m_clean)
+ setText(ADD_A_CITY_TEXT);
+ QLineEdit::focusOutEvent(event);
+}
+
+void AddCityLineEdit::focusInEvent(QFocusEvent *event)
+{
+ if (m_clean)
+ setText("");
+ QLineEdit::focusInEvent(event);
+}
+
+void AddCityLineEdit::textEditedSlot(const QString & text)
+{
+ Q_UNUSED(text);
+ static const QString cleanText(ADD_A_CITY_TEXT);
+ if (m_clean && text.length() >= cleanText.length() &&
+ text.left(cleanText.length()) == cleanText)
+ setText(text.right(text.length() - cleanText.length()));
+ m_clean = false;
+}
+
+// AddCityFirstScreen
+
+AddCityFirstScreen::AddCityFirstScreen(const QRectF &boundingRect, QGraphicsItem *parent)
+ : AddCityScreen(boundingRect, parent)
+ , m_textBackground(new QGraphicsPixmapItem(ADD_TEXT_BACKGROUND, this))
+ , m_button(new PixmapButton(80.0, ADD_BUTTON_PIXMAP, this))
+ , m_proxy(new QGraphicsProxyWidget(this))
+ , m_lineEdit(new AddCityLineEdit())
+{
+ setFlag(ItemHasNoContents, true);
+
+ m_button->setPos(BUTTON_LEFT, getCenterVerticalPos(m_button));
+ m_textBackground->setPos(ADD_TEXT_LEFT, getCenterVerticalPos(m_textBackground));
+
+
+ QFont font;
+ font.setFamily("Nokia Sans");
+ font.setPixelSize(CITY_NAME_FONT_SIZE);
+ font.setStyleStrategy(QFont::PreferAntialias);
+ m_lineEdit->setFont(font);
+
+ m_lineEdit->setFrame(false);
+ m_lineEdit->setTextMargins(0, 0, 0, 0);
+ m_lineEdit->setAttribute(Qt::WA_NoSystemBackground);
+ m_lineEdit->setStyleSheet("background: transparent; color:white");
+
+ QRect rect(0, 0, m_textBackground->pixmap().width(), m_textBackground->pixmap().height());
+ rect.adjust(5, 5, -5, -5);
+ m_lineEdit->setGeometry(rect);
+
+ m_proxy->setWidget(m_lineEdit);
+ m_proxy->setParentItem(m_textBackground);
+ m_proxy->setPos(5.0, 5.0);
+
+ m_lineEdit->setFocus();
+
+ connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClick()));
+}
+
+void AddCityFirstScreen::clean()
+{
+ m_lineEdit->editReset();
+}
+
+void AddCityFirstScreen::buttonClick()
+{
+ if (!m_lineEdit->text().isEmpty())
+ emit citySelected(m_lineEdit->text());
+}
+
+// AddCitySearchScreen
+
+AddCitySearchScreen::AddCitySearchScreen(const QRectF &boundingRect, QGraphicsItem *parent)
+ : AddCityScreen(boundingRect, parent)
+ , m_loading(new Loading(this))
+{
+ m_loading->hide();
+ m_loading->setPos(BUTTON_LEFT, getCenterVerticalPos(m_loading));
+
+}
+
+void AddCitySearchScreen::forecastResponse(int reqId, const ForecastData &forecast)
+{
+ if (reqId == m_reqId) {
+ reset();
+ if (!forecast.isNull() && !forecast.error())
+ emit forecastReceived(forecast);
+ else
+ emit forecastRequestError(m_city);
+ }
+}
+
+void AddCitySearchScreen::setCityName(const QString &name)
+{
+ m_city = name;
+ ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(int, ForecastData)));
+ m_reqId = ForecastProvider::getForecast(m_city, false);
+ m_loading->start();
+ m_loading->show();
+ update();
+}
+
+void AddCitySearchScreen::reset()
+{
+ ForecastProvider::disconnectReceiver(this);
+ m_loading->stop();
+ m_loading->hide();
+}
+
+void AddCitySearchScreen::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+
+ TextPainter header(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "Searching for");
+
+ TextPainter city(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 255), m_city);
+ city.setQuoted(true);
+ city.font().setBold(true);
+ city.setMaxWidth(BUTTON_LEFT);
+
+ qreal top = (boundingRect().height() - header.height() - city.height()) / 2;
+ top += boundingRect().top();
+
+ TextPainter::locateAtCenter(&header, 0.0, top, BUTTON_LEFT);
+ header.paint(painter);
+
+ TextPainter::locateAtCenter(&city, 0.0, top + header.height(), BUTTON_LEFT);
+ city.paint(painter);
+}
+
+// AddCityErrorScreen
+
+AddCityErrorScreen::AddCityErrorScreen(QGraphicsItem *parent)
+ : QGraphicsPixmapItem(ADD_ERROR_BACKGROUND, parent)
+ , m_button(new PixmapButton(80.0, CLOSE_BUTTON_PIXMAP, this))
+{
+ m_button->setPos(BUTTON_LEFT, getCenterVerticalPos(m_button));
+ connect(m_button, SIGNAL(clicked()), this, SIGNAL(closed()));
+}
+
+void AddCityErrorScreen::setCityName(const QString &name, ErrorType type)
+{
+ m_city = name;
+ m_type = type;
+ update();
+}
+
+void AddCityErrorScreen::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ QGraphicsPixmapItem::paint(painter, opt, widget);
+ if (m_type == NotFound)
+ paintNotFound(painter);
+ else
+ paintAlreadyInList(painter);
+}
+
+void AddCityErrorScreen::paintNotFound(QPainter *painter)
+{
+ TextPainter header(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "The city");
+ TextPainter footer(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "wasn't found");
+
+ TextPainter city(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 255), m_city);
+ city.setQuoted(true);
+ city.font().setBold(true);
+ city.setMaxWidth(BUTTON_LEFT - header.width() - 5.0);
+
+ qreal top = (boundingRect().height() - city.height() - footer.height()) / 2;
+ top += boundingRect().top();
+
+ QList<TextPainter*> topLine;
+ topLine.append(&header);
+ topLine.append(&city);
+
+ TextPainter::locateAtCenter(topLine, 0.0, top, BUTTON_LEFT);
+ header.paint(painter);
+ city.paint(painter);
+
+ TextPainter::locateAtCenter(&footer, 0.0, top + city.height(), BUTTON_LEFT);
+ footer.paint(painter);
+}
+
+void AddCityErrorScreen::paintAlreadyInList(QPainter *painter)
+{
+ TextPainter header(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "The city");
+ TextPainter header1(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "is");
+ TextPainter footer(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 102), "already on your list");
+
+ TextPainter city(ADD_SCREEN_FONT_SIZE, QColor(255, 255, 255, 255), m_city);
+ city.setQuoted(true);
+ city.font().setBold(true);
+ city.setMaxWidth(BUTTON_LEFT - header.width() - header1.width() - 10.0);
+
+ qreal top = (boundingRect().height() - city.height() - footer.height()) / 2;
+ top += boundingRect().top();
+
+ QList<TextPainter*> topLine;
+ topLine.append(&header);
+ topLine.append(&city);
+ topLine.append(&header1);
+
+ TextPainter::locateAtCenter(topLine, 0.0, top, BUTTON_LEFT);
+ header.paint(painter);
+ city.paint(painter);
+ header1.paint(painter);
+
+ TextPainter::locateAtCenter(&footer, 0.0, top + city.height(), BUTTON_LEFT);
+ footer.paint(painter);
+}
+
+// AddCityTool
+
+AddCityTool::AddCityTool(const QList<ForecastData> &content, QGraphicsItem *parent)
+ : QGraphicsPixmapItem(ADD_BACKGROUND, parent)
+ , m_content(content)
+ , m_firstScreen(createFirstScreen())
+ , m_SearchScreen(createSearchScreen())
+ , m_ErrorScreen(createErrorScreen())
+{
+ setCurrentScreen(m_firstScreen);
+}
+
+AddCityTool::~AddCityTool()
+{
+}
+
+int AddCityTool::loadImages()
+{
+ PixmapLoader::load("background_add_city");
+ PixmapLoader::load("background_error_adding");
+ PixmapLoader::load("button_list_delete");
+ PixmapLoader::load("button_city_send");
+ PixmapLoader::load("textfield_add_city");
+ return 5;
+}
+
+void AddCityTool::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+void AddCityTool::cancel()
+{
+ m_SearchScreen->cancel();
+ setCurrentScreen(m_firstScreen);
+}
+
+AddCityFirstScreen *AddCityTool::createFirstScreen()
+{
+ AddCityFirstScreen *result = new AddCityFirstScreen(boundingRect(), this);
+ connect(result, SIGNAL(citySelected(QString)), this, SLOT(citySelected(QString)));
+ result->setPos(0.0, 0.0);
+ return result;
+}
+
+AddCitySearchScreen *AddCityTool::createSearchScreen()
+{
+ AddCitySearchScreen *result = new AddCitySearchScreen(boundingRect(), this);
+ connect(result, SIGNAL(forecastReceived(ForecastData)),
+ this, SLOT(forecastReceived(ForecastData)));
+ connect(result, SIGNAL(forecastRequestError(QString)),
+ this, SLOT(forecastRequestError(QString)));
+ result->setPos(0.0, 0.0);
+ return result;
+}
+
+AddCityErrorScreen *AddCityTool::createErrorScreen()
+{
+ AddCityErrorScreen *result = new AddCityErrorScreen(this);
+ connect(result, SIGNAL(closed()), this, SLOT(errorScreenClosed()));
+ result->setPos(0.0, 0.0);
+ return result;
+}
+
+void AddCityTool::setCurrentScreen(QGraphicsItem *screen)
+{
+ m_firstScreen->clean();
+ m_firstScreen->setVisible(screen == m_firstScreen);
+ m_SearchScreen->setVisible(screen == m_SearchScreen);
+ m_ErrorScreen->setVisible(screen == m_ErrorScreen);
+}
+
+void AddCityTool::errorScreenClosed()
+{
+ setCurrentScreen(m_firstScreen);
+}
+
+void AddCityTool::forecastReceived(const ForecastData &forecast)
+{
+ for (int i = 0; i < m_content.count(); ++i)
+ if (m_content[i].key() == forecast.key()) {
+ m_ErrorScreen->setCityName(forecast.cityName(), AddCityErrorScreen::AlreadyInList);
+ setCurrentScreen(m_ErrorScreen);
+ return;
+ }
+ emit newForecast(forecast);
+ setCurrentScreen(m_firstScreen);
+}
+
+void AddCityTool::forecastRequestError(const QString &name)
+{
+ m_ErrorScreen->setCityName(name, AddCityErrorScreen::NotFound);
+ setCurrentScreen(m_ErrorScreen);
+}
+
+void AddCityTool::citySelected(const QString &city)
+{
+ QString text = city.toUpper();
+ for (int i = 0; i < m_content.count(); ++i)
+ if (m_content[i].key().toUpper() == text) {
+ m_ErrorScreen->setCityName(city, AddCityErrorScreen::AlreadyInList);
+ setCurrentScreen(m_ErrorScreen);
+ return;
+ }
+ m_SearchScreen->setCityName(city);
+ setCurrentScreen(m_SearchScreen);
+}
diff --git a/weather/src/addcitytool.h b/weather/src/addcitytool.h
new file mode 100644
index 0000000..f0f6b1f
--- /dev/null
+++ b/weather/src/addcitytool.h
@@ -0,0 +1,153 @@
+#ifndef ADDCITYTOOL_H
+#define ADDCITYTOOL_H
+
+#include <QGraphicsItem>
+#include <QGraphicsPixmapItem>
+#include <QBasicTimer>
+#include <QGraphicsSimpleTextItem>
+#include <QPen>
+#include <QBrush>
+#include <QFont>
+ #include <QGraphicsProxyWidget>
+#include <QLineEdit>
+#include <QList>
+
+#include "pixmapbutton.h"
+#include "forecastprovider.h"
+#include "loading.h"
+#include "forecastdata.h"
+
+class AddCityScreen : public QGraphicsItem
+{
+public:
+ AddCityScreen(const QRectF &boundingRect, QGraphicsItem *parent = 0);
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+private:
+ const QRectF m_boundingRect;
+};
+
+class AddCityLineEdit : public QLineEdit
+{
+ Q_OBJECT
+public:
+ AddCityLineEdit(QWidget *parent = 0);
+ void editReset();
+protected:
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+private:
+ bool m_clean;
+private slots:
+ void textEditedSlot(const QString & text);
+};
+
+
+class AddCityFirstScreen : public QObject, public AddCityScreen
+{
+ Q_OBJECT
+public:
+ AddCityFirstScreen(const QRectF &boundingRect, QGraphicsItem *parent = 0);
+ void clean();
+
+signals:
+ void citySelected(const QString &city);
+
+private:
+ QGraphicsPixmapItem * const m_textBackground;
+ PixmapButton * const m_button;
+ QString m_text;
+ QGraphicsProxyWidget *m_proxy;
+ AddCityLineEdit *m_lineEdit;
+
+private slots:
+ void buttonClick();
+};
+
+class AddCitySearchScreen : public QObject , public AddCityScreen
+{
+ Q_OBJECT
+public:
+ AddCitySearchScreen(const QRectF &boundingRect, QGraphicsItem *parent = 0);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ void setCityName(const QString &name);
+ void cancel() { reset(); }
+
+signals:
+ void forecastReceived(const ForecastData &forecast);
+ void forecastRequestError(const QString &name);
+
+private:
+ QString m_city;
+ int m_reqId;
+ Loading *m_loading;
+
+ void reset();
+
+private slots:
+ void forecastResponse(int reqId, const ForecastData &forecast);
+};
+
+class AddCityErrorScreen : public QObject, public QGraphicsPixmapItem
+{
+ Q_OBJECT
+public:
+ enum ErrorType
+ {
+ NotFound,
+ AlreadyInList
+ };
+ AddCityErrorScreen(QGraphicsItem *parent = 0);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ void setCityName(const QString &name, ErrorType type);
+
+signals:
+ void closed();
+
+private:
+ PixmapButton * const m_button;
+ QString m_city;
+ ErrorType m_type;
+
+ void paintNotFound(QPainter *painter);
+ void paintAlreadyInList(QPainter *painter);
+};
+class AddCityTool : public QObject, public QGraphicsPixmapItem
+{
+ Q_OBJECT
+public:
+ AddCityTool(const QList<ForecastData> &content, QGraphicsItem *parent = 0);
+ ~AddCityTool();
+ void cancel();
+
+ static int loadImages();
+
+private:
+ const QList<ForecastData> &m_content;
+ AddCityFirstScreen * const m_firstScreen;
+ AddCitySearchScreen * const m_SearchScreen;
+ AddCityErrorScreen * const m_ErrorScreen;
+
+ AddCityFirstScreen *createFirstScreen();
+ AddCitySearchScreen *createSearchScreen();
+ AddCityErrorScreen *createErrorScreen();
+
+ void setCurrentScreen(QGraphicsItem *screen);
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+signals:
+ void newForecast(const ForecastData &forecast);
+
+private slots:
+ void errorScreenClosed();
+ void forecastReceived(const ForecastData &forecast);
+ void forecastRequestError(const QString &name);
+ void citySelected(const QString &city);
+
+};
+
+#endif // ADDCITYTOOL_H
diff --git a/weather/src/bootmanager.cpp b/weather/src/bootmanager.cpp
new file mode 100644
index 0000000..ac41a8f
--- /dev/null
+++ b/weather/src/bootmanager.cpp
@@ -0,0 +1,69 @@
+#include "bootmanager.h"
+#include "settings.h"
+#include "forecastprovider.h"
+#include "pixmaploader.h"
+#include "forecasthungitem.h"
+#include "forecastrain.h"
+#include "forecastsnow.h"
+#include "forecaststars.h"
+#include "cityinfodisplay.h"
+#include "titlebar.h"
+#include "citycarroussel.h"
+#include "scrollbar.h"
+#include "citylist.h"
+#include "addcitytool.h"
+
+#include <QDebug>
+
+BootManager::BootManager(QObject *parent)
+ : QObject(parent)
+ , m_imagesLoaded(false)
+{
+}
+
+void BootManager::run(const QStringList &locations)
+{
+ m_keys = locations;
+ ForecastProvider::connectToResponseSignal(this, SLOT(forecastResponse(int, ForecastData)));
+ foreach (const QString & city, m_keys) {
+ m_requests.append(ForecastProvider::getForecast(city, true));
+ m_data.append(ForecastData());
+ }
+ PixmapLoader::connectToOnIdleSignal(this, SLOT(pixmapLoaderIsIdle()));
+ int count = ForecastHungItem::loadImages();
+ count += ForecastRain::loadImages();
+ count += ForecastSnow::loadImages();
+ count += ForecastStars::loadImages();
+ count += CityInfoDisplay::loadImages();
+ count += TitleBar::loadImages();
+ count += CityCarroussel::loadImages();
+ count += ScrollBar::loadImages();
+ count += CityList::loadImages();
+ count += AddCityTool::loadImages();
+
+ m_imagesLoaded = count == 0;
+ if (m_imagesLoaded)
+ PixmapLoader::disconnectReceiver(this);
+}
+
+void BootManager::forecastResponse(int reqId, const ForecastData &forecast)
+{
+ if (m_requests.removeAll(reqId)) {
+ int idx = m_keys.indexOf(forecast.key());
+ if (idx >= 0)
+ m_data[idx] = forecast;
+ }
+ if (m_requests.isEmpty()) {
+ ForecastProvider::disconnectReceiver(this);
+ if (m_imagesLoaded)
+ emit ready();
+ }
+}
+
+void BootManager::pixmapLoaderIsIdle()
+{
+ PixmapLoader::disconnectReceiver(this);
+ m_imagesLoaded = true;
+ if (m_requests.isEmpty())
+ emit ready();
+}
diff --git a/weather/src/bootmanager.h b/weather/src/bootmanager.h
new file mode 100644
index 0000000..4fc4969
--- /dev/null
+++ b/weather/src/bootmanager.h
@@ -0,0 +1,31 @@
+#ifndef BOOTMANAGER_H
+#define BOOTMANAGER_H
+
+#include <QObject>
+#include <QMap>
+#include <QStringList>
+#include "forecastdata.h"
+
+class BootManager : public QObject
+{
+ Q_OBJECT
+public:
+ BootManager(QObject *parent = 0);
+ void run(const QStringList &locations);
+ QList<ForecastData> data() const { return m_data; }
+
+signals:
+ void ready();
+
+private slots:
+ void forecastResponse(int reqId, const ForecastData &forecast);
+ void pixmapLoaderIsIdle();
+
+private:
+ QStringList m_keys;
+ QList<ForecastData> m_data;
+ QList<int> m_requests;
+ bool m_imagesLoaded;
+};
+
+#endif // BOOTMANAGER_H
diff --git a/weather/src/carroussel.h b/weather/src/carroussel.h
new file mode 100644
index 0000000..983cbc8
--- /dev/null
+++ b/weather/src/carroussel.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef __CARROUSSEL_H__
+#define __CARROUSSEL_H__
+
+#include <QList>
+#include <QDebug>
+
+
+template<class T> class Carroussel
+{
+public:
+ Carroussel() : m_pos(0) {}
+ ~Carroussel() {}
+ int count() const { return m_list.count(); }
+ int pos() const { return m_pos; }
+ void move(int offset) { m_pos = getIndex(m_pos + offset); }
+ int add(const T &item);
+ void remove(int idx);
+ T &operator[](int aIdx) { return m_list[getIndex(aIdx + m_pos)]; }
+ const T &operator[](int aIdx) const { return m_list[getIndex(aIdx + m_pos)]; }
+ void reset(const QList<T> &list, int newPos);
+private:
+ int m_pos;
+ QList<T> m_list;
+ int getIndex(int idx) const;
+ int getCarrousselIndex(int idx);
+};
+
+class CarrousselGroup
+{
+public:
+ inline ~CarrousselGroup();
+ template<class T> void add(T *carroussel);
+ inline void remove(void *carroussel);
+ inline void move(int offset);
+
+private:
+ class BasicCarrosselHandler
+ {
+ public:
+ virtual ~BasicCarrosselHandler() {}
+ virtual void move(int offset) = 0;
+ virtual bool isEqual(void *value) = 0;
+ };
+ template<class T> class CarrousselHandler : public BasicCarrosselHandler
+ {
+ public:
+ CarrousselHandler(T *carroussel) : m_carroussel(carroussel) {}
+ void move(int offset) { m_carroussel->move(offset); }
+ bool isEqual(void *value) { return m_carroussel == value; }
+ private:
+ T *m_carroussel;
+ };
+
+ QList<BasicCarrosselHandler*> m_items;
+ inline int find(void *value);
+};
+
+// Carroussel
+
+template<class T> int Carroussel<T>::add(const T &item)
+{
+ int idx = m_list.count() - m_pos;
+ m_list.append(item);
+ return idx <= m_list.count() / 2 ? idx : idx - m_list.count();
+}
+
+template<class T> void Carroussel<T>::remove(int idx)
+{
+ idx = getIndex(m_pos + idx);
+ m_list.removeAt(idx);
+ m_pos = getIndex(idx < m_pos ? m_pos - 1 : m_pos);
+}
+
+template<class T> void Carroussel<T>::reset(const QList<T> &list, int newPos)
+{
+ m_list = list;
+ m_pos = getIndex(newPos);
+}
+
+template<class T> int Carroussel<T>::getIndex(int idx) const
+{
+ if (m_list.count() == 0)
+ return 0;
+ idx %= m_list.count();
+ return idx >= 0 ? idx : m_list.count() + idx;
+}
+
+// CarrousselGroup
+
+CarrousselGroup::~CarrousselGroup()
+{
+ qDeleteAll(m_items);
+}
+
+template<class T> void CarrousselGroup::add(T *carroussel)
+{
+ if (find(carroussel) == -1)
+ m_items.append(new CarrousselHandler<T>(carroussel));
+}
+
+void CarrousselGroup::remove(void *carroussel)
+{
+ int idx = find(carroussel);
+ if (idx >= 0)
+ delete m_items.takeAt(idx);
+}
+
+int CarrousselGroup::find(void *value)
+{
+ for (int i = 0; i < m_items.count(); ++i)
+ if (m_items[i]->isEqual(value))
+ return i;
+ return -1;
+}
+
+void CarrousselGroup::move(int offset)
+{
+ foreach (BasicCarrosselHandler *handler, m_items)
+ handler->move(offset);
+}
+
+
+
+
+#endif /* __CARROUSSEL_H__ */
diff --git a/weather/src/citycarroussel.cpp b/weather/src/citycarroussel.cpp
new file mode 100644
index 0000000..3f2d5a3
--- /dev/null
+++ b/weather/src/citycarroussel.cpp
@@ -0,0 +1,472 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "citycarroussel.h"
+#include "settings.h"
+#include "pixmaploader.h"
+#include "titlebar.h"
+
+#include <QFont>
+
+struct ForecastEnvironment
+{
+ QPixmap picture;
+ QPixmap effect;
+ QString description;
+};
+
+struct ForecastBackgroundData
+{
+ const Forecast::ForecastType type;
+ const char * nightName;
+ const char* dayName;
+ const Forecast::Effect effect;
+ const char* description;
+
+ static ForecastEnvironment getEnvironment(Forecast::ForecastType forecast, bool night);
+
+private:
+ QString picName(bool night) const { return night ? nightName : dayName; }
+
+};
+
+static ForecastBackgroundData BackgroundData[Forecast::UnknownForecast + 1] = {
+ {Forecast::MostlyCloudy, "bg_night_rain", "bg_day_rain", Forecast::NoEffect, "mostly cloudy"},
+ {Forecast::Cloudy, "bg_night_rain", "bg_day_rain", Forecast::NoEffect, "cloudy"},
+ {Forecast::MostlySunny, "bg_night_clear", "bg_day_clear", Forecast::NoEffect, "mostly sunny"},
+ {Forecast::PartlyCloudy, "bg_night_clear", "bg_day_clear", Forecast::NoEffect, "partially cloudy"},
+ {Forecast::Sunny, "bg_night_clear", "bg_day_clear", Forecast::NoEffect, "sunny"},
+ {Forecast::Flurries, "bg_night_rain", "bg_day_rain", Forecast::NoEffect, "flurries"},
+ {Forecast::Fog, "bg_night_rain", "bg_day_heavyrain", Forecast::FogEffect, "fog"},
+ {Forecast::Haze, "bg_night_rain", "bg_day_heavyrain", Forecast::HazeEffect, "haze"},
+ {Forecast::Sand, "bg_night_rain", "bg_day_heavyrain", Forecast::HazeEffect, "sand"},
+ {Forecast::Dust, "bg_night_rain", "bg_day_heavyrain", Forecast::HazeEffect, "dust"},
+ {Forecast::Icy, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "icy"},
+ {Forecast::Sleet, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "sleet"},
+ {Forecast::ChanceOfSleet, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "chance of sleet"},
+ {Forecast::Snow, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "snow"},
+ {Forecast::ChanceOfSnow, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "chance of snow"},
+ {Forecast::Mist, "bg_night_rain", "bg_day_rain", Forecast::NoEffect, "mist"},
+ {Forecast::Rain, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "rain"},
+ {Forecast::ChanceOfRain, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "chance of rain"},
+ {Forecast::Storm, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "storm"},
+ {Forecast::ChanceOfStorm, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "chance of storm"},
+ {Forecast::Thunderstorm, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "thunderstorm"},
+ {Forecast::ChanceOfThunderstorm, "bg_night_rain", "bg_day_heavyrain", Forecast::NoEffect, "chance of thunderstorm"},
+ {Forecast::UnknownForecast, "bg_night_rain", "bg_night_rain", Forecast::NoEffect, "unknown"}
+};
+
+
+ForecastEnvironment ForecastBackgroundData::getEnvironment(Forecast::ForecastType forecast,
+ bool night)
+{
+ ForecastEnvironment result;
+ for (int i = 0; i < Forecast::UnknownForecast; ++i) {
+ if (forecast == BackgroundData[i].type) {
+ result.picture = PixmapLoader::getPic(BackgroundData[i].picName(night));
+
+ const Forecast::Effect effect = BackgroundData[i].effect;
+ const QString name = effect == Forecast::HazeEffect ? "haze"
+ : effect == Forecast::FogEffect ? "fog"
+ : QString();
+ result.effect = name.isNull() ? QPixmap() : PixmapLoader::getPic(name);
+
+ result.description = BackgroundData[i].description;
+ if (night)
+ result.description.append(" at night");
+ }
+ }
+ return result;
+}
+
+// ForecastBackground
+
+ForecastBackground::ForecastBackground(QGraphicsItem *parent)
+ : QGraphicsPixmapItem(parent)
+ , m_forecast(Forecast::UnknownForecast)
+ , m_night(false)
+ , m_effect(0)
+{
+ setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+}
+
+void ForecastBackground::setForecast(Forecast::ForecastType forecast, bool night)
+{
+ if (forecast == m_forecast && night == m_night)
+ return;
+ m_forecast = forecast;
+ m_night = night;
+ ForecastEnvironment environment = ForecastBackgroundData::getEnvironment(m_forecast, m_night);
+ setPixmap(environment.picture);
+ m_description = environment.description;
+
+ if (environment.effect.isNull()) {
+ delete m_effect;
+ m_effect = 0;
+ }
+ else {
+ if (!m_effect) {
+ m_effect = new QGraphicsPixmapItem(this);
+ m_effect->setZValue(20.0);
+ m_effect->setPos(0.0, 0.0);
+ }
+ m_effect->setPixmap(environment.effect);
+ }
+}
+
+void ForecastBackground::setReferencePos(qreal pos)
+{
+ m_pos = pos;
+ m_displacement = 0;
+ setPos(m_pos, 0.0);
+}
+
+void ForecastBackground::setDisplacement(qreal displacement)
+{
+ m_displacement = displacement;
+ setPos(m_pos + m_displacement, 0.0);
+}
+
+
+void ForecastBackground::addCompanion(QGraphicsItem *item)
+{
+ if (m_companion.indexOf(item) == -1)
+ m_companion.append(item);
+
+}
+
+void ForecastBackground::removeCompanion(QGraphicsItem *item)
+{
+ m_companion.removeAll(item);
+}
+
+QVariant ForecastBackground::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ if (change == ItemPositionChange) {
+ const QPointF move = value.toPointF() - pos();
+ for (int i = 0; i < m_companion.count(); ++i)
+ m_companion[i]->moveBy(move.x(), move.y());
+ }
+ return QGraphicsPixmapItem::itemChange(change, value);
+}
+
+// CityCarrousselGesture
+
+CityCarrousselGesture::CityCarrousselGesture(CityCarroussel &carroussel, QGraphicsItem *parent)
+ : GestureBox(parent)
+ , m_carroussel(carroussel)
+ , m_active(true)
+ , m_aborted(false)
+ , m_startPoint(0.0)
+{
+}
+
+void CityCarrousselGesture::gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick)
+{
+ Q_UNUSED(pos);
+ Q_UNUSED(acceptClick);
+ startGesture = m_carroussel.active();
+}
+
+void CityCarrousselGesture::gestureStart(QPointF pos)
+{
+ m_startPoint = pos.x();
+ m_aborted = false;
+}
+
+void CityCarrousselGesture::gestureMove(QPointF pos, QPointF movement, QPointF speed)
+{
+ Q_UNUSED(movement);
+ Q_UNUSED(speed);
+ if (!m_aborted)
+ m_carroussel.setGestureDisplacement(pos.x() - m_startPoint);
+}
+
+void CityCarrousselGesture::gestureEnd(QPointF pos, QPointF speed)
+{
+ Q_UNUSED(pos);
+ Q_UNUSED(speed);
+ if (!m_aborted)
+ m_carroussel.move(0);
+}
+
+// CityCarroussel
+
+static const qreal transparencyRef = 58.0 / 480.0;
+static const qreal marginRef = 47.0 / 480.0;
+static const qreal backgroundPosRef = -(transparencyRef + marginRef);
+static const qreal backgroundWidthRef = 635.0 / 480.0;
+
+CityCarroussel::CityCarroussel(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_view(0)
+ , m_displacement(0.0)
+ , m_gestureBox(new CityCarrousselGesture(*this, this))
+ , m_boundingRect(QPointF(0.0, 0.0), Settings::windowSize())
+ , m_backgroundWidth(backgroundWidthRef * m_boundingRect.width())
+ , m_backgroundPos(backgroundPosRef * m_boundingRect.width())
+ , m_transparencySize(transparencyRef * m_boundingRect.width())
+ , m_distance(m_backgroundWidth - m_transparencySize)
+ , m_active(true)
+ , m_deleteAfterMove(false)
+{
+ m_gestureBox->setRect(boundingRect());
+ m_carroussel.add(&m_data);
+ m_carroussel.add(&m_background);
+ m_carroussel.add(&m_cityInfo);
+
+ TitleBar *titleBar = new TitleBar(this);
+ titleBar->setPos(0.0, 0.0);
+ titleBar->setZValue(100.0);
+
+ QPixmap barPic = PixmapLoader::getPic("city_name_background_bigger");
+ QGraphicsPixmapItem * cityBar = new QGraphicsPixmapItem(barPic, this);
+ cityBar->setPos(Settings::scaleWidth(-20.0), Settings::scaleHeight(735.0));
+ cityBar->setZValue(10.0);
+
+ m_positions[0] = m_backgroundPos - m_distance;
+ m_positions[1] = m_backgroundPos;
+ m_positions[2] = m_backgroundPos + m_distance;
+
+ for (int i = -1; i <= 1; ++i) {
+ ForecastBackground *background = new ForecastBackground(this);
+ m_background.add(background);
+
+ CityInfoDisplay *city = new CityInfoDisplay(this);
+ m_cityInfo.add(city);
+ city->setPos(background->pos().x() - m_backgroundPos + Settings::windowSize().width() / 2,
+ background->pos().y() + Settings::scaleHeight(525.0));
+ city->setZValue(11.0);
+ background->addCompanion(city);
+ }
+
+ updateBackground(-1);
+ updateBackground(0);
+ updateBackground(1);
+}
+
+CityCarroussel::~CityCarroussel()
+{
+}
+
+int CityCarroussel::loadImages()
+{
+ for (int i = 0; i < Forecast::UnknownForecast;++i) {
+ PixmapLoader::load(BackgroundData[i].nightName);
+ PixmapLoader::load(BackgroundData[i].dayName);
+ }
+ PixmapLoader::load("haze");
+ PixmapLoader::load("fog");
+ PixmapLoader::load("city_name_background_bigger");
+ return Forecast::UnknownForecast + 3;
+
+}
+
+void CityCarroussel::setActive(bool value)
+{
+ if (value == m_active)
+ return;
+ m_active = value;
+ if (m_active)
+ connect(m_cityInfo[0], SIGNAL(nameClicked()), this, SIGNAL(cityNameClicked()));
+ else
+ disconnect(m_cityInfo[0]);
+}
+
+QRectF CityCarroussel::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityCarroussel::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+void CityCarroussel::add(ForecastData item)
+{
+ int idx = m_data.add(item);
+ if (idx < -1 || idx > 1)
+ return;
+ if (m_data.count()< 3) {
+ updateBackground(-1);
+ updateBackground(0);
+ updateBackground(1);
+ } else
+ updateBackground(idx);
+ if (idx == 0)
+ updateMainItem();
+}
+
+void CityCarroussel::update(QList<ForecastData> items)
+{
+ if (items.isEmpty())
+ return;
+
+ bool wasEmpty = m_data.count() == 0;
+ bool transitionNeeded = false;
+
+ if (!wasEmpty) {
+ for (int i = 0; i < items.count(); ++i) {
+ if (items[i].key() == m_data[0].key()) {
+ for (int j = 0; j < i; ++j)
+ items.append(items.takeFirst());
+ break;
+ }
+ }
+ transitionNeeded = m_data[0] != items[0];
+ if (transitionNeeded)
+ items.push_front(m_data[0]);
+ }
+
+ m_data.reset(items, 0);
+ updateBackground(-1);
+ updateBackground(1);
+
+ if (wasEmpty) {
+ updateBackground(0);
+ updateMainItem();
+ }
+ if (transitionNeeded) {
+ move(-1, true);
+ }
+}
+
+void CityCarroussel::updateMainItem()
+{
+ delete m_view;
+ m_view = ForecastView::createView(m_data[0].type(), m_data[0].night());
+ if (m_view) {
+ connect(m_cityInfo[0], SIGNAL(nameClicked()), this, SIGNAL(cityNameClicked()));
+ m_view->setPos(-m_positions[1], 0.0);
+ m_view->setZValue(10.0);
+ m_view->setParentItem(m_background[0]);
+ m_view->reset();
+
+ QAbstractAnimation * animation = m_view->getAnimation();
+ if (animation)
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ }
+}
+
+void CityCarroussel::updateBackground(int idx)
+{
+ if (m_data.count() == 0)
+ m_background[idx]->reset();
+ else
+ m_background[idx]->setForecast(m_data[idx].type(), m_data[idx].night());
+ m_background[idx]->setZValue(idx);
+ m_background[idx]->setReferencePos(m_positions[idx + 1]);
+ QString cityName = m_data.count() ? m_data[idx].cityName() : QString("unknown");
+ m_cityInfo[idx]->setCityName(cityName);
+ if (m_data.count() > 0)
+ m_cityInfo[idx]->setTemperature(m_data[idx].lower(),
+ m_data[idx].upper(), m_data[idx].current());
+ else
+ m_cityInfo[idx]->setTemperature(0, 0, 0);
+}
+
+void CityCarroussel::moveEnd(int direction)
+{
+ m_displacement = 0.0;
+ m_gestureBox->setActive(true);
+ if (direction) {
+ m_carroussel.move(-direction);
+ for (int i = -1; i <= 1; ++i) {
+ m_background[i]->setReferencePos(m_positions[i + 1]);
+ m_background[i]->setZValue(i);
+ }
+ if (m_deleteAfterMove) {
+ m_data.remove(direction);
+ updateBackground(direction);
+ }
+
+ updateBackground(-direction);
+ updateMainItem();
+ }
+}
+
+void CityCarroussel::setDisplacement(qreal displacement)
+{
+ m_displacement = displacement;
+ for (int i = -1; i <= 1; ++i)
+ m_background[i]->setDisplacement(m_displacement);
+ if (m_view)
+ m_view->setElementsDisplacement(m_displacement / m_distance);
+}
+
+void CityCarroussel::setGestureDisplacement(qreal displacement)
+{
+ if (qAbs(displacement) > boundingRect().width() * 0.5) {
+ move(displacement < 0 ? -1 : 1);
+ }
+ else
+ setDisplacement(displacement);
+}
+
+void CityCarroussel::move(int direction, bool deleteLastItem)
+{
+ m_gestureBox->setActive(false);
+ m_gestureBox->abort();
+ disconnect(m_cityInfo[0]);
+ m_deleteAfterMove = deleteLastItem;
+
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "displacement");
+
+ animation->setEndValue(direction * m_distance);
+ animation->setEasingCurve(QEasingCurve::OutQuart);
+ animation->setDuration(500);
+
+ switch(direction) {
+ case -1:
+ connect(animation, SIGNAL(finished()), this, SLOT(moveLeftEnd()));
+ break;
+ case 0:
+ connect(animation, SIGNAL(finished()), this, SLOT(moveBackEnd()));
+ break;
+ case 1:
+ connect(animation, SIGNAL(finished()), this, SLOT(moveRightEnd()));
+ break;
+ }
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+
+
+
+
+
+
+
+
diff --git a/weather/src/citycarroussel.h b/weather/src/citycarroussel.h
new file mode 100644
index 0000000..8bea25b
--- /dev/null
+++ b/weather/src/citycarroussel.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CITYCARROUSSEL_H
+#define CITYCARROUSSEL_H
+
+#include "forecast.h"
+#include "forecastview.h"
+#include "carroussel.h"
+#include "gesturebox.h"
+#include "cityinfodisplay.h"
+#include "forecastdata.h"
+
+#include <QSharedPointer>
+#include <QWeakPointer>
+
+class ForecastBackground : public QGraphicsPixmapItem
+{
+public:
+ ForecastBackground(QGraphicsItem *parent = 0);
+ Forecast::ForecastType forecast() const { return m_forecast; }
+ bool night() const { return m_night; }
+
+ void setForecast(Forecast::ForecastType forecast, bool night);
+ void reset() { setForecast(Forecast::UnknownForecast, false); }
+
+ void setReferencePos(qreal pos);
+ void setDisplacement(qreal displacement);
+
+ void addCompanion(QGraphicsItem *item);
+ void removeCompanion(QGraphicsItem *item);
+
+ QString description() const { return m_description; }
+
+private:
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+private:
+ Forecast::ForecastType m_forecast;
+ bool m_night;
+ qreal m_pos;
+ qreal m_displacement;
+ QList<QGraphicsItem*> m_companion;
+
+ QGraphicsPixmapItem *m_effect;
+ QString m_description;
+
+};
+
+class CityCarroussel;
+
+class CityCarrousselGesture : public GestureBox
+{
+public:
+ CityCarrousselGesture(CityCarroussel &carroussel, QGraphicsItem *parent = 0);
+ void setActivate(bool active) { m_active = active; }
+ void abort() { m_aborted = true; }
+
+protected:
+ void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick);
+ void gestureStart(QPointF pos);
+ void gestureMove(QPointF pos, QPointF movement, QPointF speed);
+ void gestureEnd(QPointF pos, QPointF speed);
+
+private:
+ CityCarroussel &m_carroussel;
+ bool m_active;
+ bool m_aborted;
+ qreal m_startPoint;
+
+};
+
+class CityCarroussel : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(qreal displacement READ displacement WRITE setDisplacement)
+public:
+ CityCarroussel(QGraphicsItem *parent = 0);
+ ~CityCarroussel();
+ void add(ForecastData item);
+
+ void update(QList<ForecastData> items);
+
+ static int loadImages();
+
+ bool active() const { return m_active; }
+ void setActive(bool value);
+
+ QString selected() const { return m_data.count() > 0 ? m_data[0].key() : QString(); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+signals:
+ void cityNameClicked();
+
+public slots:
+ void moveLeft() { move(-1); }
+ void moveRight() { move(1); }
+
+private:
+ friend class CityCarrousselGesture;
+
+ Carroussel<ForecastData> m_data;
+ Carroussel<ForecastBackground*> m_background;
+ Carroussel<CityInfoDisplay*> m_cityInfo;
+
+ CarrousselGroup m_carroussel;
+ ForecastView *m_view;
+ qreal m_displacement;
+ CityCarrousselGesture *m_gestureBox;
+ const QRectF m_boundingRect;
+ const qreal m_backgroundWidth;
+ const qreal m_backgroundPos;
+ const qreal m_transparencySize;
+ const qreal m_distance;
+ qreal m_positions[3];
+
+ bool m_active;
+ bool m_deleteAfterMove;
+
+ void setGestureDisplacement(qreal displacement);
+ void moveBack() { move(0); }
+ void move(int direction, bool deleteLastItem = false);
+ void moveEnd(int direction);
+
+ void updateBackground(int idx);
+ void updateMainItem();
+
+ void setDisplacement(qreal displacement);
+ qreal displacement() const { return m_displacement; }
+
+private slots:
+ void moveLeftEnd() { moveEnd(-1); }
+ void moveRightEnd() { moveEnd(1); }
+ void moveBackEnd() { moveEnd(0); }
+};
+
+#endif // CITYCARROUSSEL_H
diff --git a/weather/src/cityinfodisplay.cpp b/weather/src/cityinfodisplay.cpp
new file mode 100644
index 0000000..570ef8a
--- /dev/null
+++ b/weather/src/cityinfodisplay.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "cityinfodisplay.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+#include <QFont>
+#include <QPen>
+#include <QFontMetrics>
+#include <QDebug>
+#include <QPainter>
+#include <QGraphicsSceneMouseEvent>
+
+#define CURRENT_TEMP_FONT_SIZE (Settings::scaleHeight(170.0))
+#define TEMP_BOUND_FONT_SIZE (Settings::scaleHeight(40.0))
+#define CITY_NAME_FONT_SIZE (Settings::scaleHeight(50.0))
+#define MAX_CITY_NAME_WIDTH (Settings::scaleWidth(440.0))
+#define CITY_NAME_TOP (Settings::scaleHeight(235.0))
+
+// CurrentTemperatureDisplay
+
+CurrentTemperatureDisplay::CurrentTemperatureDisplay()
+ : m_brush(Qt::white)
+ , m_sign(PixmapLoader::getPic("minus_sign"))
+ , m_unit(PixmapLoader::getPic("centigrades"))
+ , m_value(-1)
+ , m_pos(0.0, 0.0)
+{
+ m_font.setFamily("Nokia Sans");
+ m_font.setBold(true);
+ m_font.setPixelSize(CURRENT_TEMP_FONT_SIZE);
+ m_font.setStyleStrategy(QFont::PreferAntialias);
+
+ m_pen.setColor(Qt::white);
+ m_pen.setBrush(Qt::white);
+ m_pen.setJoinStyle(Qt::RoundJoin);
+
+ setTemperature(0);
+}
+
+QRectF CurrentTemperatureDisplay::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CurrentTemperatureDisplay::paint(QPainter *painter)
+{
+ if (m_value < 0)
+ painter->drawPixmap(m_signPos, m_sign);
+
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->setFont(m_font);
+ painter->drawText(m_numberPos, m_text);
+
+ painter->drawPixmap(m_unitPos, m_unit);
+}
+
+void CurrentTemperatureDisplay::setTemperature(int value)
+{
+ static const int maxValue = 300;
+ value = qMax(qMin(maxValue, value), -maxValue);
+
+ if (m_value == value)
+ return;
+
+ m_value = value;
+ m_text = QString::number(qAbs(m_value));
+
+ QFontMetrics metrics(m_font);
+
+ int textWidth = metrics.width(m_text);
+ int width = textWidth;
+ int left = 0;
+
+ if (m_value < 0) {
+ left = -(width >> 1);
+ m_numberPos = QPointF(left, m_unit.height());
+ width += m_sign.width();
+ left -= m_sign.width();
+ m_signPos = QPointF(left, 0.0);
+ m_unitPos = QPointF(left + width, 0.0);
+ width += m_unit.width();
+ } else {
+ width += m_unit.width();
+ int left = -(width >> 1);
+ m_numberPos = QPointF(left, m_unit.height());
+ m_unitPos = QPointF(left + textWidth, 0.0);
+ }
+ m_boundingRect = QRectF(left, 0.0, width, m_unit.height());
+
+}
+
+// TemperatureBoundDisplay
+
+TemperatureBoundDisplay::TemperatureBoundDisplay(bool lowerBound)
+ : m_brush(Qt::white)
+ , m_icon(PixmapLoader::getPic(lowerBound ? "icon_min" : "icon_max"))
+ , m_value(-1)
+ , m_lowerBound(lowerBound)
+ , m_pos(0.0, 0.0)
+{
+ m_font.setFamily("Nokia Sans");
+ m_font.setPixelSize(TEMP_BOUND_FONT_SIZE);
+ m_font.setStyleStrategy(QFont::PreferAntialias);
+
+ QColor color(255, 255, 255, 178);
+
+ m_pen.setColor(color);
+ m_pen.setBrush(color);
+ m_pen.setJoinStyle(Qt::RoundJoin);
+
+ m_brush.setColor(color);
+
+ setValue(0);
+}
+
+QRectF TemperatureBoundDisplay::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void TemperatureBoundDisplay::paint(QPainter *painter)
+{
+ QRectF rect(m_boundingRect);
+ painter->drawPixmap(rect.topLeft(), m_icon);
+ rect.setLeft(rect.left() + m_icon.width());
+
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->setFont(m_font);
+ QPointF textPos(rect.left(), m_icon.height());
+ painter->drawText(textPos, m_text);
+}
+
+void TemperatureBoundDisplay::setValue(int value)
+{
+ static const int maxValue = 300;
+ value = qMax(qMin(maxValue, value), -maxValue);
+
+ if (m_value == value)
+ return;
+
+ m_value = value;
+ m_text = QString::number(m_value) + QChar(176) + 'c';
+
+ QFontMetrics metrics(m_font);
+ m_boundingRect = QRectF(0.0, 0.0, m_icon.width() + metrics.width(m_text), m_icon.height());
+}
+
+// TemperatureDisplay
+
+TemperatureDisplay::TemperatureDisplay()
+ : m_line(PixmapLoader::getPic("division_line"))
+ , m_lowerBound(true)
+ , m_upperBound(false)
+ , m_pos(0.0, 0.0)
+{
+ setTemperature(0, 0, 0);
+}
+
+void TemperatureDisplay::setTemperature(int lowerBound, int upperBound, int current)
+{
+ m_lowerBound.setValue(lowerBound);
+ m_upperBound.setValue(upperBound);
+ m_current.setTemperature(current);
+
+ m_current.setPos(0.0, 0.0);
+
+ const qreal lineRight = m_line.width() >> 1;
+ const qreal right = qMax(lineRight, m_current.boundingRect().right());
+ const qreal left = qMin(-lineRight, m_current.boundingRect().left());
+
+ qreal height = m_current.boundingRect().height() + 8.0;
+
+ m_linePos = QPointF(-lineRight, height);
+ height += m_line.height() + 8.0;
+
+ m_upperBound.setPos(-lineRight, height);
+ m_lowerBound.setPos(lineRight - m_lowerBound.boundingRect().width(), height);
+
+ height += qMax(m_lowerBound.boundingRect().height(), m_upperBound.boundingRect().height());
+
+ m_boundingRect = QRectF(left, 0.0, right - left, height);
+}
+
+QRectF TemperatureDisplay::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void TemperatureDisplay::paint(QPainter *painter)
+{
+ painter->translate(m_current.pos());
+ m_current.paint(painter);
+
+ painter->translate(m_upperBound.pos() - m_current.pos());
+ m_upperBound.paint(painter);
+
+ painter->translate(m_lowerBound.pos() - m_upperBound.pos());
+ m_lowerBound.paint(painter);
+
+ painter->translate(-m_lowerBound.pos());
+
+ painter->drawPixmap(m_linePos, m_line);
+}
+
+// CityInfoDisplay
+
+CityInfoDisplay::CityInfoDisplay(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_brush(Qt::white)
+{
+ setCacheMode(ItemCoordinateCache);
+ m_temperature.setPos(0.0, 0.0);
+
+ m_font.setFamily("Nokia Sans");
+ m_font.setPixelSize(CITY_NAME_FONT_SIZE);
+ m_font.setStyleStrategy(QFont::PreferAntialias);
+
+ m_pen.setColor(Qt::white);
+ m_pen.setBrush(Qt::white);
+ m_pen.setJoinStyle(Qt::RoundJoin);
+
+ setCityName("unknown");
+}
+
+void CityInfoDisplay::updateBoundingRect()
+{
+ const qreal right = qMax(m_temperature.boundingRect().right(), -m_textPos.x());
+ const qreal left = qMin(m_temperature.boundingRect().left(), m_textPos.x());
+ m_boundingRect = QRectF(left, 0.0, right - left, m_nameRect.bottom());
+}
+
+void CityInfoDisplay::setTemperature(int lowerBound, int upperBound, int current)
+{
+ prepareGeometryChange();
+ m_temperature.setTemperature(lowerBound, upperBound, current);
+ updateBoundingRect();
+ update();
+}
+
+void CityInfoDisplay::setCityName(const QString &name)
+{
+ prepareGeometryChange();
+ QFontMetrics metrics(m_font);
+ m_text = metrics.elidedText(name, Qt::ElideRight, MAX_CITY_NAME_WIDTH);
+
+ int width = metrics.width(m_text);
+ int right = width >> 1;
+ qreal top = CITY_NAME_TOP;
+ m_textPos = QPointF(-right, top + metrics.height());
+ m_nameRect = QRectF(-right, top, 2 * right, metrics.height() + metrics.descent());
+
+ updateBoundingRect();
+ update();
+}
+
+QRectF CityInfoDisplay::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityInfoDisplay::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->setFont(m_font);
+ painter->drawText(m_textPos, m_text);
+
+ painter->translate(m_temperature.pos());
+ m_temperature.paint(painter);
+}
+
+void CityInfoDisplay::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (m_nameRect.contains(event->pos())) {
+ emit nameClicked();
+ } else
+ event->ignore();
+}
+
+int CityInfoDisplay::loadImages()
+{
+ PixmapLoader::load("minus_sign");
+ PixmapLoader::load("centigrades");
+ PixmapLoader::load("icon_min");
+ PixmapLoader::load("icon_max");
+ PixmapLoader::load("division_line");
+ return 5;
+}
diff --git a/weather/src/cityinfodisplay.h b/weather/src/cityinfodisplay.h
new file mode 100644
index 0000000..6b6dc3b
--- /dev/null
+++ b/weather/src/cityinfodisplay.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CITYINFODISPLAY_H
+#define CITYINFODISPLAY_H
+
+#include <QGraphicsItem>
+#include <QGraphicsTextItem>
+#include <QGraphicsSimpleTextItem>
+#include <QGraphicsPixmapItem>
+#include <QPen>
+#include <QBrush>
+#include <QFont>
+
+class CurrentTemperatureDisplay
+{
+public:
+ CurrentTemperatureDisplay();
+
+ void setTemperature(int value);
+
+ QPointF pos() const { return m_pos; }
+ void setPos(QPointF pos) { m_pos = pos; }
+ void setPos(qreal x, qreal y) { setPos(QPointF(x, y)); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter);
+private:
+ QPen m_pen;
+ QBrush m_brush;
+ QFont m_font;
+ QPixmap m_sign;
+ QPixmap m_unit;
+ QString m_text;
+ int m_value;
+ QRectF m_boundingRect;
+ QPointF m_pos;
+
+ QPointF m_signPos;
+ QPointF m_numberPos;
+ QPointF m_unitPos;
+
+};
+
+class TemperatureBoundDisplay
+{
+public:
+ TemperatureBoundDisplay(bool lowerBound);
+
+ bool lowerBound() const { return m_lowerBound; }
+ int value() const { return m_value; }
+ void setValue(int value);
+
+ QPointF pos() const { return m_pos; }
+ void setPos(QPointF pos) { m_pos = pos; }
+ void setPos(qreal x, qreal y) { setPos(QPointF(x, y)); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter);
+
+private:
+ QPen m_pen;
+ QBrush m_brush;
+ QFont m_font;
+ QPixmap m_icon;
+
+ QString m_text;
+ int m_value;
+ const bool m_lowerBound;
+ QRectF m_boundingRect;
+ QPointF m_pos;
+};
+
+class TemperatureDisplay
+{
+public:
+ TemperatureDisplay();
+
+ void setTemperature(int lowerBound, int upperBound, int current);
+
+ QPointF pos() const { return m_pos; }
+ void setPos(QPointF pos) { m_pos = pos; }
+ void setPos(qreal x, qreal y) { setPos(QPointF(x, y)); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter);
+
+private:
+ QPixmap m_line;
+ TemperatureBoundDisplay m_lowerBound;
+ TemperatureBoundDisplay m_upperBound;
+ CurrentTemperatureDisplay m_current;
+
+ QRectF m_boundingRect;
+ QPointF m_linePos;
+ QPointF m_pos;
+
+};
+
+class CityInfoDisplay : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+public:
+ CityInfoDisplay(QGraphicsItem *parent = 0);
+
+ static int loadImages();
+
+ void setTemperature(int lowerBound, int upperBound, int current);
+ void setCityName(const QString &name);
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+signals:
+ void nameClicked();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+private:
+ QRectF m_boundingRect;
+ TemperatureDisplay m_temperature;
+ QPen m_pen;
+ QBrush m_brush;
+ QFont m_font;
+ QString m_text;
+ QPointF m_textPos;
+ QRectF m_nameRect;
+
+ void updateBoundingRect();
+
+};
+
+#endif // CITYINFODISPLAY_H
diff --git a/weather/src/citylist.cpp b/weather/src/citylist.cpp
new file mode 100644
index 0000000..d3ac7fd
--- /dev/null
+++ b/weather/src/citylist.cpp
@@ -0,0 +1,465 @@
+#include "citylist.h"
+#include "settings.h"
+#include "pixmaploader.h"
+#include <QPainter>
+#include <cmath>
+#include <QParallelAnimationGroup>
+#include <QPropertyAnimation>
+
+#define LIST_TOP_PIXMAP (PixmapLoader::getPic("list_top"))
+
+#define ITEM_BACKGROUND (PixmapLoader::getPic("list_item_bg"))
+#define SELECTED_ITEM_BACKGROUND (PixmapLoader::getPic("list_item_selected_bg"))
+
+#define ITEM_BUTTON_PIXMAP (PixmapLoader::getPic("button_list_delete"))
+#define ITEM_BUTTON_LEFT (Settings::scaleWidth(406.0))
+
+#define ITEM_CHECK_PIXMAP (PixmapLoader::getPic("list_check"))
+#define ITEM_CHECK_LEFT (Settings::scaleWidth(10.0))
+
+#define TEXT_LEFT (2 * ITEM_CHECK_LEFT + ITEM_CHECK_PIXMAP.width())
+
+#define CITY_NAME_FONT_SIZE (Settings::scaleHeight(50.0))
+
+static const int maxVisibleItems = 6;
+static const int minListCount = 3;
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *parent, QGraphicsItem *item)
+{
+ const qreal top = (parent->boundingRect().height() - item->boundingRect().height()) / 2;
+ return top - parent->boundingRect().top() - item->boundingRect().top();
+}
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *item)
+{
+ return getCenterVerticalPos(item->parentItem(), item);
+}
+
+// CityListScrollBox
+
+#ifdef QT_ARCH_ARM
+static const int acceleration_factor = 12;
+#else
+static const int acceleration_factor = 64;
+#endif
+
+
+static const int max_gesture_time_ms = 500;
+static const qreal degrees = 30.0;
+static const qreal limit_angle_tg = tan(degrees * 3.141592 / 180.0);
+static const qreal min_gesture_length = 100.0;
+
+CityListScrollBox::CityListScrollBox(QGraphicsItem *content, QGraphicsItem *parent)
+ : GestureBox(parent)
+ , m_content(content)
+ , m_scroll(new ScrollBar(this))
+{
+ setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
+ if (m_content)
+ m_content->setParentItem(this);
+
+ m_scroll->setPos(Settings::scaleWidth(460.0), Settings::scaleHeight(32.5));
+
+ m_content->setZValue(0.0);
+ m_scroll->setZValue(1.0);
+}
+
+void CityListScrollBox::gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick)
+{
+ Q_UNUSED(pos);
+ startGesture = true;
+ acceptClick = !m_ticker.isActive();
+ if (m_ticker.isActive()) {
+ m_ticker.stop();
+ m_speed = 0.0;
+ }
+}
+
+void CityListScrollBox::gestureStart(QPointF pos)
+{
+ m_startPoint = pos;
+}
+
+void CityListScrollBox::contentPositionUpdated()
+{
+ qreal height = boundingRect().height();
+ qreal content = m_content->boundingRect().height();
+
+ if (height < content) {
+ qreal pos = content - height;
+ pos = -m_content->pos().y() / pos;
+ m_scroll->setValue(pos < 0.0 ? 0.0 : pos);
+ } else
+ if (m_content->pos().y() != 0.0) {
+ m_content->setPos(0.0, 0.0);
+ m_scroll->setValue(0.0);
+ }
+
+}
+
+bool CityListScrollBox::move(QPointF movement)
+{
+ if (m_content && m_content->boundingRect().height() > boundingRect().height()) {
+
+ qreal dist = movement.y();
+
+
+ if (dist > 0 && m_content->pos().y() < 0) {
+ qreal top = m_content->pos().y() + dist;
+ m_content->setPos(m_content->pos().x(), top > 0 ? 0 : top);
+ contentPositionUpdated();
+ return true;
+ }
+
+ if (dist < 0) {
+ qreal top = m_content->pos().y() + dist;
+ if (top + m_content->boundingRect().height() >= boundingRect().height()) {
+ m_content->setPos(m_content->pos().x(), top);
+ contentPositionUpdated();
+ return true;
+ }
+ }
+
+ }
+ return false;
+}
+
+void CityListScrollBox::gestureMove(QPointF pos, QPointF movement, QPointF speed)
+{
+ Q_UNUSED(pos);
+ Q_UNUSED(speed);
+ move(movement);
+}
+
+void CityListScrollBox::gestureEnd(QPointF pos, QPointF speed)
+{
+ qreal x = pos.x() - m_startPoint.x();
+ qreal y = pos.y() - m_startPoint.y();
+ if (fabs(x / y) > limit_angle_tg || fabs(y) < min_gesture_length)
+ return;
+
+ qreal topSpeed = Settings::scaleHeight(1500.0);
+ m_acceleration = topSpeed / acceleration_factor;
+ m_speed = qBound(-topSpeed, speed.y(), topSpeed);
+ if (m_speed != 0) {
+ m_time = QTime::currentTime();
+ m_ticker.start(20, this);
+ }
+}
+
+void CityListScrollBox::timerEvent(QTimerEvent *event)
+{
+ bool stopTimer = true;
+ if (m_speed != 0.0) {
+ QTime now(QTime::currentTime());
+ qreal movement = m_speed * (qreal)m_time.msecsTo(now) / 1000.0;
+ m_time = now;
+ stopTimer = !move(QPointF(0, movement));
+ if (!stopTimer) {
+ m_speed = m_speed > 0.0 ? qMax(qreal(0.0), m_speed - m_acceleration)
+ : qMin(qreal(0.0), m_speed + m_acceleration);
+ stopTimer = m_speed == 0.0;
+ }
+ }
+ if (stopTimer)
+ m_ticker.stop();
+ QObject::timerEvent(event);
+}
+
+// CityListText
+
+CityListText::CityListText(const QString &text, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_painter(CITY_NAME_FONT_SIZE, Qt::white, text)
+{
+ setCacheMode(ItemCoordinateCache);
+ m_painter.setPos(0.0, 0.0);
+ m_painter.setMaxWidth(ITEM_BUTTON_LEFT - TEXT_LEFT);
+}
+
+QRectF CityListText::boundingRect() const
+{
+ return QRectF(0.0, 0.0, m_painter.width(), m_painter.height());
+}
+
+void CityListText::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ m_painter.paint(painter);
+}
+
+// CityListItem
+
+CityListItem::CityListItem(const ForecastData &data, CityContentList *list, bool deleteVisible)
+ : ContentListItem(0)
+ , m_height(ITEM_BACKGROUND.height())
+ , m_data(data)
+ , m_list(list)
+ , m_background(new QGraphicsPixmapItem(ITEM_BACKGROUND, this))
+ , m_check(new QGraphicsPixmapItem(ITEM_CHECK_PIXMAP, m_background))
+ , m_delete(new PixmapButton(80.0, ITEM_BUTTON_PIXMAP, m_background))
+ , m_text(new CityListText(m_data.cityName(), this))
+{
+ m_delete->setPos(ITEM_BUTTON_LEFT, getCenterVerticalPos(m_delete));
+ m_delete->setVisible(deleteVisible);
+ m_delete->setOpacity(deleteVisible ? 1.0 : 0.0);
+ m_check->setPos(ITEM_CHECK_LEFT, getCenterVerticalPos(m_check));
+ m_check->hide();
+ m_text->setPos(TEXT_LEFT, getCenterVerticalPos(m_text));
+ m_text->setCacheMode(ItemCoordinateCache);
+
+ connect(m_delete, SIGNAL(clicked()), this, SLOT(removeFromList()));
+}
+
+void CityListItem::select(bool selected)
+{
+ m_background->setPixmap(selected ? SELECTED_ITEM_BACKGROUND : ITEM_BACKGROUND);
+ m_check->setVisible(selected);
+}
+
+void CityListItem::removeFromList()
+{
+ if (!m_list->busy())
+ m_list->removeItem(this);
+}
+
+qreal CityListItem::contentHeight() const
+{
+ return m_height;
+}
+
+QAbstractAnimation *CityListItem::getButtonAnimation(bool hide)
+{
+ return getFadeAnimation(m_delete, hide, 150);
+}
+
+QAbstractAnimation *CityListItem::getShowAnimation()
+{
+ return getFadeAnimation(this, false, 150);
+}
+
+QAbstractAnimation *CityListItem::getHideAnimation()
+{
+ return getFadeAnimation(this, true, 150);
+}
+
+template<class T>QAbstractAnimation *CityListItem::getFadeAnimation(T *target, bool hide, int msecs)
+{
+ target->setOpacity(hide ? 1.0 : 0.0);
+ target->show();
+ QPropertyAnimation* lResult = new QPropertyAnimation(target, "opacity");
+ lResult->setEasingCurve(QEasingCurve::OutExpo);
+ lResult->setStartValue(hide ? 1.0 : 0.0);
+ lResult->setEndValue(hide ? 0.0 : 1.0);
+ lResult->setDuration(msecs);
+ if (hide)
+ connect(lResult, SIGNAL(finished()), target, SLOT(doHide()));
+ return lResult;
+}
+
+// CityContentList
+
+CityContentList::CityContentList(QList<ForecastData> &contentList, QObject *holder)
+ : ContentList(0)
+ , m_holder(holder)
+ , m_contentList(contentList)
+{
+ QList<ContentListItem*> items;
+ foreach (const ForecastData &data, m_contentList)
+ items.append(new CityListItem(data, this, m_contentList.count() > minListCount));
+ appendItems(items);
+}
+
+void CityContentList::addForecast(const ForecastData &data)
+{
+ m_contentList.append(data);
+ addItem(new CityListItem(data, this, m_contentList.count() > minListCount));
+}
+
+void CityContentList::select(const QString &selected)
+{
+ for (int i = 0; i < itemCount(); ++i) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ item->select(item->data().key() == selected);
+ }
+}
+
+QAbstractAnimation *CityContentList::getInsertAnimation(int idx, qreal height)
+{
+ if (idx < 0 || idx > itemCount())
+ return 0;
+
+ QList<QAbstractAnimation*> list;
+
+ if (m_holder && itemCount() < maxVisibleItems)
+ list.append(getMoveAnimation(m_holder, -height));
+
+ if (idx < itemCount()) {
+ for (int i = idx; i < itemCount(); ++i)
+ list.append(getItemMoveAnimation(i, height));
+ }
+
+ if (parentItem() && itemCount() >= maxVisibleItems) {
+ qreal top = parentItem()->boundingRect().height() - boundingRect().height() - height;
+ list.append(getMoveAnimation(this, top - pos().y()));
+
+ }
+
+ if (itemCount() == minListCount) {
+ for (int i = 0; i < minListCount; ++i) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ list.append(item->getButtonAnimation(false));
+ }
+ }
+
+ return createCompoundAnimation(list);
+}
+
+QAbstractAnimation *CityContentList::getRemoveAnimation(int idx)
+{
+ if (idx < 0 || idx >= itemCount())
+ return 0;
+
+ qreal offset = -getItem(idx)->contentHeight();
+ QList<QAbstractAnimation*> list;
+
+ if (m_holder && itemCount() <= maxVisibleItems)
+ list.append(getMoveAnimation(m_holder, -offset));
+
+ for (int i = idx + 1; i < itemCount(); ++i)
+ list.append(getItemMoveAnimation(i, offset));
+
+ qreal newHeight = boundingRect().height() - getItem(idx)->contentHeight();
+
+ if (parentItem()) {
+ if (newHeight < parentItem()->boundingRect().height()) {
+ if (pos().y() != 0)
+ list.append(getMoveAnimation(this, -pos().y()));
+ } else {
+ if (pos().y() + newHeight < parentItem()->boundingRect().bottom()) {
+ qreal offset = parentItem()->boundingRect().bottom() - pos().y() - newHeight;
+ list.append(getMoveAnimation(this, offset));
+ }
+ }
+ }
+
+ if (itemCount() == minListCount + 1) {
+ for (int i = 0; i <= minListCount; ++i) {
+ if (i != idx) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ list.append(item->getButtonAnimation(true));
+
+ }
+ }
+ }
+
+ m_contentList.removeAt(idx);
+
+
+ return createCompoundAnimation(list);
+}
+
+QAbstractAnimation *CityContentList::createCompoundAnimation(QList<QAbstractAnimation*> list)
+{
+ if (list.count() == 0)
+ return 0;
+ if (list.count() == 1)
+ return list[0];
+ QParallelAnimationGroup *result = new QParallelAnimationGroup();
+ for (int i = 0; i < list.count(); ++i)
+ result->addAnimation(list[i]);
+ return result;
+
+}
+
+QAbstractAnimation *CityContentList::getMoveAnimation(QObject *object, qreal offset)
+{
+ if (!object)
+ return 0;
+ qreal itemTop = object->property(ITEM_TOP_PROPERTY_NAME).toReal();
+ QPropertyAnimation* lResult = new QPropertyAnimation(object, ITEM_TOP_PROPERTY_NAME);
+
+ lResult->setEasingCurve(QEasingCurve::OutExpo);
+ lResult->setStartValue(itemTop);
+ lResult->setEndValue(itemTop + offset);
+ lResult->setDuration(150);
+ return lResult;
+}
+
+QAbstractAnimation *CityContentList::getItemMoveAnimation(int idx, qreal offset)
+{
+ return getMoveAnimation(getItem(idx), offset);
+}
+
+// CityList
+
+CityList::CityList(QList<ForecastData> &contentList, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_itemHeight(ITEM_BACKGROUND.height())
+ , m_topHeight(LIST_TOP_PIXMAP.height())
+ , m_boundingRect(getBoundingRect())
+ , m_paintRect(getPaintRect())
+ , m_top(new QGraphicsPixmapItem(LIST_TOP_PIXMAP, this))
+ , m_list(new CityContentList(contentList, this))
+ , m_scrollBox(new CityListScrollBox(m_list, this))
+{
+ m_top->setPos(-m_top->boundingRect().left(), -m_top->boundingRect().top());
+ m_scrollBox->setRect(m_paintRect);
+ m_list->setWidth(m_paintRect.width());
+}
+
+qreal CityList::initialTop() const
+{
+ int count = maxVisibleItems - m_list->itemCount();
+ if (count <= 0)
+ return 0;
+ return count * m_itemHeight;
+}
+
+QRectF CityList::getBoundingRect()
+{
+ QRectF result(QPointF(0.0, 0.0), Settings::windowSize());
+ result.setHeight(m_topHeight + maxVisibleItems * m_itemHeight);
+ result.moveTo(0.0, 0.0);
+ return result;
+}
+
+QRectF CityList::getPaintRect()
+{
+ QRectF result(QPointF(0.0, 0.0), Settings::windowSize());
+ result.setHeight(maxVisibleItems * m_itemHeight);
+ result.moveTo(0.0, m_topHeight);
+ return result;
+}
+
+int CityList::loadImages()
+{
+ PixmapLoader::load("list_top");
+ PixmapLoader::load("list_item_bg");
+ PixmapLoader::load("list_item_selected_bg");
+ PixmapLoader::load("button_list_delete");
+ PixmapLoader::load("list_check");
+ return 5;
+}
+
+void CityList::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+QRectF CityList::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityList::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ painter->fillRect(m_paintRect, QColor(7, 18, 23, 255));
+}
diff --git a/weather/src/citylist.h b/weather/src/citylist.h
new file mode 100644
index 0000000..3d93806
--- /dev/null
+++ b/weather/src/citylist.h
@@ -0,0 +1,169 @@
+#ifndef CITYLIST_H
+#define CITYLIST_H
+
+#include <QBasicTimer>
+#include <QTime>
+#include <QGraphicsItem>
+
+#include "gesturebox.h"
+#include "contentlist.h"
+#include "forecastdata.h"
+#include "pixmapbutton.h"
+#include "scrollbar.h"
+#include "painttextitem.h"
+
+// CityListScrollBox
+
+class CityListScrollBox : public QObject, public GestureBox
+{
+ Q_OBJECT
+public:
+ CityListScrollBox(QGraphicsItem *content, QGraphicsItem *parent = 0);
+
+signals:
+ void updated(qreal, qreal, qreal);
+
+protected:
+ void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick);
+ void gestureStart(QPointF pos);
+ void gestureMove(QPointF pos, QPointF movement, QPointF speed);
+ void gestureEnd(QPointF pos, QPointF speed);
+
+ void timerEvent(QTimerEvent *event);
+
+private:
+ QGraphicsItem * const m_content;
+ QBasicTimer m_ticker;
+ qreal m_speed;
+ qreal m_acceleration;
+ QTime m_time;
+ QPointF m_startPoint;
+ ScrollBar *m_scroll;
+ bool move(QPointF movement);
+ void contentPositionUpdated();
+};
+
+// CityListText
+
+class CityListText : public QGraphicsItem
+{
+public:
+ CityListText(const QString &text, QGraphicsItem *parent = 0);
+ QRectF boundingRect() const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+private:
+ TextPainter m_painter;
+
+};
+
+// CityListItem
+
+class CityContentList;
+
+class CityListItem : public ContentListItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity);
+public:
+ CityListItem(const ForecastData &data, CityContentList *list, bool deleteVisible);
+ void select(bool selected);
+
+ const ForecastData &data() const { return m_data; }
+
+ qreal contentHeight() const;
+ QAbstractAnimation *getShowAnimation();
+ QAbstractAnimation *getHideAnimation();
+
+ QAbstractAnimation *getButtonAnimation(bool hide);
+
+private slots:
+ void removeFromList();
+
+private:
+ const qreal m_height;
+ const ForecastData m_data;
+ CityContentList * const m_list;
+
+ QGraphicsPixmapItem * const m_background;
+ QGraphicsPixmapItem * const m_check;
+ PixmapButton * const m_delete;
+ CityListText * const m_text;
+
+ template<class T> static QAbstractAnimation *getFadeAnimation(T *target, bool hide, int msecs);
+
+private slots:
+ void doHide() { hide(); }
+
+};
+
+// CityContentList
+
+class CityContentList : public ContentList
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal top READ getTop WRITE setTop);
+public:
+ CityContentList(QList<ForecastData> &contentList, QObject *holder);
+ void addForecast(const ForecastData &data);
+ void select(const QString &selected);
+
+protected:
+ QAbstractAnimation *getInsertAnimation(int idx, qreal height);
+ QAbstractAnimation *getRemoveAnimation(int idx);
+
+private:
+ QObject * const m_holder;
+ QList<ForecastData> &m_contentList;
+
+ QAbstractAnimation *createCompoundAnimation(QList<QAbstractAnimation*> list);
+ QAbstractAnimation *getItemMoveAnimation(int idx, qreal offset);
+ QAbstractAnimation *getMoveAnimation(QObject *object, qreal offset);
+
+ qreal getTop() { return pos().y(); }
+ void setTop(qreal top) { setPos(pos().x(), top); }
+
+};
+
+// CityList
+
+class CityList : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal top READ getTop WRITE setTop);
+ Q_INTERFACES(QGraphicsItem);
+public:
+ CityList(QList<ForecastData> &contentList, QGraphicsItem *parent = 0);
+ void select(const QString &selected) { m_list->select(selected); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ static int loadImages();
+
+ qreal initialTop() const;
+
+public slots:
+ void addForecast(const ForecastData &data) { m_list->addForecast(data); }
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+private:
+ const qreal m_itemHeight;
+ const qreal m_topHeight;
+ const QRectF m_boundingRect;
+ const QRectF m_paintRect;
+
+ inline QRectF getBoundingRect();
+ inline QRectF getPaintRect();
+
+ QGraphicsPixmapItem * const m_top;
+ CityContentList * const m_list;
+ CityListScrollBox * const m_scrollBox;
+
+ qreal getTop() { return pos().y(); }
+ void setTop(qreal top) { setPos(pos().x(), top); }
+};
+
+#endif // CITYLIST_H
diff --git a/weather/src/citymanager.cpp b/weather/src/citymanager.cpp
new file mode 100644
index 0000000..665c3b7
--- /dev/null
+++ b/weather/src/citymanager.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "citymanager.h"
+#include "settings.h"
+
+#include <QGraphicsSceneMouseEvent>
+#include <QPropertyAnimation>
+
+static inline qreal getBottomVerticalPos(qreal &bottom, QGraphicsItem *item)
+{
+ const qreal result = bottom - item->boundingRect().bottom();
+ bottom -= item->boundingRect().height();
+ return result;
+}
+
+// CityManagerContent
+
+CityManagerContent::CityManagerContent(QList<ForecastData> &contentList, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(QPointF(0.0, 0.0), Settings::windowSize())
+{
+ setFlag(ItemHasNoContents);
+
+ qreal bottom = m_boundingRect.bottom();
+ m_addTool = new AddCityTool(contentList, this);
+ m_addTool->setPos(0.0, getBottomVerticalPos(bottom, m_addTool));
+ m_addTool->setZValue(10.0);
+
+ m_list = new CityList(contentList, this);
+ m_list->setPos(0.0, getBottomVerticalPos(bottom, m_list) + m_list->initialTop() + 1);
+ m_list->setZValue(0.0);
+
+ connect(m_addTool, SIGNAL(newForecast(ForecastData)),
+ this, SLOT(forecastSelected(ForecastData)));
+
+}
+
+void CityManagerContent::forecastSelected(ForecastData data)
+{
+ m_list->addForecast(data);
+}
+
+QRectF CityManagerContent::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityManagerContent::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+qreal CityManagerContent::getHiddenTop()
+{
+ return m_boundingRect.height() - m_list->pos().y() - 5.0;
+}
+
+// CityManager
+
+CityManager::CityManager(QList<ForecastData> contentList, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(QPointF(0.0, 0.0), Settings::windowSize())
+ , m_contentList(contentList)
+ , m_visible(false)
+{
+ m_content = new CityManagerContent(m_contentList, this);
+ m_content->setPos(0.0, m_content->getHiddenTop());
+}
+
+QRectF CityManager::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityManager::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+void CityManager::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->accept();
+ startAnimation(false);
+}
+
+void CityManager::showManager(const QString &selected)
+{
+ m_content->select(selected);
+ startAnimation(true);
+}
+
+void CityManager::startAnimation(bool show)
+{
+ if (show == m_visible)
+ return;
+
+ m_visible = show;
+
+ if (m_animation)
+ m_animation->stop();
+
+ QPropertyAnimation* animation = new QPropertyAnimation(m_content, "top");
+
+ animation->setEasingCurve(QEasingCurve::OutExpo);
+ animation->setEndValue(show ? 0.0 : m_content->getHiddenTop());
+ animation->setDuration(500);
+ m_animation = animation;
+ if (!show)
+ connect(animation, SIGNAL(finished()), this, SIGNAL(terminated()));
+ m_animation->start(QAbstractAnimation::DeleteWhenStopped);
+
+}
diff --git a/weather/src/citymanager.h b/weather/src/citymanager.h
new file mode 100644
index 0000000..3b80393
--- /dev/null
+++ b/weather/src/citymanager.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CITYMANAGER_H
+#define CITYMANAGER_H
+
+#include <QGraphicsItem>
+#include <QGraphicsPixmapItem>
+
+#include "addcitytool.h"
+#include "citylist.h"
+
+class CityManagerContent : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(qreal top READ getTop WRITE setTop);
+public:
+ CityManagerContent(QList<ForecastData> &contentList, QGraphicsItem *parent = 0);
+ void select(const QString &selected) { m_list->select(selected); }
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ qreal getHiddenTop();
+
+private:
+ const QRectF m_boundingRect;
+ AddCityTool *m_addTool;
+ CityList *m_list;
+
+ qreal getTop() { return pos().y(); }
+ void setTop(qreal top) { setPos(pos().x(), top); }
+
+private slots:
+ void forecastSelected(ForecastData data);
+
+};
+
+class CityManager : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+public:
+ CityManager(QList<ForecastData> contentList, QGraphicsItem *parent = 0);
+ void showManager(const QString &selected);
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ QList<ForecastData> forecastList() const { return m_contentList; }
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+signals:
+ void terminated();
+
+private:
+ const QRectF m_boundingRect;
+ CityManagerContent *m_content;
+ QList<ForecastData> m_contentList;
+ QPointer<QAbstractAnimation> m_animation;
+ bool m_visible;
+
+ void startAnimation(bool show);
+};
+
+#endif // CITYMANAGER_H
diff --git a/weather/src/contentlist.cpp b/weather/src/contentlist.cpp
new file mode 100644
index 0000000..8b054aa
--- /dev/null
+++ b/weather/src/contentlist.cpp
@@ -0,0 +1,441 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "contentlist.h"
+#include <QPointer>
+
+// ContentListItem
+
+ContentListItem::ContentListItem(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_geometry(QRectF(0.0, 0.0, 0.0, 0.0))
+ , m_geometryReady(false)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+}
+
+QRectF ContentListItem::boundingRect() const
+{
+ if (!m_geometryReady)
+ return QRectF(0.0, 0.0, 0.0, contentHeight());
+ return m_geometry;
+}
+
+void ContentListItem::updateGeometry()
+{
+ m_geometryReady = parentItem() != 0;
+ qreal width = parentItem() ? parentItem()->boundingRect().width() - pos().x() : 0.0;
+ QRectF geometry(0.0, 0.0, width, contentHeight());
+ if (m_geometry != geometry) {
+ prepareGeometryChange();
+ m_geometry = geometry;
+ update();
+ }
+}
+
+QVariant ContentListItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ Q_UNUSED(value);
+ switch (change) {
+ case QGraphicsItem::ItemParentHasChanged:
+ case QGraphicsItem::ItemPositionHasChanged:
+ updateGeometry();
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+void ContentListItem::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+}
+
+// ContentListActivity
+
+void ContentListActivity::addActivity(ContentListActivity *activity)
+{
+ int idx = m_list.m_queue.indexOf(this) + 1;
+ if (idx > 0)
+ m_list.m_queue.insert(idx + m_insertPos++, activity);
+}
+
+bool ContentListActivity::active()
+{
+ return false;
+}
+
+// SignalActivity
+
+
+bool SignalActivity::run()
+{
+ emit notify();
+ return false;
+}
+
+// SortActivity
+
+bool SortActivity::run()
+{
+ qSort(m_list.m_items.begin(), m_list.m_items.end(), m_compare);
+ m_list.updateItems();
+ return false;
+}
+
+// AppendItemsActivity
+
+bool AppendItemsActivity::run()
+{
+ m_list.prepareGeometryChange();
+ m_list.doAppendItems(m_items, true);
+ m_list.update();
+ return false;
+}
+
+// AnimationActivity
+
+AnimationActivity::AnimationActivity(QAbstractAnimation *animation, ContentList &list)
+ : ContentListActivity(list)
+ , m_animation(animation)
+{
+}
+
+AnimationActivity::~AnimationActivity()
+{
+ if (m_animation && m_animation->state() == QAbstractAnimation::Stopped)
+ m_animation->deleteLater();
+}
+
+
+bool AnimationActivity::run()
+{
+ if (!m_animation || m_animation->state() != QAbstractAnimation::Stopped)
+ return false;
+
+ connect(m_animation, SIGNAL(finished()), this, SLOT(animationEnd()));
+ m_animation->start(QAbstractAnimation::DeleteWhenStopped);
+ return true;
+}
+
+void AnimationActivity::animationEnd()
+{
+ m_animation = 0;
+ activityEnd();
+}
+
+// RemoveActivity
+
+RemoveActivity::RemoveActivity(int idx, bool destroyItem, bool notify, ContentList &list)
+ : ContentListActivity(list)
+ , m_idx(idx)
+ , m_destroyItem(destroyItem)
+ , m_item(0)
+ , m_active(false)
+ , m_notify(notify)
+{
+}
+
+bool RemoveActivity::run()
+{
+ if (m_active)
+ return false;
+
+ m_item = m_list.getItem(m_idx);
+ if (!m_item)
+ return false;
+
+ QAbstractAnimation *animation = m_item->getHideAnimation();
+ if (animation) {
+ connect(animation, SIGNAL(finished()), this, SLOT(hideEnd()));
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_active = true;
+ } else
+ hideEnd();
+
+ return m_active;
+}
+
+void RemoveActivity::hideEnd()
+{
+ m_item->hide();
+ QAbstractAnimation *animation = m_list.getRemoveAnimation(m_idx);
+ m_list.doRemoveItem(m_idx, m_notify);
+ if (m_destroyItem)
+ m_item->deleteLater();
+ m_active = animation != 0;
+ if (animation) {
+ connect(animation, SIGNAL(finished()), this, SLOT(activityEnd()));
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else
+ activityEnd();
+}
+
+// InsertActivity
+
+InsertActivity::InsertActivity(int idx, ContentListItem* item, bool notify, ContentList &list)
+ : ContentListActivity(list)
+ , m_idx(idx)
+ , m_item(item)
+ , m_active(false)
+ , m_notify(notify)
+{
+}
+
+bool InsertActivity::run()
+{
+ if (m_active || !m_item)
+ return false;
+
+ m_idx = m_idx < 0 ? 0 : m_idx > m_list.itemCount() ? m_list.itemCount() : m_idx;
+
+
+ QAbstractAnimation *animation = m_list.getInsertAnimation(m_idx, m_item->contentHeight());
+ if (animation) {
+ connect(animation, SIGNAL(finished()), this, SLOT(showItem()));
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_active = true;
+ } else
+ showItem();
+
+ return m_active;
+}
+
+void InsertActivity::showItem()
+{
+ m_list.doInsertItem(m_idx, m_item, m_notify);
+ m_item->show();
+ QAbstractAnimation *animation = m_item->getShowAnimation();
+ m_active = animation != 0;
+ if (animation) {
+ connect(animation, SIGNAL(finished()), this, SLOT(activityEnd()));
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else
+ activityEnd();
+}
+
+// MoveActivity
+
+MoveActivity::MoveActivity(int from, int to, ContentList &list)
+ : ContentListActivity(list)
+ , m_from(from)
+ , m_to(to)
+{
+}
+
+bool MoveActivity::run()
+{
+ if (m_from < 0 || m_from >= m_list.itemCount())
+ return false;
+
+ m_to = m_to < 0 ? 0 : m_to > m_list.itemCount() ? m_list.itemCount() : m_to;
+
+ if (m_from == m_to || (m_from == m_list.itemCount() - 1 && m_to > m_from))
+ return false;
+
+ addActivity(new RemoveActivity(m_from, false, false, m_list));
+ addActivity(new InsertActivity(m_to, m_list.getItem(m_from), false, m_list));
+
+ return false;
+}
+
+// ContentList
+
+ContentList::ContentList(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(0.0, 0.0, 0.0, 0.0)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+}
+
+ContentList::ContentList(QList<ContentListItem*> items, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(0.0, 0.0, 0.0, 0.0)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+ doAppendItems(items, false);
+}
+
+void ContentList::doAppendItems(QList<ContentListItem*> items, bool notify)
+{
+ qreal top = 0;
+ foreach(ContentListItem *item, m_items)
+ top += item->contentHeight();
+ foreach(ContentListItem *item, items) {
+ item->setParentItem(this);
+ item->setPos(0, top);
+ top += item->contentHeight();
+ m_items.append(item);
+ m_boundingRect.setHeight(m_boundingRect.height() + item->contentHeight());
+ if (notify)
+ emit newContentItem(item);
+ }
+}
+
+ContentList::~ContentList()
+{
+ foreach(ContentListActivity *activity, m_queue)
+ activity->deleteLater();
+}
+
+qreal ContentList::width() const
+{
+ return m_boundingRect.width();
+}
+
+void ContentList::setWidth(qreal width)
+{
+ if (m_boundingRect.width() != width) {
+ prepareGeometryChange();
+ m_boundingRect.setWidth(width);
+ update();
+ foreach(ContentListItem *item, m_items)
+ item->updateGeometry();
+ }
+}
+
+QRectF ContentList::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void ContentList::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+}
+
+bool ContentList::insertItem(int idx, ContentListItem* item)
+{
+ if (getItemIndex(item) != -1)
+ return false;
+ addActivity(new InsertActivity(idx, item, true, *this));
+ return true;
+}
+
+bool ContentList::removeItem(int idx)
+{
+ if (idx < 0 || idx >= m_items.count())
+ return false;
+ addActivity(new RemoveActivity(idx, true, true, *this));
+ return true;
+}
+
+bool ContentList::moveItem(int from, int to)
+{
+ if (from < 0 || from >= m_items.count())
+ return false;
+
+ to = to < 0 ? 0 : to > m_items.count() ? m_items.count() : to;
+
+ if (from == to)
+ return true;
+ addActivity(new MoveActivity(from, to, *this));
+ return true;
+}
+
+void ContentList::addActivity(ContentListActivity *activity)
+{
+ m_queue.append(activity);
+ checkQueue();
+}
+
+void ContentList::checkQueue()
+{
+ while (m_queue.count() > 0 && !m_queue[0]->active())
+ if (!m_queue[0]->run())
+ m_queue.takeFirst()->deleteLater();
+}
+
+void ContentList::activityEnd()
+{
+ m_queue.takeFirst()->deleteLater();
+ checkQueue();
+}
+
+void ContentList::doRemoveItem(int idx, bool notify)
+{
+ if (idx >= 0 && idx < m_items.count()) {
+ ContentListItem *item = m_items[idx];
+ item->setParentItem(0);
+ m_items.removeAt(idx);
+ if (notify) {
+ prepareGeometryChange();
+ m_boundingRect.setHeight(m_boundingRect.height() - item->contentHeight());
+ update();
+ emit contentItemRemoved(item);
+ }
+ }
+}
+
+void ContentList::doInsertItem(int idx, ContentListItem * item, bool notify)
+{
+ if (idx >= 0 && idx <= m_items.count()) {
+ qreal top = 0;
+ for (int i = 0; i < idx; ++i)
+ top += m_items[i]->contentHeight();
+ item->setPos(0, top);
+ item->setParentItem(this);
+ if (notify) {
+ prepareGeometryChange();
+ m_boundingRect.setHeight(m_boundingRect.height() + item->contentHeight());
+ update();
+ emit newContentItem(item);
+ }
+ m_items.insert(idx, item);
+ }
+}
+
+void ContentList::updateItems()
+{
+ qreal top = 0;
+ foreach(ContentListItem *item, m_items) {
+ item->setPos(0, top);
+ top += item->contentHeight();
+ }
+ update();
+}
+
+void ContentList::sortItems(ContentListItemCompare compare)
+{
+ addActivity(new SortActivity(*this, compare));
+}
+
+void ContentList::appendItems(QList<ContentListItem*> items)
+{
+ addActivity(new AppendItemsActivity(*this, items));
+}
diff --git a/weather/src/contentlist.h b/weather/src/contentlist.h
new file mode 100644
index 0000000..8f37c7f
--- /dev/null
+++ b/weather/src/contentlist.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CONTENTLIST_H
+#define CONTENTLIST_H
+
+#include <QList>
+#include <QObject>
+#include <QGraphicsObject>
+#include <QAbstractAnimation>
+#include <QPointer>
+
+#define ITEM_TOP_PROPERTY_NAME "top"
+
+class ContentListItem : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal top READ getTop WRITE setTop);
+ Q_INTERFACES(QGraphicsItem);
+public:
+ ContentListItem(QGraphicsItem *parent = 0);
+ virtual qreal contentHeight() const = 0;
+ QRectF boundingRect() const;
+
+ virtual QAbstractAnimation *getShowAnimation() = 0;
+ virtual QAbstractAnimation *getHideAnimation() = 0;
+
+protected:
+ void updateGeometry();
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+private:
+ friend class ContentList;
+ QRectF m_geometry;
+ bool m_geometryReady;
+ qreal getTop() { return pos().y(); }
+ void setTop(qreal top) { setPos(pos().x(), top); }
+
+};
+
+class ContentListActivity;
+class RemoveActivity;
+class InsertActivity;
+
+class ContentList : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem);
+public:
+ ContentList(QGraphicsItem *parent = 0);
+ ContentList(QList<ContentListItem*> items, QGraphicsItem *parent = 0);
+ ~ContentList();
+
+ bool insertItem(int idx, ContentListItem*);
+ bool addItem(ContentListItem *item) { return insertItem(m_items.count(), item); }
+ bool removeItem(int idx);
+ bool removeItem(ContentListItem* item) { return removeItem(getItemIndex(item)); }
+ bool moveItem(int from, int to);
+ bool moveItem(ContentListItem* item, int to) { return moveItem(getItemIndex(item), to); }
+ void appendItems(QList<ContentListItem*> items);
+
+ int itemCount() const { return m_items.count(); }
+ ContentListItem* getItem(int idx) { return m_items.at(idx); }
+ int getItemIndex(ContentListItem* item) { return m_items.indexOf(item); }
+
+ bool busy() { return m_queue.count() > 0; }
+ void addActivity(ContentListActivity *);
+
+ void updateItems();
+
+ QRectF boundingRect() const;
+ qreal width() const;
+ void setWidth(qreal width);
+
+signals:
+ void newContentItem(QGraphicsItem *item);
+ void contentItemRemoved(QGraphicsItem *item);
+
+protected:
+ virtual QAbstractAnimation *getInsertAnimation(int idx, qreal height) = 0;
+ virtual QAbstractAnimation *getRemoveAnimation(int idx) = 0;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ QList<ContentListItem*> getItems() { return m_items; }
+
+ typedef bool (*ContentListItemCompare)(const ContentListItem* s1, const ContentListItem* s2);
+ void sortItems(ContentListItemCompare compare);
+
+private:
+ friend class ContentListActivity;
+ friend class RemoveActivity;
+ friend class InsertActivity;
+ friend class SortActivity;
+ friend class AppendItemsActivity;
+ QList<ContentListItem*> m_items;
+ QList<ContentListActivity*> m_queue;
+
+ QRectF m_boundingRect;
+
+ void updateGeometry();
+
+ void checkQueue();
+ void activityEnd();
+ void doRemoveItem(int idx, bool notify);
+ void doInsertItem(int idx, ContentListItem* item, bool notify);
+ void doAppendItems(QList<ContentListItem*> items, bool notify);
+
+};
+
+class ContentListActivity : public QObject
+{
+ Q_OBJECT
+public:
+ ContentListActivity(ContentList &list) : m_list(list), m_insertPos(0) {}
+ virtual bool run() = 0;
+ virtual bool active();
+
+protected:
+ ContentList &m_list;
+ void addActivity(ContentListActivity*);
+
+protected slots:
+ void activityEnd() { m_list.activityEnd(); }
+
+private:
+ int m_insertPos;
+
+};
+
+class SignalActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ SignalActivity(ContentList &list) : ContentListActivity(list) {}
+ bool run();
+
+signals:
+ void notify();
+};
+
+class SortActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ SortActivity(ContentList &list, ContentList::ContentListItemCompare compare)
+ : ContentListActivity(list), m_compare(compare) {}
+ bool run();
+
+private:
+ ContentList::ContentListItemCompare m_compare;
+};
+
+class AppendItemsActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ AppendItemsActivity(ContentList &list, QList<ContentListItem*> items)
+ : ContentListActivity(list), m_items(items) {}
+ bool run();
+
+private:
+ QList<ContentListItem*> m_items;
+};
+
+class AnimationActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ AnimationActivity(QAbstractAnimation *animation, ContentList &list);
+ ~AnimationActivity();
+ bool run();
+ bool active() { return m_animation && m_animation->state() != QAbstractAnimation::Stopped; }
+
+private slots:
+ void animationEnd();
+
+private:
+ QAbstractAnimation *m_animation;
+
+};
+
+class RemoveActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ RemoveActivity(int idx, bool destroyItem, bool notify, ContentList &list);
+ bool run();
+ bool active() { return m_active; }
+
+private slots:
+ void hideEnd();
+
+private:
+ int m_idx;
+ bool m_destroyItem;
+ ContentListItem *m_item;
+ bool m_active;
+ const bool m_notify;
+
+};
+
+class InsertActivity : public ContentListActivity
+{
+ Q_OBJECT
+public:
+ InsertActivity(int idx, ContentListItem* item, bool notify, ContentList &list);
+ bool run();
+ bool active() { return m_active; }
+
+private slots:
+ void showItem();
+
+private:
+ int m_idx;
+ QPointer<ContentListItem> m_item;
+ bool m_active;
+ const bool m_notify;
+
+};
+
+class MoveActivity : public ContentListActivity
+{
+public:
+ MoveActivity(int from, int to, ContentList &list);
+ bool run();
+
+private:
+ int m_from;
+ int m_to;
+
+};
+
+#endif // CONTENTLIST_H
diff --git a/weather/src/demoforecastsource.cpp b/weather/src/demoforecastsource.cpp
new file mode 100644
index 0000000..292b6ea
--- /dev/null
+++ b/weather/src/demoforecastsource.cpp
@@ -0,0 +1,142 @@
+#include "demoforecastsource.h"
+#include "forecast.h"
+
+#include <QTimer>
+#include <QFile>
+#include <QDebug>
+
+struct CityInfo
+{
+ const char * name;
+ const int lower;
+ const int temp;
+ const int upper;
+ const bool night;
+};
+
+static const CityInfo cityNames[Forecast::UnknownForecast] = {
+ {"Araxa", -7, -4, -1, true}, // MostlyCloudy
+ {"Belo Horizonte", 18, 22, 27, false}, // Cloudy
+ {"Recife", 25, 31, 34, true}, // MostlySunny
+ {"Ipojuca", 22, 29, 36, false}, // PartlyCloudy
+ {"Fortaleza", 31, 34, 36, true}, // Sunny
+ {"Sao Paulo", -1, 10, 12, false}, // Flurries
+ {"Uberlandia", -26, -23, -20, false}, // Fog
+ {"Governador Valadares", 3, 17, 18, true}, // Haze
+ {"Brasilia", 22, 25, 30, false}, // Sand
+ {"Florianopolis", 0, 8, 10, true}, // Dust
+ {"Gravata", -10, -2, 3, false}, // Icy
+ {"Maceio", 22, 35, 38, true}, // Sleet
+ {"Porto Alegre", -8, -6, -4, true}, // ChanceOfSleet
+ {"Batatais", -20, -12, -10, false}, // Snow
+ {"Ribeirao Preto", -20, -17, 0, true}, // ChanceOfSnow
+ {"Campinas", 1, 4, 10, false}, // Mist
+ {"Campo belo", 12, 19, 21, true}, // Rain
+ {"Sertaozinho", 10, 15, 17, true}, // ChanceOfRain
+ {"Pirassununga", -15, -12, 2, false}, // Storm
+ {"Goiania", 7, 13, 18, true}, // ChanceOfStorm
+ {"Manaus", 35, 41, 44, false}, // Thunderstorm
+ {"Belem", -14, -13, -5, true} // ChanceOfThunderstorm
+};
+
+DemoForecastSource::DemoForecastSource(int waitTime, QObject *parent)
+ : ForecastSource(parent)
+ , m_waitTime(waitTime)
+ , m_idSeq(0)
+{
+ createContentList();
+}
+
+
+int DemoForecastSource::findByLocId(const QString &locId)
+{
+ if (!locId.isEmpty())
+ for (int i = 0; i < m_requests.count(); ++i)
+ if (m_requests[i].m_locId == locId)
+ return i;
+ return -1;
+}
+
+int DemoForecastSource::findByQuery(const QString &query)
+{
+ if (!query.isEmpty())
+ for (int i = 0; i < m_requests.count(); ++i)
+ if (m_requests[i].m_cityQuery == query)
+ return i;
+ return -1;
+}
+
+QString DemoForecastSource::findCity(const QString &name)
+{
+ QString city = name.toUpper();
+ for (int i = 0; i < m_list.count(); ++i) {
+ if (city == m_list[i].cityName().toUpper())
+ return m_list[i].key();
+ }
+ return QString();
+}
+
+int DemoForecastSource::getForecast(const QString &key, bool locationId)
+{
+
+ int idx = locationId ? findByLocId(key) : findByQuery(key);
+ if (idx >= 0)
+ return m_requests[idx].m_reqId;
+
+ QString locId = locationId ? key : findCity(key);
+
+ Request request;
+ request.m_locId = locId;
+ request.m_cityQuery = key;
+ request.m_reqId = getId();
+ m_requests.append(request);
+ if (m_waitTime > 0)
+ QTimer::singleShot(m_waitTime, this, SLOT(sendResponse()));
+ else sendResponse();
+ return request.m_reqId;
+}
+
+void DemoForecastSource::createContentList()
+{
+ QFile file(":fake_content.xml");
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "cannot open fake content file.";
+ return;
+ }
+ QDomDocument doc;
+ QString msg;
+ if (!doc.setContent(&file, &msg)) {
+ qWarning() << "fake content parse error: " << msg;
+ return;
+ }
+ if (doc.documentElement().childNodes().count() == 0) {
+ qWarning() << "fake content is empty.";
+ return;
+ }
+ QDomElement root = doc.documentElement();
+ for (int i = 0; i < root.childNodes().count(); ++i) {
+ if (root.childNodes().at(i).isElement()) {
+ QDomElement element = root.childNodes().at(i).toElement();
+ QString id = element.attribute("locId");
+ if (!id.isEmpty() && element.childNodes().count() == 1 &&
+ element.childNodes().at(0).isElement()) {
+ QDomElement child = element.childNodes().at(0).toElement();
+ m_list.append(ForecastData(new YahooWeatherResponse(id, child)));
+ }
+ }
+ }
+}
+
+void DemoForecastSource::sendResponse()
+{
+ if (!m_requests.isEmpty()) {
+ Request request = m_requests.takeFirst();
+ for (int i = 0; i < m_list.count(); ++i) {
+ if (m_list[i].key() == request.m_locId) {
+ emit forecastReceived(request.m_reqId, m_list[i]);
+ return;
+ }
+ }
+ emit forecastReceived(request.m_reqId, ForecastData(0));
+ }
+}
diff --git a/weather/src/demoforecastsource.h b/weather/src/demoforecastsource.h
new file mode 100644
index 0000000..dbfd715
--- /dev/null
+++ b/weather/src/demoforecastsource.h
@@ -0,0 +1,44 @@
+#ifndef DEMOFORECASTSOURCE_H
+#define DEMOFORECASTSOURCE_H
+
+#include "forecastsource.h"
+#include "yahooweatherresponse.h"
+
+#include <QList>
+
+class DemoForecastSource : public ForecastSource
+{
+ Q_OBJECT
+public:
+ DemoForecastSource(int waitTime, QObject *parent = 0);
+ int getForecast(const QString &key, bool locationId);
+
+private:
+
+ class Request
+ {
+ public:
+ int m_reqId;
+ QString m_locId;
+ QString m_cityQuery;
+ };
+
+ const int m_waitTime;
+ QList<Request> m_requests;
+ QList<ForecastData> m_list;
+ int m_idSeq;
+
+ void createContentList();
+ QString findCity(const QString &name);
+
+ int getId() { return ++m_idSeq; }
+
+ int findByLocId(const QString &locId);
+ int findByQuery(const QString &query);
+
+private slots:
+ void sendResponse();
+
+};
+
+#endif // DEMOFORECASTSOURCE_H
diff --git a/weather/src/fakecontentscreen.cpp b/weather/src/fakecontentscreen.cpp
new file mode 100644
index 0000000..493df9c
--- /dev/null
+++ b/weather/src/fakecontentscreen.cpp
@@ -0,0 +1,104 @@
+#include "fakecontentscreen.h"
+#include "pixmaploader.h"
+#include "settings.h"
+#include "painttextitem.h"
+
+#include <QPainter>
+#include <QDebug>
+#include <QCoreApplication>
+#include <QGraphicsSceneMouseEvent>
+
+#define SCREEN_FONT_SIZE (Settings::scaleHeight(30.0))
+#define BUTTON_FONT_SIZE (Settings::scaleHeight(40.0))
+
+#define LEFT_BUTTON_NAME "button_softkey_right"
+#define RIGHT_BUTTON_NAME "button_softkey_left"
+
+#define LEFT_PIXMAP (PixmapLoader::getPic(LEFT_BUTTON_NAME))
+#define RIGHT_PIXMAP (PixmapLoader::getPic(RIGHT_BUTTON_NAME))
+
+FakeContentScreen::FakeContentScreen(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_boundingRect(QPointF(0.0, 0.0), Settings::windowSize())
+ , m_left(LEFT_PIXMAP)
+ , m_right(RIGHT_PIXMAP)
+{
+}
+
+QRectF FakeContentScreen::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void FakeContentScreen::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+
+ static TextPainter text1(SCREEN_FONT_SIZE, QColor(255, 255, 255, 255), "You have no internet");
+ static TextPainter text2(SCREEN_FONT_SIZE, QColor(255, 255, 255, 255),
+ "connection. Do you want to");
+ static TextPainter text3(SCREEN_FONT_SIZE, QColor(255, 255, 255, 255),
+ "continue to the off-line");
+ static TextPainter text4(SCREEN_FONT_SIZE, QColor(255, 255, 255, 255), "weather demo?");
+
+ static TextPainter left(BUTTON_FONT_SIZE, QColor(255, 255, 255, 255), "YES");
+ static TextPainter right(BUTTON_FONT_SIZE, QColor(255, 255, 255, 255), "EXIT");
+
+ static const int buttonTop = boundingRect().height() - m_left.height();
+ static const int buttonLeft = boundingRect().width() - m_right.width();
+
+ static bool firstTime = true;
+
+ if (firstTime) {
+ firstTime = false;
+ const qreal top = (boundingRect().height() - 4 * text1.height() - m_left.height()) / 2;
+ TextPainter::locateAtCenter(&text1, 0.0, top, boundingRect().width());
+ TextPainter::locateAtCenter(&text2, 0.0, top + 1 * text1.height(), boundingRect().width());
+ TextPainter::locateAtCenter(&text3, 0.0, top + 2 * text1.height(), boundingRect().width());
+ TextPainter::locateAtCenter(&text4, 0.0, top + 3 * text1.height(), boundingRect().width());
+
+ const qreal buttonTextTop = (m_left.height() - left.height()) / 2 + buttonTop;
+ left.setPos(30, buttonTextTop);
+ right.setPos(boundingRect().width() - right.width() - 30, buttonTextTop);
+
+ }
+
+ painter->fillRect(boundingRect(), QColor(7, 18, 23, 255));
+
+ text1.paint(painter);
+ text2.paint(painter);
+ text3.paint(painter);
+ text4.paint(painter);
+
+ painter->drawPixmap(0, buttonTop, m_left);
+ painter->drawPixmap(buttonLeft, buttonTop, m_right);
+
+ left.paint(painter);
+ right.paint(painter);
+
+}
+
+int FakeContentScreen::loadImages()
+{
+ PixmapLoader::load(LEFT_BUTTON_NAME);
+ PixmapLoader::load(RIGHT_BUTTON_NAME);
+ return 2;
+}
+
+void FakeContentScreen::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ static QRectF left(0.0, boundingRect().height() - m_left.height(),
+ m_left.width(), m_left.height());
+ static QRectF right(boundingRect().width() - m_right.width(),
+ boundingRect().height() - m_left.height(),
+ m_right.width(), m_right.height());
+
+
+ if (left.contains(event->pos()))
+ emit userAccepted();
+ else
+ QCoreApplication::instance()->quit();
+
+}
diff --git a/weather/src/fakecontentscreen.h b/weather/src/fakecontentscreen.h
new file mode 100644
index 0000000..09c788a
--- /dev/null
+++ b/weather/src/fakecontentscreen.h
@@ -0,0 +1,30 @@
+#ifndef FAKECONTENTSCREEN_H
+#define FAKECONTENTSCREEN_H
+
+#include <QObject>
+#include <QGraphicsItem>
+
+class FakeContentScreen : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+public:
+ FakeContentScreen(QGraphicsItem *parent = 0);
+ static int loadImages();
+
+ QRectF boundingRect() const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+private:
+ const QRectF m_boundingRect;
+ QPixmap m_left;
+ QPixmap m_right;
+
+signals:
+ void userAccepted();
+};
+
+#endif // FAKECONTENTSCREEN_H
diff --git a/weather/src/forecast.h b/weather/src/forecast.h
new file mode 100644
index 0000000..f6fb6a8
--- /dev/null
+++ b/weather/src/forecast.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECAST_H
+#define FORECAST_H
+
+namespace Forecast
+{
+ enum ForecastType
+ {
+ MostlyCloudy = 0,
+ Cloudy,
+ MostlySunny,
+ PartlyCloudy,
+ Sunny,
+ Flurries,
+ Fog,
+ Haze,
+ Sand,
+ Dust,
+ Icy,
+ Sleet,
+ ChanceOfSleet,
+ Snow,
+ ChanceOfSnow,
+ Mist,
+ Rain,
+ ChanceOfRain,
+ Storm,
+ ChanceOfStorm,
+ Thunderstorm,
+ ChanceOfThunderstorm,
+ UnknownForecast
+ };
+
+ enum Effect
+ {
+ FogEffect = 0,
+ HazeEffect,
+ NoEffect
+ };
+}
+
+#endif // FORECAST_H
diff --git a/weather/src/forecastdata.cpp b/weather/src/forecastdata.cpp
new file mode 100644
index 0000000..45e2567
--- /dev/null
+++ b/weather/src/forecastdata.cpp
@@ -0,0 +1,78 @@
+#include "forecastdata.h"
+
+struct ConditionTypeData
+{
+ const int id;
+ Forecast::ForecastType type;
+};
+
+static const int ContitionTypeCount = 47;
+static const ConditionTypeData ContitionType[ContitionTypeCount] = {
+ {1, Forecast::Storm},
+ {2, Forecast::Thunderstorm},
+ {3, Forecast::Thunderstorm},
+ {4, Forecast::Thunderstorm},
+ {5, Forecast::Sleet},
+ {6, Forecast::Rain},
+ {7, Forecast::Sleet},
+ {8, Forecast::Mist},
+ {9, Forecast::Mist},
+ {10, Forecast::Rain},
+ {11, Forecast::Rain},
+ {12, Forecast::Rain},
+ {13, Forecast::Flurries},
+ {14, Forecast::Flurries},
+ {15, Forecast::Snow},
+ {16, Forecast::Snow},
+ {17, Forecast::Snow},
+ {18, Forecast::Sleet},
+ {19, Forecast::Haze},
+ {20, Forecast::Fog},
+ {21, Forecast::Haze},
+ {22, Forecast::Fog},
+ {23, Forecast::Cloudy},
+ {24, Forecast::Cloudy},
+ {25, Forecast::Cloudy},
+ {26, Forecast::Cloudy},
+ {27, Forecast::MostlyCloudy},
+ {28, Forecast::MostlyCloudy},
+ {29, Forecast::PartlyCloudy},
+ {30, Forecast::PartlyCloudy},
+ {31, Forecast::Sunny},
+ {32, Forecast::Sunny},
+ {33, Forecast::MostlySunny},
+ {34, Forecast::MostlySunny},
+ {35, Forecast::Sleet},
+ {36, Forecast::Sunny},
+ {37, Forecast::Thunderstorm},
+ {38, Forecast::Thunderstorm},
+ {39, Forecast::Thunderstorm},
+ {40, Forecast::Storm},
+ {41, Forecast::Snow},
+ {42, Forecast::Sleet},
+ {43, Forecast::Snow},
+ {44, Forecast::PartlyCloudy},
+ {45, Forecast::Thunderstorm},
+ {46, Forecast::Sleet},
+ {47, Forecast::Thunderstorm}
+};
+
+
+Forecast::ForecastType ForecastData::type() const
+{
+ if (m_data) {
+ for (int i = 0; i < ContitionTypeCount; ++i)
+ if (ContitionType[i].id == m_data->condition().code())
+ return ContitionType[i].type;
+ }
+ return Forecast::UnknownForecast;
+}
+
+bool ForecastData::night() const
+{
+ if (!m_data)
+ return false;
+ return m_data->condition().date().time() < m_data->astronomy().sunrise()
+ || m_data->condition().date().time() > m_data->astronomy().sunset();
+}
+
diff --git a/weather/src/forecastdata.h b/weather/src/forecastdata.h
new file mode 100644
index 0000000..bf5b9cd
--- /dev/null
+++ b/weather/src/forecastdata.h
@@ -0,0 +1,44 @@
+#ifndef FORECASTDATA_H
+#define FORECASTDATA_H
+
+#include <QString>
+#include "forecast.h"
+#include "yahooweatherresponse.h"
+
+#include <QSharedPointer>
+
+class YahooWeatherResponse;
+
+class ForecastData
+{
+public:
+ ForecastData() : m_error(false), m_data(0) {}
+ ForecastData(YahooWeatherResponse *data) : m_error(!data), m_data(data) {}
+
+ Forecast::ForecastType type() const;
+ bool night() const;
+ bool error() const { return m_error; }
+ QString key() const { return m_data ? m_data->locationCode() : QString(); }
+ QString cityName() const { return m_data ? m_data->location().city() : QString(); }
+ int lower() const { return m_data ? m_data->forecast(0).low() : 0; }
+ int current() const { return m_data ? m_data->condition().temperature() : 0; }
+ int upper() const { return m_data ? m_data->forecast(0).high() : 0; }
+ bool isNull() const { return !m_error && !m_data; }
+
+ inline bool operator==(const ForecastData &other);
+ bool operator!=(const ForecastData &other) { return !operator==(other); }
+
+private:
+ bool m_error;
+
+ QSharedPointer<YahooWeatherResponse> m_data;
+};
+
+bool ForecastData::operator==(const ForecastData &other)
+{
+ return type() == other.type() && night() == other.night() && error() == other.error()
+ && key() == other.key() && cityName() == other.cityName() && lower() == other.lower()
+ && current() == other.current() && upper() == other.upper();
+}
+
+#endif // FORECASTDATA_H
diff --git a/weather/src/forecasthungitem.cpp b/weather/src/forecasthungitem.cpp
new file mode 100644
index 0000000..428b83c
--- /dev/null
+++ b/weather/src/forecasthungitem.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "forecasthungitem.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+#include <QPropertyAnimation>
+
+#define LINE_NAME_SUFFIX "_line"
+
+typedef struct
+{
+ const char * const prefix;
+ const qreal x;
+ const qreal y;
+
+ QString name() const { return prefix; }
+ QString lineName() const { return name() + LINE_NAME_SUFFIX; }
+
+ qreal scaledX() const { return Settings::scaleWidth(x); }
+ qreal scaledY() const { return Settings::scaleHeight(y); }
+
+ QPixmap pic() const { return PixmapLoader::getPic(name()); }
+ QPixmap linePic() const { return PixmapLoader::getPic(lineName()); }
+
+private:
+
+} HungObjectData;
+
+static HungObjectData HungItemsData[ForecastHungItem::TypeCount] = {
+ {"cloud_1", 160.0, 3.0}, // Cloud1
+ {"cloud_2", 131.0, 6.0}, // Cloud2
+ {"cloud_3", 102.0, 11.0}, // Cloud3
+ {"cloud_rain_1", 160.0, 3.0}, // CloudRain1
+ {"cloud_rain_2", 131.0, 6.0}, // CloudRain2
+ {"cloud_rain_3", 102.0, 11.0}, // CloudRain3
+ {"cloud_storm_1", 160.0, 3.0}, // CloudStorm1
+ {"cloud_storm_2", 131.0, 6.0}, // CloudStorm2
+ {"cloud_storm_3", 102.0, 11.0}, // CloudStorm3
+ {"cloud_tstorm_1", 160.0, 3.0}, // CloudTStorm1
+ {"cloud_tstorm_2", 131.0, 6.0}, // CloudTStorm2
+ {"sun", 198.0, 125.0}, // Sun
+ {"cold_sun", 121.5, 65.0}, // ColdSun
+ {"moon", 97.5, 18.0} // Moon
+};
+
+// ForecastHungItem
+
+ForecastHungItem::ForecastHungItem(ItemType type, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_type(type)
+ , m_targetPicTop(0)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+
+ HungObjectData &data = HungItemsData[m_type];
+
+ m_pic = new QGraphicsPixmapItem(data.pic(), this);
+ m_line = new QGraphicsPixmapItem(data.linePic(), m_pic);
+
+ m_pic->setPos(0.0, 0.0);
+
+ m_line->setPos(data.scaledX(), data.scaledY() - m_line->boundingRect().bottom());
+ reset();
+}
+
+int ForecastHungItem::loadImages()
+{
+ int result = 0;
+ for (int i = 0; i < ForecastHungItem::TypeCount; ++i) {
+ PixmapLoader::load(HungItemsData[i].name());
+ PixmapLoader::load(HungItemsData[i].lineName());
+ result += 2;
+ }
+ return result;
+}
+
+qreal ForecastHungItem::lineLenght() const
+{
+ return m_targetPicTop + HungItemsData[m_type].scaledY();
+}
+
+void ForecastHungItem::setLineLenght(qreal lenght)
+{
+ m_targetPicTop = lenght - HungItemsData[m_type].scaledY();
+}
+
+void ForecastHungItem::setLinePos(qreal linePos)
+{
+ setPos(linePos - HungItemsData[m_type].scaledX(), 0.0);
+}
+
+QPointF ForecastHungItem::picPos() const
+{
+ return QPointF(pos().x(), pos().y() + m_targetPicTop);
+}
+
+void ForecastHungItem::setPicPos(QPointF picPos)
+{
+ setPos(picPos.x(), 0.0);
+ m_targetPicTop = picPos.y();
+}
+
+void ForecastHungItem::reset()
+{
+ setPicTop(-m_pic->boundingRect().height());
+}
+
+QRectF ForecastHungItem::boundingRect () const
+{
+ return QRectF(0.0, 0.0, m_pic->boundingRect().width(),
+ m_targetPicTop + m_pic->boundingRect().height());
+}
+
+void ForecastHungItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+QAbstractAnimation *ForecastHungItem::getAnimation()
+{
+ QPropertyAnimation *result = new QPropertyAnimation(this, "picTop");
+ result->setStartValue(-m_pic->boundingRect().height());
+ result->setEndValue(m_targetPicTop);
+ result->setEasingCurve(QEasingCurve::OutBack);
+ result->setDuration(500);
+ return result;
+}
diff --git a/weather/src/forecasthungitem.h b/weather/src/forecasthungitem.h
new file mode 100644
index 0000000..bb86552
--- /dev/null
+++ b/weather/src/forecasthungitem.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECASTHUNGITEM_H
+#define FORECASTHUNGITEM_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QAbstractAnimation>
+
+class ForecastHungItem : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(qreal picTop READ picTop WRITE setPicTop)
+public:
+ enum ItemType {
+ Cloud1 = 0,
+ Cloud2,
+ Cloud3,
+ CloudRain1,
+ CloudRain2,
+ CloudRain3,
+ CloudStorm1,
+ CloudStorm2,
+ CloudStorm3,
+ CloudTStorm1,
+ CloudTStorm2,
+ Sun,
+ ColdSun,
+ Moon,
+ TypeCount
+ };
+
+ static int loadImages();
+
+ ForecastHungItem(ItemType type, QGraphicsItem *parent = 0);
+ QAbstractAnimation *getAnimation();
+
+ qreal lineLenght() const;
+ void setLineLenght(qreal lenght);
+ void setLinePos(qreal linePos);
+
+ QPointF picPos() const;
+ void setPicPos(QPointF pos);
+ void setPicPos(qreal x, qreal y) { setPicPos(QPointF(x, y)); }
+
+ void reset();
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+private:
+ QGraphicsPixmapItem *m_pic;
+ QGraphicsPixmapItem *m_line;
+ ItemType m_type;
+ qreal m_targetPicTop;
+
+ qreal picTop() const { return m_pic->pos().y(); }
+ void setPicTop(qreal top) { m_pic->setPos(m_pic->pos().x(), top); }
+
+};
+
+#endif // FORECASTHUNGITEM_H
diff --git a/weather/src/forecastprovider.cpp b/weather/src/forecastprovider.cpp
new file mode 100644
index 0000000..5bda3fe
--- /dev/null
+++ b/weather/src/forecastprovider.cpp
@@ -0,0 +1,54 @@
+#include "forecastprovider.h"
+#include <QTime>
+#include <QTimer>
+
+#include "forecast.h"
+
+ForecastProvider::ForecastProvider()
+ : m_source(0)
+{
+}
+
+ForecastProvider::~ForecastProvider()
+{
+}
+
+int ForecastProvider::getForecast(const QString &key, bool locationId)
+{
+ ForecastProvider *obj = instance();
+ if (obj->m_source)
+ return obj->m_source->getForecast(key, locationId);
+ return -1;
+}
+
+ForecastProvider *ForecastProvider::instance()
+{
+ static ForecastProvider * const result(new ForecastProvider());
+ return result;
+}
+
+
+void ForecastProvider::setForecastSource(ForecastSource *source)
+{
+ ForecastProvider *obj = instance();
+ if (obj->m_source) {
+ disconnect(obj->m_source, SIGNAL(forecastReceived(int, ForecastData)),
+ obj, SIGNAL(forecastResponse(int, ForecastData)));
+ obj->m_source->deleteLater();
+ }
+ obj->m_source = source;
+ if (obj->m_source)
+ connect(obj->m_source, SIGNAL(forecastReceived(int, ForecastData)),
+ obj, SIGNAL(forecastResponse(int, ForecastData)), Qt::QueuedConnection);
+}
+
+void ForecastProvider::connectToResponseSignal(QObject *receiver, const char *method)
+{
+ QObject::connect(instance(), SIGNAL(forecastResponse(int, ForecastData)),
+ receiver, method, Qt::QueuedConnection);
+}
+
+void ForecastProvider::disconnectReceiver(QObject *receiver)
+{
+ instance()->disconnect(receiver);
+}
diff --git a/weather/src/forecastprovider.h b/weather/src/forecastprovider.h
new file mode 100644
index 0000000..fcb75dd
--- /dev/null
+++ b/weather/src/forecastprovider.h
@@ -0,0 +1,37 @@
+#ifndef FORECASTPROVIDER_H
+#define FORECASTPROVIDER_H
+
+#include <QObject>
+#include <QString>
+#include <QStringList>
+#include <QList>
+#include <QMap>
+
+#include "forecastdata.h"
+#include "forecastsource.h"
+
+class ForecastProvider : public QObject
+{
+ Q_OBJECT
+public:
+ static int getForecast(const QString &key, bool locationId);
+
+ static void connectToResponseSignal(QObject *receiver, const char *method);
+ static void disconnectReceiver(QObject *receiver);
+
+ static void setForecastSource(ForecastSource *source);
+
+signals:
+ void forecastResponse(int reqId, const ForecastData &forecast);
+
+private:
+
+ ForecastSource *m_source;
+
+ ForecastProvider();
+ ~ForecastProvider();
+ static ForecastProvider *instance();
+
+};
+
+#endif // FORECASTPROVIDER_H
diff --git a/weather/src/forecastrain.cpp b/weather/src/forecastrain.cpp
new file mode 100644
index 0000000..81f3175
--- /dev/null
+++ b/weather/src/forecastrain.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "forecastrain.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+typedef struct
+{
+ const char * const prefix;
+ QString name() const { return prefix; }
+ QPixmap pic() const { return PixmapLoader::getPic(name()); }
+
+private:
+
+} RainItemData;
+
+static const int RainItemCount = 3;
+static RainItemData RainItemArray[RainItemCount] = {
+ {"rain_01"},
+ {"rain_02"},
+ {"rain_03"}
+};
+
+ForecastRain::ForecastRain(RainType type, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_animation(this, "progress")
+{
+ for (int i = Light; i <= Heavy; ++i) {
+ QGraphicsPixmapItem *item = new QGraphicsPixmapItem(RainItemArray[i].pic(), this);
+ item->setPos(0.0, -20.0);
+ item->hide();
+ m_items.append(item);
+ }
+ m_animation.setStartValue(0.0);
+ m_animation.setEndValue(3.0);
+ m_animation.setDuration(type == Light ? 3000 : type == Medium ? 2000 : 1000);
+ m_animation.setLoopCount(-1);
+}
+
+int ForecastRain::loadImages()
+{
+ for (int i = 0; i < RainItemCount;++i)
+ PixmapLoader::load(RainItemArray[i].name());
+ return RainItemCount;
+}
+
+void ForecastRain::start()
+{
+ switch (m_animation.state()) {
+ case QAbstractAnimation::Stopped:
+ m_animation.start();
+ break;
+ case QAbstractAnimation::Paused:
+ m_animation.resume();
+ break;
+ default:
+ break;
+ }
+}
+
+QRectF ForecastRain::boundingRect () const
+{
+ return m_items.isEmpty() ? QRectF() : m_items[0]->boundingRect();
+}
+
+void ForecastRain::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+void ForecastRain::stop()
+{
+ if (m_items.count() > 1 && m_animation.state() == QAbstractAnimation::Running)
+ m_animation.pause();
+}
+
+void ForecastRain::setProgress(qreal progress)
+{
+ m_progress = progress;
+ for (int i = 0; i < m_items.count(); ++i) {
+
+ qreal x = progress + i - 1;
+ x = x < 0 ? 3.0 + x : x > 3.0 ? x - 3.0 : x;
+ const qreal opacity = x <= 1.0 ? x
+ : x <= 2.0 ? 2.0 - x
+ : 0.0;
+ m_items[i]->setOpacity(opacity);
+ if ((opacity != 0.0) != m_items[i]->isVisible())
+ m_items[i]->setVisible(opacity != 0.0);
+ }
+}
diff --git a/weather/src/forecastrain.h b/weather/src/forecastrain.h
new file mode 100644
index 0000000..91b5aaf
--- /dev/null
+++ b/weather/src/forecastrain.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECASTRAIN_H
+#define FORECASTRAIN_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QPropertyAnimation>
+#include <QGraphicsPixmapItem>
+
+#include <QDebug>
+
+class ForecastRain : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(qreal progress READ progress WRITE setProgress)
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
+public:
+ enum RainType
+ {
+ Light = 0,
+ Medium,
+ Heavy
+ };
+ ForecastRain(RainType type, QGraphicsItem *parent = 0);
+
+ static int loadImages();
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+public slots:
+ void start();
+ void stop();
+ void show() { QGraphicsItem::show(); }
+
+private:
+ QList<QGraphicsPixmapItem*> m_items;
+ QPropertyAnimation m_animation;
+ qreal m_progress;
+
+ void setProgress(qreal progress);
+ qreal progress() const { return m_progress; }
+};
+
+#endif // FORECASTRAIN_H
diff --git a/weather/src/forecastsnow.cpp b/weather/src/forecastsnow.cpp
new file mode 100644
index 0000000..d342c1e
--- /dev/null
+++ b/weather/src/forecastsnow.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "forecastsnow.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+#include <QPainter>
+
+typedef struct
+{
+ const char * const prefix;
+ QString name() const { return prefix; }
+ QPixmap pic() const { return PixmapLoader::getPic(name()); }
+
+private:
+
+} SnowFlaketData;
+
+static const int SnowFlakeCount = 10;
+static SnowFlaketData SnowFlakeArray[SnowFlakeCount] = {
+ {"snow_flake"},
+ {"snow_flake_01"},
+ {"snow_flake_02"},
+ {"snow_flake_03"},
+ {"snow_flake_04"},
+ {"snow_flake_05"},
+ {"snow_flake_06"},
+ {"snow_flake_07"},
+ {"snow_flake_08"},
+ {"snow_flake_09"},
+};
+
+static const qreal maxVerticalSpeed = 70.0;
+static const qreal minVerticalSpeed = 30.0;
+static const qreal mediumVerticalSpeed = (maxVerticalSpeed + minVerticalSpeed) / 2;
+
+static const qreal maxHorizontalSpeed = 100.0;
+static const qreal minHorizontalSpeed = 20.0;
+
+static const qreal windSpeed = -50.0;
+
+static const qreal speedDeviation = 0.15;
+
+static const int deviation1 = 2;
+static const int deviation2 = 31;
+
+#define RANDOM_FACTOR (qreal(qrand()) / qreal(RAND_MAX))
+
+// ForecastSnow
+
+ForecastSnow::SnowFlake::SnowFlake(int type, const QSizeF &bounds)
+ : m_pixmap(SnowFlakeArray[type].pic())
+ , m_bounds(bounds)
+ , m_waitingTime(0)
+{
+ qreal x = minHorizontalSpeed + windSpeed +
+ RANDOM_FACTOR * (maxHorizontalSpeed - minHorizontalSpeed);
+ qreal y = minVerticalSpeed + RANDOM_FACTOR * (maxVerticalSpeed - minVerticalSpeed);
+ m_speed = QPointF(x, y);
+}
+
+int ForecastSnow::loadImages()
+{
+ for (int i = 0; i < SnowFlakeCount;++i)
+ PixmapLoader::load(SnowFlakeArray[i].name());
+ return SnowFlakeCount;
+}
+
+static inline bool doDeviation(qreal &factor)
+{
+ if (qrand() % deviation2 > deviation1)
+ return false;
+ factor = 1.0 + ((qrand() % 2) ? speedDeviation : -speedDeviation);
+ return true;
+}
+
+void ForecastSnow::SnowFlake::timerEvent(int interval_ms)
+{
+ if (m_waitingTime < 0) {
+ m_waitingTime += interval_ms;
+ return;
+ }
+ const qreal interval_s = qreal(interval_ms) / 1000.0;
+ qreal x = m_pos.x() + m_speed.x() * interval_s;
+ qreal y = m_pos.y() + m_speed.y() * interval_s;
+
+ y = y >= m_bounds.height() ? -m_pixmap.height() : y;
+ x = x <= -m_pixmap.width() ? m_bounds.width()
+ : x >= m_bounds.width() ? -m_pixmap.width() : x;
+
+ m_pos = QPointF(x, y);
+
+ qreal factor;
+ if (doDeviation(factor))
+ m_speed.setY(qMax(qMin(m_speed.y() * factor, maxVerticalSpeed), minVerticalSpeed));
+ if (doDeviation(factor)) {
+ qreal x = m_speed.x() - windSpeed;
+ x = qMax(qMin(x * factor, maxHorizontalSpeed), minHorizontalSpeed) + windSpeed;
+ m_speed.setX(x);
+ }
+}
+
+void ForecastSnow::SnowFlake::paint(QPainter *painter)
+{
+ if (isVisible() && m_waitingTime >= 0)
+ painter->drawPixmap(m_pos, m_pixmap);
+}
+
+ForecastSnow::ForecastSnow(int count, const QRectF &bounds, QGraphicsItem *parent)
+ : m_bounds(bounds)
+ , m_parent(parent)
+ , m_visible(false)
+{
+ qsrand(QTime(0, 0).secsTo(QTime::currentTime()) * qrand());
+ for (int i = 0; i < count; ++i)
+ m_flakes.append(new SnowFlake(qrand() % 10, m_bounds.size()));
+ updateFLakesPositions();
+}
+
+void ForecastSnow::updateFLakesPositions()
+{
+ qsrand(QTime(0, 0).secsTo(QTime::currentTime()) * qrand());
+
+ int maxTime = -1000 * m_bounds.height() / mediumVerticalSpeed;
+
+ foreach(SnowFlake *flake, m_flakes) {
+
+ const qreal height = -flake->height();
+ const qreal width = m_bounds.width() - flake->width();
+
+ flake->setPos(width *RANDOM_FACTOR, height *RANDOM_FACTOR);
+ flake->setWaitingTime(maxTime * RANDOM_FACTOR);
+ }
+}
+
+void ForecastSnow::paint(QPainter *painter)
+{
+ if (!m_visible)
+ return;
+
+ painter->translate(m_bounds.topLeft());
+
+ foreach(SnowFlake *flake, m_flakes)
+ flake->paint(painter);
+
+ painter->translate(QPointF(-m_bounds.left(), -m_bounds.top()));
+}
+
+void ForecastSnow::start()
+{
+ m_lastTick = QTime::currentTime();
+ m_ticker.start(30, this);
+ show();
+}
+
+void ForecastSnow::stop()
+{
+ m_ticker.stop();
+}
+
+void ForecastSnow::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ QTime now = QTime::currentTime();
+ int interval = m_lastTick.msecsTo(now);
+ m_lastTick = now;
+ foreach(SnowFlake *flake, m_flakes)
+ flake->timerEvent(interval);
+ if (m_parent)
+ m_parent->update(m_bounds);
+}
diff --git a/weather/src/forecastsnow.h b/weather/src/forecastsnow.h
new file mode 100644
index 0000000..809b232
--- /dev/null
+++ b/weather/src/forecastsnow.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECASTSNOW_H
+#define FORECASTSNOW_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QBasicTimer>
+#include <QTime>
+
+class ForecastSnow : public QObject
+{
+ Q_OBJECT
+public:
+ ForecastSnow(int count, const QRectF &bounds, QGraphicsItem *parent);
+
+ static int loadImages();
+
+ void paint(QPainter *painter);
+
+public slots:
+ void start();
+ void stop();
+ void show() { m_visible = true; }
+ void hide() { m_visible = false; }
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ class SnowFlake
+ {
+ public:
+ SnowFlake(int type, const QSizeF &bounds);
+ void timerEvent(int interval_ms);
+
+ qreal width() const { return m_pixmap.width(); }
+ qreal height() const { return m_pixmap.height(); }
+
+ void setPos(qreal x, qreal y) { m_pos = QPointF(x, y); }
+ void setWaitingTime(int value) { m_waitingTime = value; }
+
+ bool isVisible() const { return !m_pixmap.isNull(); }
+
+ inline void paint(QPainter *painter);
+
+ private:
+ QPixmap m_pixmap;
+ const QSizeF m_bounds;
+ QPointF m_speed;
+ QPointF m_pos;
+ int m_waitingTime;
+ };
+
+ const QRectF m_bounds;
+ QGraphicsItem *m_parent;
+ QList<SnowFlake*> m_flakes;
+ QBasicTimer m_ticker;
+ QTime m_lastTick;
+
+ qreal m_visible;
+
+ void updateFLakesPositions();
+
+};
+
+#endif // FORECASTSNOW_H
diff --git a/weather/src/forecastsource.h b/weather/src/forecastsource.h
new file mode 100644
index 0000000..8c7cfbe
--- /dev/null
+++ b/weather/src/forecastsource.h
@@ -0,0 +1,18 @@
+#ifndef FORECASTSOURCE_H
+#define FORECASTSOURCE_H
+
+#include <QObject>
+#include "forecastdata.h"
+
+class ForecastSource : public QObject
+{
+ Q_OBJECT
+public:
+ ForecastSource(QObject *parent = 0) : QObject(parent) {}
+ virtual int getForecast(const QString &key, bool locationId) = 0;
+
+signals:
+ void forecastReceived(int id, const ForecastData &data);
+};
+
+#endif // FORECASTSOURCE_H
diff --git a/weather/src/forecaststars.cpp b/weather/src/forecaststars.cpp
new file mode 100644
index 0000000..97379dc
--- /dev/null
+++ b/weather/src/forecaststars.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "forecaststars.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+#include <QTime>
+#include <QPropertyAnimation>
+
+typedef struct
+{
+ const char * const prefix;
+ const qreal radius;
+ QString name() const { return prefix; }
+ QPixmap pic() const { return PixmapLoader::getPic(name()); }
+
+private:
+
+} StarObjectData;
+
+static const int StarTypesCount = 3;
+static StarObjectData StarObjectsData[StarTypesCount] = {
+ {"star_01", 0.30000},
+ {"star_02", 0.22428},
+ {"star_03", 0.16666}
+};
+
+// ForecastStars
+
+ForecastStars::Star::Star(int type, QGraphicsItem *parent)
+ : QGraphicsPixmapItem(StarObjectsData[type].pic(), parent)
+ , starType(type)
+{
+ setOpacity(0.0);
+ hide();
+}
+
+ForecastStars::ForecastStars(int count, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_progress(1.0)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+ qsrand(QTime(0, 0).secsTo(QTime::currentTime()) * qrand());
+ for (int i = 0; i < count; ++i)
+ m_starts.append(new Star(qrand() % 3, this));
+}
+
+int ForecastStars::loadImages()
+{
+ for (int i = 0; i < StarTypesCount;++i)
+ PixmapLoader::load(StarObjectsData[i].name());
+ return StarTypesCount;
+}
+
+QAbstractAnimation *ForecastStars::getAnimation()
+{
+ QPropertyAnimation *result = new QPropertyAnimation(this, "progress");
+ result->setStartValue(0.0);
+ result->setEndValue(1.0);
+ result->setEasingCurve(QEasingCurve::OutBack);
+ result->setDuration(m_starts.count() * 100);
+ connect(result, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)),
+ this, SLOT(animationStateChanged(QAbstractAnimation::State, QAbstractAnimation::State)));
+ return result;
+}
+
+void ForecastStars::animationStateChanged(QAbstractAnimation::State newState,
+ QAbstractAnimation::State oldState)
+{
+ if (oldState == QAbstractAnimation::Stopped && newState == QAbstractAnimation::Running) {
+ foreach (Star *star, m_starts) {
+ star->setOpacity(0.0);
+ star->show();
+ }
+ }
+}
+
+void ForecastStars::setRect(QRectF rect)
+{
+ setPos(rect.topLeft());
+ m_boundingRect = rect;
+ m_boundingRect.moveTo(0.0, 0.0);
+ updateStarsPositions();
+}
+
+QRectF ForecastStars::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+QRectF ForecastStars::rect() const
+{
+ QRectF result(m_boundingRect);
+ result.moveTo(pos());
+ return result;
+}
+
+void ForecastStars::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+}
+
+void ForecastStars::setProgress(qreal progress)
+{
+ m_progress = progress;
+ const qreal delta = 1.0 / qreal(m_starts.count());
+
+ for (int i = 0; i < m_starts.count(); ++i) {
+ qreal val = progress - i * delta;
+ m_starts[i]->setOpacity(val < 0.0 ? 0.0 : val > delta ? 1.0 : val / delta);
+ }
+}
+
+bool ForecastStars::checkColision(const QRectF &rect1, qreal radius1,
+ const QRectF &rect2, qreal radius2)
+{
+ QPointF p1(rect1.left() + rect1.width() / 2.0, rect1.top() + rect1.height() / 2.0);
+ QPointF p2(rect2.left() + rect2.width() / 2.0, rect2.top() + rect2.height() / 2.0);
+ qreal min = radius1 + radius2;
+ return qAbs(p1.x() - p2.x()) > min && qAbs(p1.y() - p2.y()) > min;
+}
+
+bool ForecastStars::checkColision(Star *item1, Star *item2)
+{
+ QRectF r1 = item1->boundingRect();
+ r1.moveTo(item1->pos());
+ qreal radius1 = item1->boundingRect().width() * StarObjectsData[item1->starType].radius;
+
+ QRectF r2 = item2->boundingRect();
+ r2.moveTo(item2->pos());
+ qreal radius2 = item2->boundingRect().width() * StarObjectsData[item2->starType].radius;
+
+ return checkColision(r1, radius1, r2, radius2);
+}
+
+bool ForecastStars::checkColision(Star *star, const QList<Star*> &items)
+{
+ foreach(Star *item, items) {
+ if (!checkColision(star, item))
+ return false;
+ }
+ return true;
+}
+
+QPointF ForecastStars::getRandomPos(const QRectF &border)
+{
+ return QPointF(qreal(qrand()) / qreal(RAND_MAX) * border.width() + border.left(),
+ qreal(qrand()) / qreal(RAND_MAX) * border.height() + border.top());
+}
+
+void ForecastStars::updateStarsPositions()
+{
+ QList<Star*> items;
+ qsrand(QTime(0, 0).secsTo(QTime::currentTime()) * qrand());
+ foreach(Star *star, m_starts) {
+ const QRectF rect(boundingRect());
+ const QRectF starRect(star->boundingRect());
+ const QPointF topLeft(rect.left() - starRect.left(), rect.top() - starRect.top());
+ const QPointF bottomRight(rect.right() - starRect.right(),
+ rect.bottom() - starRect.bottom());
+ const QRectF border(topLeft, bottomRight);
+
+ int tries = 10;
+ do
+ star->setPos(getRandomPos(border));
+ while (tries-- && !checkColision(star, items));
+ items.append(star);
+ }
+}
diff --git a/weather/src/forecaststars.h b/weather/src/forecaststars.h
new file mode 100644
index 0000000..23a135c
--- /dev/null
+++ b/weather/src/forecaststars.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECASTSTARS_H
+#define FORECASTSTARS_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QAbstractAnimation>
+
+class ForecastStars : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(qreal progress READ progress WRITE setProgress)
+public:
+ ForecastStars(int count, QGraphicsItem *parent = 0);
+ QAbstractAnimation *getAnimation();
+
+ static int loadImages();
+
+ QRectF rect() const;
+ void setRect(QRectF rect);
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+private:
+ class Star : public QGraphicsPixmapItem
+ {
+ public:
+ Star(int type, QGraphicsItem *parent);
+ const int starType;
+ };
+
+ qreal m_progress;
+ QRectF m_boundingRect;
+ QList<Star*> m_starts;
+
+ void setProgress(qreal progress);
+ qreal progress() const { return m_progress; }
+
+ inline bool checkColision(const QRectF &rect1, qreal radius1,
+ const QRectF &rect2, qreal radius2);
+ inline bool checkColision(Star *item1, Star *item2);
+ inline bool checkColision(Star *star, const QList<Star*> &items);
+ inline QPointF getRandomPos(const QRectF &border);
+ void updateStarsPositions();
+
+private slots:
+ void animationStateChanged(QAbstractAnimation::State newState,
+ QAbstractAnimation::State oldState);
+};
+
+#endif // FORECASTSTARS_H
diff --git a/weather/src/forecastview.cpp b/weather/src/forecastview.cpp
new file mode 100644
index 0000000..f7a0d6b
--- /dev/null
+++ b/weather/src/forecastview.cpp
@@ -0,0 +1,643 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "forecastview.h"
+#include "settings.h"
+
+#include <QPropertyAnimation>
+#include <QParallelAnimationGroup>
+#include <QSequentialAnimationGroup>
+
+#include <QDebug>
+
+// Variations
+
+struct VariationData
+{
+ const qreal x;
+ const qreal y;
+};
+
+static const int variationCount = 3;
+static const VariationData variationArray[variationCount] =
+{
+ {35.0, 20.0},
+ {25.0, 15.0},
+ {15.0, 10.0}
+};
+
+// Sun positions
+
+struct SunPosition
+{
+ const ForecastHungItem::ItemType type;
+ const qreal linePos;
+ const qreal picTop;
+};
+
+static const int sunPositionsArrayCount = 3;
+static const SunPosition sunPositionsArray[sunPositionsArrayCount] =
+{
+ {ForecastHungItem::Sun, 240, 124},
+ {ForecastHungItem::ColdSun, 240, 205},
+ {ForecastHungItem::Moon, 240, 235}
+};
+
+class ForecastMostlyCloudyView : public ForecastView
+{
+public:
+ ForecastMostlyCloudyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::MostlyCloudy, parent)
+ {
+ if (night) {
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(357, 308), 0);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(46, 274), 1);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(463, 273), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(139, 221), 2);
+ setMainHangItem(ForecastHungItem::Moon);
+ } else {
+ addHungItem(ForecastHungItem::Cloud1, QPointF(357, 308), 0);
+ addHungItem(ForecastHungItem::Cloud2, QPointF(46, 274), 1);
+ addHungItem(ForecastHungItem::Cloud2, QPointF(463, 273), 1);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(139, 221), 2);
+ setMainHangItem(ForecastHungItem::ColdSun);
+ }
+ }
+};
+
+class ForecastCloudyView : public ForecastView
+{
+public:
+ ForecastCloudyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Cloudy, parent)
+ {
+ if (night) {
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(440, 275), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(121, 273), 0);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(307, 266), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(372, 224), 2);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(33, 227), 2);
+ setMainHangItem(ForecastHungItem::Moon);
+ } else {
+ addHungItem(ForecastHungItem::Cloud1, QPointF(440, 275), 0);
+ addHungItem(ForecastHungItem::Cloud1, QPointF(121, 273), 0);
+ addHungItem(ForecastHungItem::Cloud2, QPointF(307, 266), 1);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(372, 224), 2);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(33, 227), 2);
+ setMainHangItem(ForecastHungItem::ColdSun);
+ }
+ }
+};
+
+class ForecastMostlySunnyView : public ForecastView
+{
+public:
+ ForecastMostlySunnyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::MostlySunny, parent)
+ {
+ if (night) {
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(72, 389), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(454, 295), 2);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(48, 164), 2);
+ setMainHangItem(ForecastHungItem::Moon);
+ addStars();
+ } else {
+ addHungItem(ForecastHungItem::Cloud2, QPointF(72, 389), 1);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(454, 295), 2);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(48, 164), 2);
+ setMainHangItem(ForecastHungItem::Sun);
+ }
+ }
+};
+
+class ForecastPartlyCloudyView : public ForecastView
+{
+public:
+ ForecastPartlyCloudyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::PartlyCloudy, parent)
+ {
+ if (night) {
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(389, 315), 0);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(92, 300), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(451, 258), 2);
+ setMainHangItem(ForecastHungItem::Moon);
+ } else {
+ addHungItem(ForecastHungItem::Cloud1, QPointF(389, 315), 0);
+ addHungItem(ForecastHungItem::Cloud2, QPointF(92, 300), 1);
+ addHungItem(ForecastHungItem::Cloud3, QPointF(451, 258), 2);
+ setMainHangItem(ForecastHungItem::Sun);
+ }
+ }
+};
+
+class ForecastSunnyView : public ForecastView
+{
+public:
+ ForecastSunnyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Sunny, parent)
+ {
+ if (night) {
+ setMainHangItem(ForecastHungItem::Moon);
+ addStars();
+ } else {
+ setMainHangItem(ForecastHungItem::Sun);
+ }
+ }
+};
+
+class ForecastFlurriesView : public ForecastView
+{
+public:
+ ForecastFlurriesView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Flurries, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(113, 300), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(382, 267), 0, true);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(72, 243), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(349, 226), 2);
+ addSnow(10);
+ }
+};
+
+class ForecastFogView : public ForecastView
+{
+public:
+ ForecastFogView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Fog, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(95, 311), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(363, 236), 2);
+ }
+};
+
+class ForecastHazeView : public ForecastView
+{
+ // haze || sand || dust
+public:
+ ForecastHazeView(Forecast::ForecastType type, bool night, QGraphicsItem *parent = 0)
+ : ForecastView(type, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(98, 311), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(396, 235), 2);
+ }
+};
+
+class ForecastIcyView : public ForecastView
+{
+public:
+ ForecastIcyView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Icy, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(113, 300), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(382, 267), 0, true);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(72, 243), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(349, 226), 2);
+ addSnow(10);
+ }
+};
+
+class ForecastSleetView : public ForecastView
+{
+public:
+ ForecastSleetView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Sleet, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(135, 317), 0);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(341, 313), 1, true);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(72, 276), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(199, 250), 2);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(417, 268), 2);
+ addSnow(10);
+ addRain(ForecastRain::Light);
+ }
+};
+
+class ForecastSnowView : public ForecastView
+{
+public:
+ ForecastSnowView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Snow, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(46, 325), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(359, 302), 0, true);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(158, 288), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(73, 267), 2);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(401, 273), 2);
+ addSnow(20);
+ }
+};
+
+class ForecastMistView : public ForecastView
+{
+public:
+ ForecastMistView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Mist, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(52, 312), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(437, 255), 0);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(246, 311), 1, true);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(119, 268), 2);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(352, 244), 2);
+ addRain(ForecastRain::Light);
+ }
+};
+
+class ForecastRainView : public ForecastView
+{
+public:
+ ForecastRainView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Rain, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(60, 317), 0);
+ addHungItem(ForecastHungItem::CloudRain1, QPointF(308, 287), 0, true);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(192, 251), 1);
+ addHungItem(ForecastHungItem::CloudRain2, QPointF(432, 268), 1);
+ addHungItem(ForecastHungItem::CloudRain3, QPointF(355, 235), 2);
+ addRain(ForecastRain::Medium);
+ }
+};
+
+class ForecastStormView : public ForecastView
+{
+public:
+ ForecastStormView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Storm, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudStorm1, QPointF(324, 296), 0);
+ addHungItem(ForecastHungItem::CloudStorm1, QPointF(91, 261), 0, true);
+ addHungItem(ForecastHungItem::CloudStorm2, QPointF(432, 257), 1);
+ addHungItem(ForecastHungItem::CloudStorm3, QPointF(92, 210), 2);
+ addHungItem(ForecastHungItem::CloudStorm3, QPointF(334, 224), 2);
+ addRain(ForecastRain::Heavy);
+ }
+};
+
+class ForecastThunderstormView : public ForecastView
+{
+public:
+ ForecastThunderstormView(bool night, QGraphicsItem *parent = 0)
+ : ForecastView(Forecast::Thunderstorm, parent)
+ {
+ setMainHangItem(night ? ForecastHungItem::Moon : ForecastHungItem::ColdSun);
+ addHungItem(ForecastHungItem::CloudTStorm1, QPointF(371, 303), 0);
+ addHungItem(ForecastHungItem::CloudStorm1, QPointF(110, 283), 0, true);
+ addHungItem(ForecastHungItem::CloudTStorm2, QPointF(207, 272), 1);
+ addHungItem(ForecastHungItem::CloudStorm2, QPointF(367, 248), 1);
+ addHungItem(ForecastHungItem::CloudStorm3, QPointF(111, 223), 2);
+ addRain(ForecastRain::Heavy);
+ }
+};
+
+static const qreal minHungItemsZ = -1.0;
+static const qreal maxHungItemsZ = 0.0;
+static const qreal sunZ = (minHungItemsZ + maxHungItemsZ) / 2.0 + 0.01;
+static const qreal starsZ = -10.0;
+
+static const qreal radius = (maxHungItemsZ - minHungItemsZ) * 1;
+static const qreal maxDisplacementFactor = 1.5;
+
+// HungItemsManager
+
+static QAbstractAnimation *addPause(QAbstractAnimation *animation, int pause_ms)
+{
+ if (!animation || pause_ms <= 0)
+ return animation;
+ QSequentialAnimationGroup *result = new QSequentialAnimationGroup();
+ result->addPause(pause_ms);
+ result->addAnimation(animation);
+ return result;
+}
+
+void HungItemsManager::reset()
+{
+ for (int i = 0; i < m_items.count(); ++i) {
+ m_items[i]->reset();
+ m_items[i]->setPos(m_positions[i], 0.0);
+ }
+ if (m_sun) {
+ m_sun->reset();
+ m_sun->setPos(m_sunPos, 0.0);
+ }
+}
+
+QAbstractAnimation *HungItemsManager::getAnimation()
+{
+ QList<QAbstractAnimation*> list;
+ const qreal diff = (m_maxZ - m_minZ) / qreal(m_items.count());
+
+ for (int i = 0; i < m_items.count(); ++i) {
+ m_items[i]->setZValue(m_maxZ - i * diff);
+ QAbstractAnimation *animation = m_items[i]->getAnimation();
+ if (animation)
+ list.append(animation);
+ }
+
+ QAbstractAnimation *clouds = 0;
+
+ switch (list.count()) {
+ case 0: break;
+ case 1:
+ clouds = list[0];
+ break;
+ default: {
+ QParallelAnimationGroup *animation = new QParallelAnimationGroup();
+ for (int i = 0; i < list.count(); ++i)
+ animation->addAnimation(addPause(list[i], i * 200));
+ clouds = animation;
+ }
+ }
+
+ QAbstractAnimation *sun = m_sun ? m_sun->getAnimation() : 0;
+
+ if (clouds && m_sun) {
+ QSequentialAnimationGroup *result = new QSequentialAnimationGroup();
+ result->addAnimation(clouds);
+ result->addAnimation(sun);
+ return result;
+ }
+ return clouds ? clouds : sun;
+}
+
+void HungItemsManager::addItem(ForecastHungItem *item)
+{
+ m_items.append(item);
+ m_positions.append(item->pos().x());
+}
+
+void HungItemsManager::setSun(ForecastHungItem *sun)
+{
+ m_sun = sun;
+ m_sunPos = sun->pos().x();
+}
+
+void HungItemsManager::doSetDisplacement(ForecastHungItem *item, qreal max, qreal pos)
+{
+ const qreal itemRadius = radius + item->zValue();
+ const qreal itemDisplacement = (itemRadius / radius) * max * m_displacement;
+ item->setPos(pos + itemDisplacement, 0.0);
+}
+
+void HungItemsManager::setElementsDisplacement(qreal displacement)
+{
+ const qreal max = maxDisplacementFactor * Settings::windowSize().width();
+ m_displacement = qMin(qMax(displacement, qreal(-1.0)), qreal(1.0));
+ for (int i = 0; i < m_items.count(); ++i)
+ doSetDisplacement(m_items[i], max, m_positions[i]);
+ if (m_sun)
+ doSetDisplacement(m_sun, max, m_sunPos);
+}
+
+// ForecastView
+
+void ForecastView::setElementsDisplacement(qreal displacement)
+{
+ m_hungManager.setElementsDisplacement(displacement);
+}
+
+ForecastView *ForecastView::createView(Forecast::ForecastType type, bool night, QGraphicsItem *parent)
+{
+ switch (type) {
+ case Forecast::MostlyCloudy : return new ForecastMostlyCloudyView(night, parent);
+ case Forecast::Cloudy : return new ForecastCloudyView(night, parent);
+ case Forecast::MostlySunny : return new ForecastMostlySunnyView(night, parent);
+ case Forecast::PartlyCloudy : return new ForecastPartlyCloudyView(night, parent);
+ case Forecast::Sunny : return new ForecastSunnyView(night, parent);
+ case Forecast::Flurries : return new ForecastFlurriesView(night, parent);
+ case Forecast::Fog : return new ForecastFogView(night, parent);
+ case Forecast::Haze : return new ForecastHazeView(Forecast::Haze, night, parent);
+ case Forecast::Sand : return new ForecastHazeView(Forecast::Sand, night, parent);
+ case Forecast::Dust : return new ForecastHazeView(Forecast::Dust, night, parent);
+ case Forecast::Icy : return new ForecastIcyView(night, parent);
+ case Forecast::Sleet : return new ForecastSleetView(night, parent);
+ case Forecast::ChanceOfSleet : return new ForecastSleetView(night, parent);
+ case Forecast::Snow : return new ForecastSnowView(night, parent);
+ case Forecast::ChanceOfSnow : return new ForecastSnowView(night, parent);
+ case Forecast::Mist : return new ForecastMistView(night, parent);
+ case Forecast::Rain : return new ForecastRainView(night, parent);
+ case Forecast::ChanceOfRain : return new ForecastRainView(night, parent);
+ case Forecast::Storm : return new ForecastStormView(night, parent);
+ case Forecast::ChanceOfStorm : return new ForecastStormView(night, parent);
+ case Forecast::Thunderstorm : return new ForecastThunderstormView(night, parent);
+ case Forecast::ChanceOfThunderstorm : return new ForecastThunderstormView(night, parent);
+ default : return 0;
+ }
+
+}
+
+ForecastView::ForecastView(Forecast::ForecastType type, bool night, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_type(type)
+ , m_night(night)
+ , m_boundingRect(QPointF(0.0, 0.0), Settings::windowSize())
+ , m_hungManager(minHungItemsZ, maxHungItemsZ)
+ , m_stars(0)
+ , m_reference(0)
+ , m_rain(0)
+ , m_snow(0)
+{
+}
+
+ForecastView::~ForecastView()
+{
+ delete m_snow;
+}
+
+QRectF ForecastView::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void ForecastView::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ if (m_snow)
+ m_snow->paint(painter);
+}
+
+static const qreal cloudAdjustment = 20.0;
+
+void ForecastView::addHungItem(ForecastHungItem::ItemType type, const QPointF &itemPos,
+ int deviation, bool reference)
+{
+ const bool validDev = deviation >= 0 && deviation < variationCount;
+
+ qreal devX = validDev ? variationArray[deviation].x : 0.0;
+ qreal devY = validDev ? variationArray[deviation].y : 0.0;
+
+ devX *= 1.0 - qreal(qrand()) / qreal(RAND_MAX >> 1);
+ devY *= 1.0 - qreal(qrand()) / qreal(RAND_MAX >> 1);
+
+ ForecastHungItem *item = new ForecastHungItem(type, this);
+ item->setLinePos(Settings::scaleWidth(itemPos.x() + devX - cloudAdjustment));
+ item->setPicPos(item->pos().x(), Settings::scaleHeight(itemPos.y() + devY));
+
+ if (reference)
+ m_reference = item;
+
+ m_hungManager.addItem(item);
+}
+
+void ForecastView::reset()
+{
+ m_hungManager.reset();
+ if (m_rain) {
+ m_rain->stop();
+ m_rain->hide();
+ }
+ if (m_snow) {
+ m_snow->stop();
+ m_snow->hide();
+ }
+}
+
+void ForecastView::addStars()
+{
+ if (!m_stars) {
+ m_stars = new ForecastStars(8, this);
+ QRectF rect = m_boundingRect;
+ rect.setHeight(Settings::scaleHeight(522.0));
+ m_stars->setRect(rect);
+ m_stars->setZValue(starsZ);
+ }
+}
+
+QRectF ForecastView::getEffectRect()
+{
+ QRectF result = m_boundingRect;
+ if (m_reference) {
+ QRectF ref(m_reference->boundingRect());
+ result.setTop(ref.bottom());
+ result.setHeight(m_boundingRect.height() - ref.bottom());
+ }
+ return result;
+}
+
+template<class T> QAbstractAnimation *ForecastView::createEffectAnimation(T *effect)
+{
+ effect->setOpacity(0.0);
+ EffectAnimation *result = new EffectAnimation();
+ result->setTargetObject(effect);
+ result->setPropertyName("opacity");
+ result->setStartValue(0.0);
+ result->setEndValue(1.0);
+ result->setDuration(200);
+ result->setEasingCurve(QEasingCurve::InQuart);
+ connect(result, SIGNAL(started()), effect, SLOT(show()));
+ connect(result, SIGNAL(finished()), effect, SLOT(start()));
+ return result;
+}
+
+void ForecastView::addRain(ForecastRain::RainType type)
+{
+ if (!m_rain) {
+ m_rain = new ForecastRain(type, this);
+ m_rain->setPos(getEffectRect().topLeft());
+ m_rain->hide();
+ m_rain->setZValue(-1);
+ }
+}
+
+void ForecastView::addSnow(int count)
+{
+ if (!m_snow) {
+ m_snow = new ForecastSnow(count, getEffectRect(), this);
+ m_snow->hide();
+ }
+}
+
+void ForecastView::setMainHangItem(ForecastHungItem::ItemType type)
+{
+ if (!m_hungManager.sun()) {
+ for (int i = 0; i < sunPositionsArrayCount; ++i) {
+ if (sunPositionsArray[i].type == type ){
+ ForecastHungItem *sun = new ForecastHungItem(type, this);
+ sun->setPicPos(0.0, Settings::scaleHeight(sunPositionsArray[i].picTop));
+ sun->setLinePos(Settings::scaleWidth(sunPositionsArray[i].linePos));
+ sun->setZValue(sunZ);
+ m_hungManager.setSun(sun);
+ break;
+ }
+ }
+ }
+}
+
+QAbstractAnimation *ForecastView::getAnimation()
+{
+ QList<QAbstractAnimation*> list;
+
+ QAbstractAnimation *animation = m_hungManager.getAnimation();
+ if (animation)
+ list.append(animation);
+
+ animation = m_stars ? m_stars->getAnimation() : 0;
+ if (animation)
+ list.append(animation);
+
+ animation = m_rain ? createEffectAnimation(m_rain) : 0;
+ if (animation)
+ list.append(animation);
+
+ QAbstractAnimation *result = 0;
+
+ switch (list.count()) {
+ case 0: break;
+ case 1:
+ result = list[0];
+ break;
+ default: {
+ QSequentialAnimationGroup *group = new QSequentialAnimationGroup();
+ foreach (QAbstractAnimation *a, list)
+ group->addAnimation(a);
+ result = group;
+ }
+ }
+
+ if (result && m_snow)
+ connect(result, SIGNAL(finished()), m_snow, SLOT(start()));
+ else
+ if (m_snow)
+ m_snow->show();
+ return result;
+}
+
+
+
+
diff --git a/weather/src/forecastview.h b/weather/src/forecastview.h
new file mode 100644
index 0000000..4378a59
--- /dev/null
+++ b/weather/src/forecastview.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORECASTVIEW_H
+#define FORECASTVIEW_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QAbstractAnimation>
+
+#include "forecasthungitem.h"
+#include "forecastsnow.h"
+#include "forecastrain.h"
+#include "forecaststars.h"
+#include "forecast.h"
+
+class HungItemsManager
+{
+public:
+ HungItemsManager(qreal minZ, qreal maxZ)
+ : m_minZ(minZ)
+ , m_maxZ(maxZ)
+ , m_displacement(0.0)
+ , m_sun(0)
+ , m_sunPos(0) {}
+ void setSun(ForecastHungItem *sun);
+ ForecastHungItem *sun() { return m_sun; }
+ void addItem(ForecastHungItem *item);
+ QAbstractAnimation *getAnimation();
+ void reset();
+ void setElementsDisplacement(qreal displacement);
+private:
+ const qreal m_minZ;
+ const qreal m_maxZ;
+ qreal m_displacement;
+ ForecastHungItem *m_sun;
+ qreal m_sunPos;
+ QList<ForecastHungItem*> m_items;
+ QList<qreal> m_positions;
+
+ void doSetDisplacement(ForecastHungItem *item, qreal max, qreal pos);
+};
+
+class EffectAnimation : public QPropertyAnimation
+{
+ Q_OBJECT
+public:
+signals:
+ void started();
+protected:
+ void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState)
+ {
+ if (oldState == Stopped && newState == Running)
+ emit started();
+ }
+
+private:
+};
+
+class ForecastView : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+public:
+ ~ForecastView();
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ Forecast::ForecastType viewType() const { return m_type; }
+ bool night() const { return m_night; }
+
+ void setElementsDisplacement(qreal displacement);
+
+ void reset();
+ QAbstractAnimation *getAnimation();
+
+ static ForecastView *createView(Forecast::ForecastType type, bool night, QGraphicsItem *parent = 0);
+
+
+protected:
+ ForecastView(Forecast::ForecastType type, bool night, QGraphicsItem *parent = 0);
+
+ void addHungItem(ForecastHungItem::ItemType type, const QPointF &itemPos,
+ int deviation, bool reference = false);
+ void setMainHangItem(ForecastHungItem::ItemType);
+ void addStars();
+
+ void addSnow(int count);
+ void addRain(ForecastRain::RainType);
+
+private:
+ const Forecast::ForecastType m_type;
+ const bool m_night;
+ const QRectF m_boundingRect;
+ HungItemsManager m_hungManager;
+ ForecastStars *m_stars;
+ ForecastHungItem *m_reference;
+ ForecastRain *m_rain;
+ ForecastSnow *m_snow;
+
+ QRectF getEffectRect();
+ template<class T> QAbstractAnimation *createEffectAnimation(T *effect);
+
+};
+
+#endif // FORECASTVIEW_H
diff --git a/weather/src/gesturebox.cpp b/weather/src/gesturebox.cpp
new file mode 100644
index 0000000..cd348bf
--- /dev/null
+++ b/weather/src/gesturebox.cpp
@@ -0,0 +1,392 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gesturebox.h"
+
+#include <QApplication>
+#include <QGraphicsView>
+#include <QMouseEvent>
+#include <QDebug>
+#include <QPointer>
+#include <QTime>
+
+#include "gesturebox_p.h"
+
+// GestureData
+
+#ifdef QT_ARCH_ARM
+static const int move_threshold = 30;
+#else
+static const int move_threshold = 5;
+#endif
+
+GestureData::GestureData(QPoint pos, const QList<GestureBox*> &boxes,
+ QGraphicsView *view, QList<QEvent*> &ignoreList, bool sendClickEvents)
+ : m_state(Pressed)
+ , m_view(view)
+ , m_pressPos(pos)
+ , m_scenePressPos(m_view->mapToScene(m_pressPos))
+ , m_boxes(boxes)
+ , m_ignoreList(ignoreList)
+ , m_time(QTime::currentTime())
+ , m_currentScenePos(m_scenePressPos)
+ , m_speed(0.0, 0.0)
+ , m_sendClickEvents(sendClickEvents)
+{
+}
+
+void GestureData::sendClick()
+{
+ if (m_sendClickEvents) {
+ QMouseEvent *press = new QMouseEvent(QEvent::MouseButtonPress,
+ m_pressPos, Qt::LeftButton,
+ Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent *release = new QMouseEvent(QEvent::MouseButtonRelease,
+ m_pressPos, Qt::LeftButton,
+ Qt::LeftButton, Qt::NoModifier);
+ m_ignoreList << press;
+ m_ignoreList << release;
+ QApplication::postEvent(m_view->viewport(), press);
+ QApplication::postEvent(m_view->viewport(), release);
+ }
+}
+
+void GestureData::release(QPoint pos)
+{
+ Q_UNUSED(pos);
+ switch (m_state) {
+ case Pressed:
+ sendClick();
+ break;
+ case Moving:
+ foreach(GestureBox *box, m_boxes)
+ box->gestureEnd(box->mapFromScene(m_currentScenePos), m_speed);
+ break;
+ default:
+ break;
+ }
+ m_state = Ended;
+ deleteLater();
+}
+
+void GestureData::move(QPoint pos)
+{
+ if (m_state == Pressed &&
+ abs(pos.x() - m_pressPos.x()) < move_threshold &&
+ abs(pos.y() - m_pressPos.y()) < move_threshold) {
+ return;
+ }
+ QTime time(QTime::currentTime());
+ QPointF scenePos = m_view->mapToScene(pos);
+ QPointF move = scenePos - m_currentScenePos;
+ qreal secs = (qreal)(m_time.msecsTo(time)) / 1000;
+ m_currentScenePos = scenePos;
+ m_speed = QPointF(move.x() / secs, move.y() / secs);
+ m_time = time;
+
+ switch (m_state) {
+ case Pressed:
+ foreach(GestureBox *box, m_boxes) {
+ box->gestureStart(box->mapFromScene(m_scenePressPos));
+ box->gestureMove(box->mapFromScene(m_currentScenePos), move, m_speed);
+ }
+ m_state = Moving;
+ break;
+ case Moving:
+ foreach(GestureBox *box, m_boxes)
+ box->gestureMove(box->mapFromScene(m_currentScenePos), move, m_speed);
+ break;
+ default:
+ break;
+ }
+}
+
+// ViewData
+
+ViewData::~ViewData()
+{
+ if (m_gesture)
+ m_gesture->deleteLater();
+}
+
+bool ViewData::ignoreEvent(QEvent *event)
+{
+ return m_ignored.removeAll(event);
+}
+
+bool ViewData::startGesture(GestureBox *box, QPointF scenePos, bool &sendClick)
+{
+ bool result = true;
+ bool click = true;
+ box->gestureMousePress(box->mapFromScene(scenePos), result, click);
+ if (result && !click)
+ sendClick = false;
+ return result;
+}
+
+bool ViewData::press(QPoint pos)
+{
+ if (m_gesture && !m_gesture->ended()) {
+ qWarning() << "ERROR: unexpected mouse press during gesture.";
+ return false;
+ }
+
+ if (!m_view || !m_view->scene())
+ return false;
+
+ QList<GestureBox*> boxes;
+ bool sendClick = true;
+ QPointF scenePos = m_view->mapToScene(pos);
+
+ QList<QGraphicsItem*> itemList = m_view->scene()->items(scenePos,
+ Qt::IntersectsItemBoundingRect,
+ Qt::AscendingOrder);
+
+ foreach(QGraphicsItem *item, itemList) {
+ GestureBox *box = dynamic_cast<GestureBox*>(item);
+ if (box && startGesture(box, scenePos, sendClick))
+ boxes.append(box);
+ }
+
+ if (boxes.isEmpty())
+ return false;
+
+ m_gesture = new GestureData(pos, boxes, m_view, m_ignored, sendClick);
+ return true;
+
+}
+
+bool ViewData::release(QPoint pos)
+{
+ if (!m_gesture || m_gesture->ended())
+ return false;
+
+ m_gesture->release(pos);
+ return true;
+}
+
+bool ViewData::move(QPoint pos)
+{
+ if (!m_gesture || m_gesture->ended())
+ return false;
+
+ m_gesture->move(pos);
+ return true;
+}
+
+QList<GestureBox*> ViewData::getBoxList(QPoint pos) {
+ QList<GestureBox*> result;
+ if (m_view->scene()) {
+ QPointF scenePos = m_view->mapToScene(pos);
+ QGraphicsItem *item = m_view->scene()->itemAt(scenePos);
+ while (item) {
+ GestureBox *box = dynamic_cast<GestureBox*>(item);
+ if (box && box->boundingRect().contains(box->mapFromScene(scenePos)))
+ result.append(box);
+ item = item->parentItem();
+ }
+ }
+ return result;
+}
+
+// GestureObserver
+
+GestureObserver *GestureObserver::instance()
+{
+ static GestureObserver *obj = new GestureObserver();
+ return obj;
+}
+
+void GestureObserver::addBox(GestureBox *box)
+{
+ if (box->scene()) {
+ foreach(QGraphicsView *view, box->scene()->views()) {
+ ViewData *data = getViewData(view);
+ if (data->m_boxes.indexOf(box) < 0) {
+ data->m_boxes.append(box);
+ m_boxes.insertMulti(box, data);
+ }
+ }
+ }
+}
+
+void GestureObserver::removeBox(GestureBox *box)
+{
+ QList<ViewData*> dataList = m_boxes.values(box);
+ foreach(ViewData* data, dataList) {
+ data->m_boxes.removeAll(box);
+ if (data->m_boxes.count() == 0) {
+ m_views.remove(data->view()->viewport());
+ data->view()->viewport()->removeEventFilter(this);
+ delete data;
+ }
+ }
+ m_boxes.remove(box);
+}
+
+
+ViewData *GestureObserver::getViewData(QGraphicsView *view)
+{
+ if (m_views.contains(view->viewport()))
+ return m_views.value(view->viewport());
+ ViewData *data = new ViewData(view);
+ view->viewport()->installEventFilter(this);
+ connect(view->viewport(), SIGNAL(destroyed()), this, SLOT(viewDestroyed()));
+ m_views.insert(view->viewport(), data);
+ return data;
+}
+
+void GestureObserver::viewDestroyed()
+{
+ if (!sender()->isWidgetType())
+ return;
+ QWidget *viewport = qobject_cast<QWidget*>(sender());
+ if (viewport || m_views.contains(viewport)) {
+ ViewData *data = m_views.take(viewport);
+ if (data) {
+ foreach (GestureBox *box, data->m_boxes) {
+ QList<ViewData*> boxes = m_boxes.values(box);
+ m_boxes.remove(box);
+
+ foreach(ViewData *viewData, boxes) {
+ if (viewData->widget() != viewport)
+ m_boxes.insertMulti(box, viewData);
+ }
+ }
+ delete data;
+ }
+ }
+
+}
+
+bool GestureObserver::eventFilter(QObject *object, QEvent *event)
+{
+ if (!object->isWidgetType())
+ return false;
+
+ QEvent::Type type = event->type();
+ if (type != QEvent::MouseButtonPress && type != QEvent::MouseButtonRelease &&
+ type != QEvent::MouseMove)
+ return false;
+
+ QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
+ if (!mouseEvent || mouseEvent->modifiers() != Qt::NoModifier)
+ return false;
+
+ QWidget *view = dynamic_cast<QWidget*>(object);
+ if (!view || !m_views.contains(view))
+ return false;
+
+ ViewData *data = m_views[view];
+ if (!data || data->ignoreEvent(event))
+ return false;
+
+ bool consumed = false;
+
+ switch (mouseEvent->type()) {
+ case QEvent::MouseButtonPress:
+ consumed = mouseEvent->buttons() == Qt::LeftButton && data->press(mouseEvent->pos());
+ break;
+ case QEvent::MouseButtonRelease:
+ consumed = data->release(mouseEvent->pos());
+ break;
+ case QEvent::MouseMove:
+ consumed = data->move(mouseEvent->pos());
+ break;
+ default:
+ break;
+ }
+ return consumed;
+}
+
+// GestureBox
+
+GestureBox::GestureBox(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+{
+ GestureObserver::instance()->addBox(this);
+}
+
+GestureBox::~GestureBox()
+{
+ GestureObserver::instance()->removeBox(this);
+}
+
+QRectF GestureBox::rect() const
+{
+ QRectF result(m_boundingRect);
+ result.moveTo(pos());
+ return result;
+}
+
+void GestureBox::setRect(QRectF value)
+{
+ setPos(value.topLeft());
+ value.moveTo(0.0, 0.0);
+ if (value != m_boundingRect) {
+ prepareGeometryChange();
+ m_boundingRect = value;
+ update();
+ }
+}
+
+void GestureBox::setRect(qreal x, qreal y, qreal width, qreal height)
+{
+ setRect(QRectF(x, y, width, height));
+}
+
+QRectF GestureBox::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void GestureBox::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter);
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+}
+
+QVariant GestureBox::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ switch (change) {
+ case QGraphicsItem::ItemSceneHasChanged:
+ case QGraphicsItem::ItemParentHasChanged:
+ GestureObserver::instance()->addBox(this);
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+
diff --git a/weather/src/gesturebox.h b/weather/src/gesturebox.h
new file mode 100644
index 0000000..a754105
--- /dev/null
+++ b/weather/src/gesturebox.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GESTUREBOX_H
+#define GESTUREBOX_H
+
+#include <QGraphicsItem>
+
+class GestureBox : public QGraphicsItem
+{
+public:
+ GestureBox(QGraphicsItem *parent = 0);
+ ~GestureBox();
+
+ QRectF rect() const;
+ void setRect(QRectF);
+ void setRect(qreal, qreal, qreal, qreal);
+ QRectF boundingRect() const;
+
+protected:
+ virtual void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick) = 0;
+ virtual void gestureStart(QPointF pos) = 0;
+ virtual void gestureMove(QPointF pos, QPointF movement, QPointF speed) = 0;
+ virtual void gestureEnd(QPointF pos, QPointF speed) = 0;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value);
+
+private:
+ friend class GestureData;
+ friend class ViewData;
+ QRectF m_boundingRect;
+
+};
+
+#endif // GESTUREBOX_H
diff --git a/weather/src/gesturebox_p.h b/weather/src/gesturebox_p.h
new file mode 100644
index 0000000..0daa89e
--- /dev/null
+++ b/weather/src/gesturebox_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GESTUREBOX_P_H
+#define GESTUREBOX_P_H
+
+#include <QObject>
+#include <QPointer>
+#include <QTime>
+#include <QPoint>
+#include <QPointF>
+#include <QGraphicsView>
+
+#include "gesturebox.h"
+
+class GestureData : public QObject
+{
+public:
+ inline GestureData(QPoint pos, const QList<GestureBox*> &boxes, QGraphicsView *view,
+ QList<QEvent*> &ignoreList, bool sendClickEvents);
+
+ inline void release(QPoint pos);
+ inline void move(QPoint pos);
+ inline bool ended() const { return m_state == Ended; }
+
+private:
+ enum State {Pressed, Moving, Ended};
+ State m_state;
+ QGraphicsView * const m_view;
+ const QPoint m_pressPos;
+ const QPointF m_scenePressPos;
+ const QList<GestureBox*> m_boxes;
+ QList<QEvent*> &m_ignoreList;
+ QTime m_time;
+ QPointF m_currentScenePos;
+ QPointF m_speed;
+ bool m_sendClickEvents;
+
+ void sendClick();
+};
+
+class ViewData
+{
+public:
+ ViewData(QGraphicsView *view) : m_view(view), m_gesture(0), m_widget(view->viewport()) {}
+ inline ~ViewData();
+
+ inline bool press(QPoint pos);
+ inline bool release(QPoint pos);
+ inline bool move(QPoint pos);
+
+ QGraphicsView *view() { return m_view; }
+ QWidget *widget() { return m_widget; }
+ inline bool ignoreEvent(QEvent *event);
+
+ QList<GestureBox*> m_boxes;
+
+private:
+ QGraphicsView *const m_view;
+ QPointer<GestureData> m_gesture;
+ QList<QEvent*> m_ignored;
+ QWidget * const m_widget;
+
+ inline QList<GestureBox*> getBoxList(QPoint pos);
+ inline bool startGesture(GestureBox *box, QPointF scenePos, bool &sendClick);
+};
+
+class GestureObserver : public QObject
+{
+ Q_OBJECT
+public:
+ static inline GestureObserver *instance();
+ inline void addBox(GestureBox *box);
+ inline void removeBox(GestureBox *box);
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event);
+
+private:
+ QHash<QWidget*, ViewData*> m_views;
+ QHash<GestureBox*, ViewData*> m_boxes;
+
+ GestureObserver() {}
+ ~GestureObserver() {}
+ ViewData *getViewData(QGraphicsView *view);
+
+private slots:
+ void viewDestroyed();
+
+};
+
+#endif // GESTUREBOX_P_H
diff --git a/weather/src/loading.cpp b/weather/src/loading.cpp
new file mode 100644
index 0000000..02750f9
--- /dev/null
+++ b/weather/src/loading.cpp
@@ -0,0 +1,77 @@
+#include "loading.h"
+#include "pixmaploader.h"
+
+// Loading
+
+struct ProgressImageData
+{
+ const char * const name;
+ QPixmap pic() const { return PixmapLoader::getPic(name); }
+ QRectF getRect()
+ {
+ QPixmap pixmap = pic();
+ return QRectF(0.0, 0.0, pixmap.width(), pixmap.height());
+ }
+};
+
+static const int ProgressImageCount = 25;
+static ProgressImageData ProgressImageArray[ProgressImageCount] = {
+ {"loading_1"},
+ {"loading_2"},
+ {"loading_3"},
+ {"loading_4"},
+ {"loading_5"},
+ {"loading_6"},
+ {"loading_7"},
+ {"loading_8"},
+ {"loading_9"},
+ {"loading_10"},
+ {"loading_11"},
+ {"loading_12"},
+ {"loading_13"},
+ {"loading_14"},
+ {"loading_15"},
+ {"loading_16"},
+ {"loading_17"},
+ {"loading_18"},
+ {"loading_19"},
+ {"loading_20"},
+ {"loading_21"},
+ {"loading_22"},
+ {"loading_23"},
+ {"loading_24"},
+ {"loading_25"}
+};
+
+void Loading::loadImages()
+{
+ for (int i = 0; i < ProgressImageCount; ++i)
+ PixmapLoader::load(ProgressImageArray[i].name);
+}
+
+Loading::Loading(QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_image(0)
+ , m_boundingRect(ProgressImageArray[m_image].getRect())
+{
+}
+
+QRectF Loading::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void Loading::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ painter->drawPixmap(0, 0, ProgressImageArray[m_image].pic());
+}
+
+void Loading::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ ++m_image;
+ m_image %= ProgressImageCount;
+ update();
+}
diff --git a/weather/src/loading.h b/weather/src/loading.h
new file mode 100644
index 0000000..2735b28
--- /dev/null
+++ b/weather/src/loading.h
@@ -0,0 +1,33 @@
+#ifndef LOADING_H
+#define LOADING_H
+
+#include <QObject>
+#include <QGraphicsItem>
+#include <QBasicTimer>
+#include <QPainter>
+
+class Loading : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+public:
+ Loading(QGraphicsItem *parent = 0);
+
+ static void loadImages();
+
+ QRectF boundingRect () const;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+
+ void start() { m_ticker.start(50, this); }
+ void stop() { m_ticker.stop(); }
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ int m_image;
+ const QRectF m_boundingRect;
+ QBasicTimer m_ticker;
+};
+
+#endif // LOADING_H
diff --git a/weather/src/main.cpp b/weather/src/main.cpp
new file mode 100644
index 0000000..b2681a9
--- /dev/null
+++ b/weather/src/main.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsView>
+#include <QPixmapCache>
+#include <QSettings>
+#include <QDebug>
+
+#include "system.h"
+#include "mainview.h"
+#include "forecastdata.h"
+#include "settings.h"
+
+#include "forecastprovider.h"
+#include "demoforecastsource.h"
+#include "networkforecastsource.h"
+
+#if defined (Q_OS_SYMBIAN)
+#include "symbiannetwork.h"
+#endif
+
+
+int main(int argc, char **argv)
+{
+ qRegisterMetaType<ForecastData>("ForecastData");
+
+ QApplication app(argc, argv);
+ QCoreApplication::setOrganizationName("openBossa");
+ QCoreApplication::setOrganizationDomain("openbossa.org");
+ QCoreApplication::setApplicationName("weather");
+
+#ifdef Q_OS_SYMBIAN
+ const bool connected = connect();
+#else
+ const bool connected = true;
+#endif
+
+ if (connected)
+ ForecastProvider::setForecastSource(new NetworkForecastSource());
+ else
+ ForecastProvider::setForecastSource(new DemoForecastSource(1500));
+
+ MainView mainView(connected);
+
+ System::setViewMode(&mainView, System::PortraitMode);
+
+#if defined(Q_OS_SYMBIAN) || defined(Q_WS_MAEMO_5)
+ mainView.showFullScreen();
+#else
+ mainView.show();
+#endif
+
+ return app.exec();
+}
diff --git a/weather/src/mainview.cpp b/weather/src/mainview.cpp
new file mode 100644
index 0000000..5f9b5d3
--- /dev/null
+++ b/weather/src/mainview.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainview.h"
+#include "settings.h"
+
+#include "pixmaploader.h"
+
+// MainView
+
+MainView::MainView(bool connected, QWidget *parent)
+ : QGraphicsView(parent)
+ , m_carroussel(0)
+ , m_loading(0)
+ , m_bootManager(0)
+ , m_manager(0)
+ , m_fakeContentScreen(0)
+ , m_connected(connected)
+{
+ setScene(&m_scene);
+ setFrameShape(QFrame::NoFrame);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
+ QPainter::TextAntialiasing);
+ setWindowTitle("Weather");
+ m_scene.setSceneRect(QRectF(QPointF(0.0, 0.0), Settings::windowSize()));
+ resize(Settings::windowSize());
+ m_scene.setBackgroundBrush(Qt::black);
+
+ PixmapLoader::connectToOnIdleSignal(this, SLOT(pixmapLoaderIsIdle()));
+ Loading::loadImages();
+ if (!m_connected)
+ FakeContentScreen::loadImages();
+}
+
+MainView::~MainView()
+{
+}
+
+void MainView::pixmapLoaderIsIdle()
+{
+ PixmapLoader::disconnectReceiver(this);
+ if (m_connected)
+ startBoot();
+ else {
+ m_fakeContentScreen = new FakeContentScreen();
+ connect(m_fakeContentScreen, SIGNAL(userAccepted()), this, SLOT(startBoot()));
+ m_fakeContentScreen->setPos(0.0, 0.0);
+ m_scene.addItem(m_fakeContentScreen);
+ }
+}
+
+void MainView::startBoot()
+{
+ delete m_fakeContentScreen;
+ m_fakeContentScreen = 0;
+ m_loading = new Loading();
+ m_loading->setPos((geometry().width() - m_loading->boundingRect().width()) / 2,
+ (geometry().height() - m_loading->boundingRect().height()) / 2);
+ m_scene.addItem(m_loading);
+ m_loading->start();
+ m_bootManager = new BootManager(this);
+ connect(m_bootManager, SIGNAL(ready()), this, SLOT(bootEnd()));
+ m_bootManager->run(m_connected ? Settings::getCurrentCities() : Settings::getDemoCities());
+}
+
+void MainView::bootEnd()
+{
+ m_loading->deleteLater();
+ m_loading = 0;
+ showCarroussel();
+ createCityManager();
+ m_bootManager->deleteLater();
+ m_bootManager = 0;
+}
+
+void MainView::showCarroussel()
+{
+ QList<ForecastData> data = m_bootManager->data();
+ m_carroussel = new CityCarroussel();
+ connect(m_carroussel, SIGNAL(cityNameClicked()), this, SLOT(cityNameClicked()));
+
+ connect(this, SIGNAL(moveLeft()), m_carroussel, SLOT(moveLeft()));
+ connect(this, SIGNAL(moveRight()), m_carroussel, SLOT(moveRight()));
+
+ m_carroussel->update(data);
+ m_carroussel->setPos(0.0, 0.0);
+ m_carroussel->setZValue(0.0);
+ m_scene.addItem(m_carroussel);
+}
+
+void MainView::createCityManager()
+{
+ m_manager = new CityManager(m_bootManager->data());
+ m_manager->setPos(0.0, 0.0);
+ m_manager->setZValue(1.0);
+ m_manager->hide();
+
+}
+
+void MainView::cityNameClicked()
+{
+ if (!m_manager)
+ return;
+ m_carroussel->setActive(false);
+ connect(m_manager, SIGNAL(terminated()), this, SLOT(closeCityManager()));
+ m_scene.addItem(m_manager);
+ m_manager->show();
+ m_manager->showManager(m_carroussel->selected());
+}
+
+void MainView::closeCityManager()
+{
+ if (!m_manager)
+ return;
+ m_manager->hide();
+ m_scene.removeItem(m_manager);
+ disconnect(m_manager, SIGNAL(terminated()), this, SLOT(closeCityManager()));
+ saveManagerList();
+ m_carroussel->update(m_manager->forecastList());
+ m_carroussel->setActive(true);
+}
+
+void MainView::saveManagerList()
+{
+ if (m_connected) {
+ QStringList list;
+ foreach(const ForecastData &data, m_manager->forecastList())
+ list.append(data.key());
+ Settings::setCurrentCities(list);
+ }
+}
+
+void MainView::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Left)
+ emit moveLeft();
+ if (event->key() == Qt::Key_Right)
+ emit moveRight();
+ QGraphicsView::keyPressEvent(event);
+}
diff --git a/weather/src/mainview.h b/weather/src/mainview.h
new file mode 100644
index 0000000..fc54837
--- /dev/null
+++ b/weather/src/mainview.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINVIEW_H
+#define MAINVIEW_H
+
+#include <QGraphicsView>
+#include <QKeyEvent>
+#include <QGraphicsScene>
+
+#include "titlebar.h"
+#include "citycarroussel.h"
+#include "forecastdata.h"
+#include "bootmanager.h"
+#include "loading.h"
+#include "citymanager.h"
+#include "fakecontentscreen.h"
+
+class MainView: public QGraphicsView
+{
+ Q_OBJECT
+
+public:
+ MainView(bool connected, QWidget *parent = 0);
+ ~MainView();
+
+protected:
+ void keyPressEvent(QKeyEvent* event);
+
+signals:
+ void moveLeft();
+ void moveRight();
+
+private slots:
+ void cityNameClicked();
+ void closeCityManager();
+
+ void pixmapLoaderIsIdle();
+ void bootEnd();
+ void startBoot();
+
+private:
+ QGraphicsScene m_scene;
+ CityCarroussel *m_carroussel;
+ Loading *m_loading;
+ BootManager *m_bootManager;
+ CityManager *m_manager;
+ FakeContentScreen *m_fakeContentScreen;
+ const bool m_connected;
+
+ void showCarroussel();
+ void createCityManager();
+ void saveManagerList();
+};
+
+#endif // MAINVIEW_H
+
diff --git a/weather/src/networkforecastsource.cpp b/weather/src/networkforecastsource.cpp
new file mode 100644
index 0000000..98456c8
--- /dev/null
+++ b/weather/src/networkforecastsource.cpp
@@ -0,0 +1,234 @@
+#include "networkforecastsource.h"
+#include "yahooweatherresponse.h"
+#include "xoapweatherresponse.h"
+
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QDebug>
+#include <QBuffer>
+#include <QDomDocument>
+#include <QDateTime>
+#include <QTime>
+
+#define CITY_NAME_HOST "xoap.weather.com"
+#define CITY_NAME_TO_LOCATION_ID_URL "http://" CITY_NAME_HOST "/search/search"
+#define CITY_NAME_TO_LOCATION_ID_PARAM "where"
+
+#define FORECAST_HOST "weather.yahooapis.com"
+#define FORECAST_URL "http://" FORECAST_HOST "/forecastrss"
+#define FORECAST_LOCATION_PARAM "p"
+#define FORECAST_UNIT_PARAM "u"
+#define FORECAST_CELSIUS_UNIT "c"
+
+#define YWEATHER_NS_URI "http://xml.weather.yahoo.com/ns/rss/1.0"
+#define YWEATHER_NS_NAME "yweather"
+
+static int getId()
+{
+ static int id(0);
+ return ++id;
+}
+
+// LocationRequestManager
+
+LocationRequestManager::LocationRequestManager(QObject *parent)
+ : QObject(parent)
+{
+ connect(&m_network, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(receiveResponse(QNetworkReply*)));
+}
+
+int LocationRequestManager::addRequest(const QString &query)
+{
+ if (m_requests.contains(query))
+ return m_requests[query];
+ QUrl url(CITY_NAME_TO_LOCATION_ID_URL);
+ url.addQueryItem(CITY_NAME_TO_LOCATION_ID_PARAM, query);
+ m_network.get(QNetworkRequest(url));
+
+ int result = getId();
+ m_requests[query] = result;
+ return result;
+}
+
+QString LocationRequestManager::readResponse(const QString &query, QNetworkReply *reply)
+{
+
+ if (reply->error() != QNetworkReply::NoError) {
+ qWarning() << "location id query error: " << query;
+ return QString();
+ }
+
+ QDomDocument doc;
+
+ if (!doc.setContent(reply)) {
+ qWarning() << "location id parse error: " << query;
+ return QString();
+ }
+ XoapWeatherResponse reader;
+ reader.read(doc.documentElement());
+
+ if (reader.items().count() == 0) {
+ qWarning() << "empty location id response: " << query;
+ return QString();
+ }
+
+ return reader.items()[0].id();
+
+}
+
+void LocationRequestManager::receiveResponse(QNetworkReply *reply)
+{
+ const QString query = reply->request().url().queryItemValue(CITY_NAME_TO_LOCATION_ID_PARAM);
+
+ if (query.isNull() || !m_requests.contains(query)) {
+ qWarning() << "Unexpected location id query response. (query = " << query << ")";
+ return;
+ }
+ const int reqId = m_requests[query];
+ m_requests.remove(query);
+
+ QString locId = readResponse(query, reply);
+
+ if (locId.isEmpty())
+ emit locationIdQueryError(reqId, query);
+ else
+ emit newLocationId(reqId, locId);
+}
+
+// ForecastRequestmanager
+
+ForecastRequestmanager::ForecastRequestmanager(QObject *parent)
+ : QObject(parent)
+{
+ connect(&m_network, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(receiveResponse(QNetworkReply*)));
+}
+
+int ForecastRequestmanager::addRequest(const QString &locId)
+{
+ if (m_requests.contains(locId))
+ return m_requests[locId][0];
+ int result = getId();
+ doAddRequest(result, locId);
+ return result;
+}
+
+void ForecastRequestmanager::addRequest(int reqId, const QString &locId)
+{
+ if (m_requests.contains(locId)) {
+ m_requests[locId].append(reqId);
+ return;
+ }
+ doAddRequest(reqId, locId);
+}
+
+void ForecastRequestmanager::doAddRequest(int reqId, const QString &locId)
+{
+ QUrl url(FORECAST_URL);
+ url.addQueryItem(FORECAST_LOCATION_PARAM, locId);
+ url.addQueryItem(FORECAST_UNIT_PARAM, FORECAST_CELSIUS_UNIT);
+ m_network.get(QNetworkRequest (url));
+ QList<int> list;
+ list.append(reqId);
+ m_requests[locId] = list;
+}
+
+YahooWeatherResponse *ForecastRequestmanager::readResponse(const QString &locId,
+ QNetworkReply *reply)
+{
+ if (reply->error() != QNetworkReply::NoError) {
+ qWarning() << "forecast query error: " << locId;
+ return 0;
+ }
+
+ QDomDocument doc;
+
+ if (!doc.setContent(reply)) {
+ qWarning() << "forecast parse error: " << locId;
+ return 0;
+ }
+
+ bool ok = doc.documentElement().childNodes().count() > 0
+ && doc.documentElement().childNodes().at(0).isElement();
+ if (ok) {
+ QDomElement element = doc.documentElement().childNodes().at(0).toElement();
+ ok = element.elementsByTagName("lastBuildDate").count() == 1;
+ }
+
+ if (!ok) {
+ qWarning() << "invalid forecast response: " << locId;
+ return 0;
+ }
+
+ return new YahooWeatherResponse(locId, doc.documentElement());
+}
+
+void ForecastRequestmanager::receiveResponse(QNetworkReply *reply)
+{
+ const QString locId = reply->request().url().queryItemValue(FORECAST_LOCATION_PARAM);
+
+ if (locId.isNull() || !m_requests.contains(locId)) {
+ qWarning() << "Unexpected forecast query response. (locId = " << locId << ")";
+ return;
+ }
+
+ const QList<int> list = m_requests[locId];
+ m_requests.remove(locId);
+
+ YahooWeatherResponse *result = readResponse(locId, reply);
+ if (result)
+ foreach (int reqId, list) {
+ emit newForecastResponse(reqId, result);
+ }
+ else
+ foreach (int reqId, list)
+ emit forecastResponseError(reqId);
+}
+
+// NetworkForecastSource
+
+NetworkForecastSource::NetworkForecastSource(QObject *parent)
+ : ForecastSource(parent)
+{
+ connect(&m_locationManager, SIGNAL(newLocationId(int, QString)),
+ &m_forecastManager, SLOT(addRequest(int, QString)));
+
+ connect(&m_locationManager, SIGNAL(locationIdQueryError(int, QString)),
+ &m_forecastManager, SLOT(addRequest(int, QString)));
+
+ connect(&m_forecastManager, SIGNAL(forecastResponseError(int)),
+ this, SLOT(forecastResponseError(int)));
+
+ connect(&m_forecastManager, SIGNAL(newForecastResponse(int, YahooWeatherResponse*)),
+ this, SLOT(newForecastResponse(int, YahooWeatherResponse*)));
+}
+
+NetworkForecastSource::~NetworkForecastSource()
+{
+}
+
+int NetworkForecastSource::getForecast(const QString &key, bool locationId)
+{
+ if (locationId)
+ return m_forecastManager.addRequest(key);
+ else
+ return m_locationManager.addRequest(key);
+}
+
+void NetworkForecastSource::newForecastResponse(int reqId, YahooWeatherResponse *forecast)
+{
+ forecast->print();
+ emit forecastReceived(reqId, ForecastData(forecast));
+}
+
+void NetworkForecastSource::forecastResponseError(int reqId)
+{
+ emit forecastReceived(reqId, ForecastData(0));
+}
+
+
+
+
+
diff --git a/weather/src/networkforecastsource.h b/weather/src/networkforecastsource.h
new file mode 100644
index 0000000..49f26c5
--- /dev/null
+++ b/weather/src/networkforecastsource.h
@@ -0,0 +1,75 @@
+#ifndef NETWORKFORECASTSOURCE_H
+#define NETWORKFORECASTSOURCE_H
+
+#include "forecastdata.h"
+#include "forecastsource.h"
+#include <QObject>
+#include <QMap>
+#include <QStringList>
+#include <QNetworkAccessManager>
+
+class LocationRequestManager : public QObject
+{
+ Q_OBJECT
+public:
+ LocationRequestManager(QObject *parent = 0);
+ int addRequest(const QString &query);
+
+signals:
+ void newLocationId(int reqId, const QString &locId);
+ void locationIdQueryError(int reqId, const QString &query);
+
+private slots:
+ void receiveResponse(QNetworkReply *reply);
+
+private:
+ QMap<QString, int> m_requests;
+ QNetworkAccessManager m_network;
+
+ QString readResponse(const QString &query, QNetworkReply *reply);
+};
+
+class ForecastRequestmanager : public QObject
+{
+ Q_OBJECT
+public:
+ ForecastRequestmanager(QObject *parent = 0);
+
+ int addRequest(const QString &locId);
+
+public slots:
+ void addRequest(int reqId, const QString &locId);
+
+signals:
+ void newForecastResponse(int reqId, YahooWeatherResponse *forecast);
+ void forecastResponseError(int reqId);
+
+private slots:
+ void receiveResponse(QNetworkReply *reply);
+
+private:
+ QMap<QString, QList<int> > m_requests;
+ QNetworkAccessManager m_network;
+
+ YahooWeatherResponse *readResponse(const QString &query, QNetworkReply *locId);
+ void doAddRequest(int reqId, const QString &locId);
+};
+
+class NetworkForecastSource : public ForecastSource
+{
+ Q_OBJECT
+public:
+ NetworkForecastSource(QObject *parent = 0);
+ ~NetworkForecastSource();
+ int getForecast(const QString &key, bool locationId);
+
+private:
+ LocationRequestManager m_locationManager;
+ ForecastRequestmanager m_forecastManager;
+
+private slots:
+ void newForecastResponse(int reqId, YahooWeatherResponse *forecast);
+ void forecastResponseError(int reqId);
+};
+
+#endif // NETWORKFORECASTSOURCE_H
diff --git a/weather/src/painttextitem.cpp b/weather/src/painttextitem.cpp
new file mode 100644
index 0000000..7356815
--- /dev/null
+++ b/weather/src/painttextitem.cpp
@@ -0,0 +1,67 @@
+#include "painttextitem.h"
+#include <QPainter>
+#include <QDebug>
+
+TextPainter::TextPainter(int fontSize, QColor color, const QString &text)
+ : m_pen(color)
+ , m_brush(color)
+ , m_text(text)
+ , m_quoted(false)
+ , m_maxWidth(-1)
+{
+ m_font.setFamily("Nokia Sans");
+ m_font.setPixelSize(fontSize);
+ m_font.setStyleStrategy(QFont::PreferAntialias);
+ m_pen.setJoinStyle(Qt::RoundJoin);
+}
+
+int TextPainter::width() const
+{
+ QFontMetrics m(m_font);
+ int result = 0;
+ if (m_quoted)
+ result += 2 * m.width('"');
+ QString text = m_maxWidth > 0 ? m.elidedText(m_text, Qt::ElideRight, m_maxWidth - result)
+ : m_text;
+ return result + m.width(text);
+}
+
+void TextPainter::paint(QPainter *painter)
+{
+ QFontMetrics m(m_font);
+ QString text;
+ if (m_quoted) {
+ int quote = 2 * m.width('"');
+ text = m_maxWidth > 0 ? m.elidedText(m_text, Qt::ElideRight, m_maxWidth - quote) : m_text;
+ text = '"' + text + '"';
+ } else
+ text = m_maxWidth > 0 ? m.elidedText(m_text, Qt::ElideRight, m_maxWidth) : m_text;
+ painter->setFont(m_font);
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+
+ painter->drawText(QPointF(m_pos.x(), m_pos.y() + m.ascent()), text);
+}
+
+void TextPainter::locateAtCenter(TextPainter *item, qreal left, qreal top, qreal width)
+{
+ item->setPos(left + (width - item->width()) / 2, top);
+}
+
+void TextPainter::locateAtCenter(QList<TextPainter*> items, qreal left, qreal top, qreal width)
+{
+ static const int margin = 5;
+
+ if (items.isEmpty())
+ return;
+
+ int itemsWidth = (items.count() - 1) * margin;
+ for (int i = 0; i < items.count(); ++i)
+ itemsWidth += items[i]->width();
+
+ left += (width - itemsWidth) / 2;
+ for (int i = 0; i < items.count(); ++i) {
+ items[i]->setPos(left, top);
+ left += items[i]->width() + margin;
+ }
+}
diff --git a/weather/src/painttextitem.h b/weather/src/painttextitem.h
new file mode 100644
index 0000000..9811226
--- /dev/null
+++ b/weather/src/painttextitem.h
@@ -0,0 +1,49 @@
+#ifndef PAINTTEXTITEM_H
+#define PAINTTEXTITEM_H
+
+#include <QFont>
+#include <QPen>
+#include <QBrush>
+#include <QFontMetrics>
+#include <QGraphicsItem>
+
+class TextPainter
+{
+public:
+ TextPainter(int fontSize, QColor color, const QString &text);
+ QFont &font() { return m_font; }
+ QPen &pen() { return m_pen; }
+ QBrush &brush() { return m_brush; }
+
+ const QString text() const { return m_text; }
+ void setText(const QString &text) { m_text = text; }
+
+ QPointF pos() const { return m_pos; }
+ void setPos(QPointF pos) { m_pos = pos; }
+ void setPos(qreal x, qreal y) { setPos(QPointF(x, y)); }
+
+ bool quoted() const { return m_quoted; }
+ void setQuoted(bool quoted) { m_quoted = quoted; }
+
+ int maxWidth() const { return m_maxWidth; }
+ void setMaxWidth(int width) { m_maxWidth = width; }
+
+ int width() const;
+ int height() const { return QFontMetrics(m_font).height(); }
+
+ void paint(QPainter *painter);
+
+ static void locateAtCenter(QList<TextPainter*> items, qreal left, qreal top, qreal width);
+ static void locateAtCenter(TextPainter *item, qreal left, qreal top, qreal width);
+
+private:
+ QFont m_font;
+ QPen m_pen;
+ QBrush m_brush;
+ QString m_text;
+ QPointF m_pos;
+ bool m_quoted;
+ int m_maxWidth;
+};
+
+#endif // PAINTTEXTITEM_H
diff --git a/weather/src/pixmapbutton.cpp b/weather/src/pixmapbutton.cpp
new file mode 100644
index 0000000..6ec8be5
--- /dev/null
+++ b/weather/src/pixmapbutton.cpp
@@ -0,0 +1,30 @@
+#include "pixmapbutton.h"
+
+// PixmapButton
+
+PixmapButton::PixmapButton(qreal minSize, const QPixmap &pixmap, QGraphicsItem *parent)
+ : QGraphicsPixmapItem(pixmap, parent)
+ , m_minSize(minSize)
+{
+ setShapeMode(BoundingRectShape);
+}
+
+void PixmapButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+ emit clicked();
+}
+
+QPainterPath PixmapButton::shape() const
+{
+ return QGraphicsItem::shape();
+}
+
+QRectF PixmapButton::boundingRect() const
+{
+ QRectF result(QGraphicsPixmapItem::boundingRect());
+ qreal hMargin = m_minSize < result.width() ? 0 : (m_minSize - result.width()) / 2;
+ qreal vMargin = m_minSize < result.height() ? 0 : (m_minSize - result.height()) / 2;
+ result.adjust(-hMargin, -vMargin, hMargin, vMargin);
+ return result;
+}
diff --git a/weather/src/pixmapbutton.h b/weather/src/pixmapbutton.h
new file mode 100644
index 0000000..60c95a3
--- /dev/null
+++ b/weather/src/pixmapbutton.h
@@ -0,0 +1,29 @@
+#ifndef PIXMAPBUTTON_H
+#define PIXMAPBUTTON_H
+
+#include <QObject>
+#include <QGraphicsPixmapItem>
+
+class PixmapButton: public QObject, public QGraphicsPixmapItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity);
+public:
+ PixmapButton(qreal minSize, const QPixmap &pixmap, QGraphicsItem *parent = 0);
+ QRectF boundingRect() const;
+ QPainterPath shape() const;
+
+public slots:
+ void doHide() { hide(); }
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+
+private:
+ const qreal m_minSize;
+};
+
+#endif // PIXMAPBUTTON_H
diff --git a/weather/src/pixmaploader.cpp b/weather/src/pixmaploader.cpp
new file mode 100644
index 0000000..e60cef4
--- /dev/null
+++ b/weather/src/pixmaploader.cpp
@@ -0,0 +1,125 @@
+#include "pixmaploader.h"
+#include "settings.h"
+#include <QMutexLocker>
+
+#include "settings.h"
+
+// PixmapLoaderThread
+
+PixmapLoaderThread::PixmapLoaderThread(PixmapLoader *loader)
+ : QThread(loader)
+ , m_loader(loader)
+{
+}
+
+void PixmapLoaderThread::run()
+{
+ static const qreal widthFactor = Settings::widthFactor();
+ static const qreal heightFactor = Settings::heightFactor();
+ QString name = m_loader->dequeue();
+ while (!name.isEmpty()) {
+ QImage image(":images/weather_elements/" + name + ".png");
+ if (!image.isNull()) {
+ QSize size(widthFactor * image.size().width(), heightFactor * image.size().height());
+ if (size.width() && size.height())
+ image = image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ emit imageIsReady(name, image);
+ name = m_loader->dequeue();
+ }
+ deleteLater();
+}
+
+// PixmapLoader
+
+PixmapLoader::PixmapLoader(QObject *parent)
+ : QObject(parent)
+ , m_thread(0)
+{
+}
+
+void PixmapLoader::load(const QString &name)
+{
+ instance()->enqueue(name);
+}
+
+PixmapLoader *PixmapLoader::instance()
+{
+ static PixmapLoader * const result(new PixmapLoader());
+ return result;
+}
+
+void PixmapLoader::connectToOnIdleSignal(QObject *receiver, const char *method)
+{
+ QObject::connect(instance(), SIGNAL(onIdle()), receiver, method, Qt::QueuedConnection);
+}
+
+void PixmapLoader::disconnectReceiver(QObject *receiver)
+{
+ instance()->disconnect(receiver);
+}
+
+QPixmap PixmapLoader::getPic(const QString &name)
+{
+ PixmapLoader *obj = instance();
+ QMutexLocker locker(&obj->m_mutex);
+ return obj->m_store.contains(name) ? obj->m_store[name] : QPixmap();
+}
+
+void PixmapLoader::enqueue(const QString &name)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (m_queue.indexOf(name) >= 0 || m_currentImages.indexOf(name) >= 0 || m_store.contains(name))
+ return;
+
+ m_queue.append(name);
+
+ if (!m_thread) {
+ m_thread = new PixmapLoaderThread(this);
+ connect(m_thread, SIGNAL(imageIsReady(QString,QImage)),
+ this, SLOT(imageIsReady(QString,QImage)));
+ m_thread->start();
+ }
+ m_condition.wakeOne();
+}
+
+QString PixmapLoader::doDequeue()
+{
+ QString name = m_queue.isEmpty() ? QString() : m_queue.takeAt(0);
+
+ while (!name.isEmpty() && (m_currentImages.indexOf(name) >= 0 || m_store.contains(name)))
+ name = m_queue.isEmpty() ? QString() : m_queue.takeAt(0);
+
+ if (!name.isEmpty())
+ m_currentImages.append(name);
+
+ return name;
+}
+
+QString PixmapLoader::dequeue()
+{
+ static const int waitTime = 60000;
+ QMutexLocker locker(&m_mutex);
+ QString result;
+ do
+ result = doDequeue();
+ while (result.isEmpty() && m_condition.wait(&m_mutex, waitTime));
+ if (result.isEmpty()) {
+ m_thread = 0;
+ }
+ return result;
+}
+
+void PixmapLoader::imageIsReady(const QString &name, QImage image)
+{
+ QMutexLocker locker(&m_mutex);
+
+ QPixmap pixmap = QPixmap::fromImage(image, Qt::ColorOnly);
+ if (!pixmap.isNull())
+ m_store[name] = pixmap;
+
+ m_currentImages.removeAll(name);
+ if (m_currentImages.isEmpty() && m_queue.isEmpty())
+ emit onIdle();
+}
diff --git a/weather/src/pixmaploader.h b/weather/src/pixmaploader.h
new file mode 100644
index 0000000..f32bf64
--- /dev/null
+++ b/weather/src/pixmaploader.h
@@ -0,0 +1,60 @@
+#ifndef PIXMAPLOADER_H
+#define PIXMAPLOADER_H
+
+#include <QThread>
+#include <QMutex>
+#include <QMap>
+#include <QStringList>
+#include <QPixmap>
+#include <QWaitCondition>
+
+class PixmapLoader;
+
+class PixmapLoaderThread : public QThread
+{
+ Q_OBJECT
+public:
+ PixmapLoaderThread(PixmapLoader *loader);
+signals:
+ void imageIsReady(const QString &name, QImage image);
+protected:
+ void run();
+private:
+ PixmapLoader * const m_loader;
+
+};
+
+class PixmapLoader : public QObject
+{
+ Q_OBJECT
+public:
+ static void load(const QString &name);
+ static void connectToOnIdleSignal(QObject *receiver, const char *method);
+ static void disconnectReceiver(QObject *receiver);
+ static QPixmap getPic(const QString &name);
+
+signals:
+ void onIdle();
+
+private:
+ friend class PixmapLoaderThread;
+ QMutex m_mutex;
+ QWaitCondition m_condition;
+
+ QStringList m_queue;
+ QStringList m_currentImages;
+ QMap<QString, QPixmap> m_store;
+ PixmapLoaderThread *m_thread;
+
+ PixmapLoader(QObject *parent = 0);
+ static PixmapLoader *instance();
+
+ void enqueue(const QString &name);
+ QString doDequeue();
+ QString dequeue();
+
+private slots:
+ void imageIsReady(const QString &name, QImage image);
+};
+
+#endif // PIXMAPLOADER_H
diff --git a/weather/src/scrollbar.cpp b/weather/src/scrollbar.cpp
new file mode 100644
index 0000000..5fc1264
--- /dev/null
+++ b/weather/src/scrollbar.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QPropertyAnimation>
+#include <QDebug>
+
+#include "scrollbar.h"
+#include "pixmaploader.h"
+
+ScrollBar::ScrollBar(QGraphicsItem* parent)
+ : QGraphicsPixmapItem(PixmapLoader::getPic("scroll"), parent)
+ , m_knob(PixmapLoader::getPic("scroll_knob"), this)
+ , m_height(pixmap().height() - m_knob.pixmap().height())
+ , m_value(0.0)
+ , m_visible(false)
+{
+ hide();
+ setOpacity(0.0);
+ m_knob.setPos(0.0, 0.0);
+ m_timer.setSingleShot(true);
+ m_timer.setInterval(1000);
+
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
+}
+
+int ScrollBar::loadImages()
+{
+ PixmapLoader::load("scroll");
+ PixmapLoader::load("scroll_knob");
+ return 2;
+}
+bool ScrollBar::startAnimation(bool show)
+{
+ if (show == m_visible)
+ return false;
+
+ m_visible = show;
+ m_timer.stop();
+
+ if (m_animation)
+ m_animation->stop();
+
+ QPropertyAnimation* animation = new QPropertyAnimation(this, "opacity");
+ animation->setEasingCurve(QEasingCurve::OutExpo);
+ animation->setDuration(500);
+ animation->setEndValue(show ? 1.0 : 0.0);
+
+ if (show)
+ connect(animation, SIGNAL(finished()), &m_timer, SLOT(start()));
+ else
+ connect(animation, SIGNAL(finished()), this, SLOT(hideScrollBar()));
+
+ m_animation = animation;
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+ return true;
+}
+
+void ScrollBar::setValue(qreal value)
+{
+ if (value < 0 || value > 1.0)
+ return;
+ m_value = value;
+ m_knob.setPos(0, m_value * m_height);
+
+ show();
+ if (!startAnimation(true))
+ m_timer.start();
+}
+
+
+
+
diff --git a/weather/src/scrollbar.h b/weather/src/scrollbar.h
new file mode 100644
index 0000000..fb509d7
--- /dev/null
+++ b/weather/src/scrollbar.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SCROLLBAR_H
+#define SCROLLBAR_H
+
+#include <QTimer>
+#include <QPointer>
+#include <QGraphicsPixmapItem>
+#include <QAbstractAnimation>
+
+class ScrollBar: public QObject, public QGraphicsPixmapItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity);
+public:
+ ScrollBar(QGraphicsItem* parent = 0);
+ qreal value() const { return m_value; }
+ void setValue(qreal);
+ static int loadImages();
+
+private slots:
+ void timeout() { startAnimation(false); }
+ void hideScrollBar() { hide(); }
+
+private:
+ QGraphicsPixmapItem m_knob;
+ const qreal m_height;
+ qreal m_value;
+ QTimer m_timer;
+ QPointer<QAbstractAnimation> m_animation;
+ bool m_visible;
+
+ bool startAnimation(bool show);
+};
+
+#endif /* SCROLLBAR_H */
diff --git a/weather/src/settings.cpp b/weather/src/settings.cpp
new file mode 100644
index 0000000..32b3071
--- /dev/null
+++ b/weather/src/settings.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "settings.h"
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QTimer>
+#include <QDebug>
+
+#if defined(Q_OS_SYMBIAN)
+#define SETTINGS_FILE "c://data/weather.ini"
+#elif defined(Q_WS_MAEMO_5)
+#define SETTINGS_FILE "/usr/share/applications/weather.ini"
+#else
+#define SETTINGS_FILE "weather.ini"
+#endif
+
+Settings::Settings()
+ : m_settings(SETTINGS_FILE, QSettings::IniFormat)
+{
+}
+
+Settings *Settings::instance()
+{
+ static Settings * const result = new Settings();
+ return result;
+}
+
+QSize Settings::windowSize()
+{
+#ifdef Q_OS_SYMBIAN
+ static const QSize result(QApplication::desktop()->screenGeometry().size());
+#else
+ static const QSize result(instance()->m_settings.value("windowSize").toSize());
+#endif
+ return result;
+}
+
+bool Settings::scaledImages()
+{
+ QSize size(windowSize());
+ return size.width() != 480 || size.height() != 864;
+}
+
+class CitySortHelper
+{
+public:
+ CitySortHelper(const QMap<QString, int> &map) : m_map(map) {}
+ bool operator()(const QString &city1, const QString &city2)
+ {
+ return m_map[city1] < m_map[city2];
+ }
+
+private:
+ const QMap<QString, int> &m_map;
+};
+
+QStringList Settings::getCurrentCities()
+{
+ return getCities("Cities");
+}
+
+QStringList Settings::getDemoCities()
+{
+ return getCities("Demo");
+}
+
+QStringList Settings::getCities(const QString &type)
+{
+ Settings *obj = instance();
+
+ obj->m_settings.beginGroup(type);
+
+ QStringList result = obj->m_settings.allKeys();
+ QMap<QString, int> map;
+ foreach(const QString &key, result)
+ map[key] = obj->m_settings.value(key).toInt();
+
+ obj->m_settings.endGroup();
+
+ qSort(result.begin(), result.end(), CitySortHelper(map));
+ return result;
+}
+
+void Settings::setCurrentCities(QStringList cities)
+{
+ Settings *obj = instance();
+ obj->m_settings.beginGroup("Cities");
+ obj->m_settings.remove("");
+ for (int i = 0; i < cities.count(); ++i)
+ obj->m_settings.setValue(cities[i], i);
+ obj->m_settings.endGroup();
+ obj->m_settings.sync();
+}
diff --git a/weather/src/settings.h b/weather/src/settings.h
new file mode 100644
index 0000000..7556e72
--- /dev/null
+++ b/weather/src/settings.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <QObject>
+#include <QSettings>
+#include <QSize>
+#include <QPixmap>
+
+class Settings : public QObject
+{
+ Q_OBJECT
+public:
+ Settings();
+
+ static QString elementPath(const QString & element)
+ { return ":images/weather_elements/" + element; }
+
+ static QSize windowSize();
+ static bool scaledImages();
+
+ static qreal referenceWidth() { return 480.0; }
+ static qreal referenceHeight() { return 864.0; }
+
+ static qreal widthFactor() { return qreal(windowSize().width()) / referenceWidth(); }
+ static qreal heightFactor() { return qreal(windowSize().height()) / referenceHeight(); }
+
+ static qreal scaleWidth(qreal width) { return width * widthFactor(); }
+ static qreal scaleHeight(qreal height) { return height * heightFactor(); }
+
+ static QStringList getDemoCities();
+ static QStringList getCurrentCities();
+ static void setCurrentCities(QStringList cities);
+
+private:
+ QSettings m_settings;
+ static Settings *instance();
+
+ static QStringList getCities(const QString &type);
+
+};
+
+#endif // SETTINGS_H
diff --git a/weather/src/symbiannetwork.h b/weather/src/symbiannetwork.h
new file mode 100644
index 0000000..52cf45c
--- /dev/null
+++ b/weather/src/symbiannetwork.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef SYMBIAN_NETWORK_H
+#define SYMBIAN_NETWORK_H
+
+#include <es_sock.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <rconnmon.h>
+
+
+#include <stdarg.h>
+#include <e32std.h>
+
+/*
+void writeDebugMsg(const char* msg, ...)
+{
+ static char buffer[1024];
+
+ va_list args;
+ va_start( args, msg);
+ vsprintf(buffer, msg, args);
+ va_end( args);
+
+ FILE *logfile;
+ logfile = fopen("C:\\Data\\weather.log", "a");
+ fprintf(logfile, "%s\n", buffer);
+ fclose(logfile);
+}
+*/
+
+QString qt_TDesC2QStringL(const TDesC& aDescriptor)
+{
+#ifdef QT_NO_UNICODE
+ return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length());
+#else
+ return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length());
+#endif
+}
+
+static bool useCurrentConnectionL(RConnectionMonitor &monitor)
+{
+ TUint count;
+ TRequestStatus status;
+ monitor.GetConnectionCount(count, status);
+ User::WaitForRequest(status);
+ User::LeaveIfError(status.Int());
+ if (count <= 0)
+ return false;
+
+ TUint connId;
+ TUint subConnCount;
+ User::LeaveIfError(monitor.GetConnectionInfo(1, connId, subConnCount));
+ TBuf<50> iapName;
+ monitor.GetStringAttribute(connId, 0, KIAPName, iapName, status);
+ User::WaitForRequest(status);
+ User::LeaveIfError(status.Int());
+
+ QString strIapName = qt_TDesC2QStringL(iapName);
+ struct ifreq ifReq;
+ strcpy(ifReq.ifr_name, strIapName.toLatin1().data());
+ User::LeaveIfError(setdefaultif(&ifReq));
+ return true;
+}
+
+static bool useCurrentConnectionL()
+{
+ RConnectionMonitor monitor;
+ if (monitor.ConnectL() != KErrNone)
+ return false;
+ CleanupClosePushL(monitor);
+ bool result = useCurrentConnectionL(monitor);
+ CleanupStack::PopAndDestroy();
+ return result;
+}
+
+static bool useCurrentConnection()
+{
+ bool result = false;
+ TRAPD(error, result = useCurrentConnectionL());
+ return error == KErrNone && result;
+}
+
+static void createNewConnectionL()
+{
+ RSocketServ server;
+ User::LeaveIfError(server.Connect());
+ CleanupClosePushL(server);
+
+ RConnection connection;
+ User::LeaveIfError(connection.Open(server));
+ CleanupClosePushL(connection);
+ User::LeaveIfError(connection.Start());
+
+ _LIT(KIapName, "IAP\\Name");
+ TBuf8<50> iapName;
+ User::LeaveIfError(connection.GetDesSetting(TPtrC(KIapName), iapName));
+ connection.Stop();
+ CleanupStack::PopAndDestroy(2);
+
+ iapName.ZeroTerminate();
+ struct ifreq ifReq;
+ strcpy(ifReq.ifr_name, (char*)iapName.Ptr());
+ User::LeaveIfError(setdefaultif(&ifReq));
+}
+
+static bool createNewConnection()
+{
+ TRAPD(error, createNewConnectionL());
+ return error == KErrNone;
+}
+
+static bool connect()
+{
+ return useCurrentConnection() || createNewConnection();
+}
+
+#endif
diff --git a/weather/src/titlebar.cpp b/weather/src/titlebar.cpp
new file mode 100644
index 0000000..c38377a
--- /dev/null
+++ b/weather/src/titlebar.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "titlebar.h"
+#include "settings.h"
+#include "pixmaploader.h"
+
+#include <QFont>
+#include <QCoreApplication>
+
+// CloseButton
+
+CloseButton::CloseButton(QGraphicsItem *parent)
+ : PixmapButton(80.0, PixmapLoader::getPic("button_close"), parent)
+{
+}
+
+void CloseButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+ QCoreApplication::instance()->quit();
+}
+
+TitleBar::TitleBar(QGraphicsItem *parent)
+ : QGraphicsPixmapItem(PixmapLoader::getPic("title_bar"), parent)
+{
+ QGraphicsTextItem *text = new QGraphicsTextItem(this);
+ QFont font = text->font();
+ font.setFamily("Nokia sans regular");
+ font.setPixelSize(Settings::scaleHeight(25));
+ text->setFont(font);
+ text->setDefaultTextColor(QColor("white"));
+ text->setPlainText("Weather");
+ QRectF rect(boundingRect());
+ qreal x = rect.left() + (rect.width() - text->boundingRect().width()) / 2;
+ text->setPos(x, Settings::scaleHeight(5.0));
+
+ CloseButton *button = new CloseButton(this);
+ button->setPos(Settings::scaleWidth(442.0), Settings::scaleHeight(14.0));
+}
+
+int TitleBar::loadImages()
+{
+ PixmapLoader::load("button_close");
+ PixmapLoader::load("title_bar");
+ return 2;
+}
diff --git a/weather/src/titlebar.h b/weather/src/titlebar.h
new file mode 100644
index 0000000..4636a77
--- /dev/null
+++ b/weather/src/titlebar.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: openBossa - INdT (renato.chencarek@openbossa.org)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** the openBossa stream from INdT (renato.chencarek@openbossa.org).
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TITLEBAR_H
+#define TITLEBAR_H
+
+#include <QGraphicsPixmapItem>
+#include "pixmapbutton.h"
+
+class CloseButton: public PixmapButton
+{
+public:
+ CloseButton(QGraphicsItem *parent = 0);
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+};
+
+class TitleBar : public QGraphicsPixmapItem
+{
+public:
+ TitleBar(QGraphicsItem *parent = 0);
+ static int loadImages();
+};
+
+#endif // TITLEBAR_H
diff --git a/weather/src/xoapweatherresponse.cpp b/weather/src/xoapweatherresponse.cpp
new file mode 100644
index 0000000..5804bc9
--- /dev/null
+++ b/weather/src/xoapweatherresponse.cpp
@@ -0,0 +1,50 @@
+#include "xoapweatherresponse.h"
+
+#include <QDebug>
+
+static inline int strToInt(const QString &str, int defaultValue)
+{
+ bool ok;
+ int result = str.toInt(&ok);
+ return ok ? result : defaultValue;
+}
+
+static inline QString getNodeContentText(QDomNode node)
+{
+ if (node.childNodes().count() == 0 || !node.childNodes().at(0).isText())
+ return QString();
+ return node.childNodes().at(0).nodeValue();
+}
+
+void XoapWeatherResponse::Item::read(QDomElement element)
+{
+ m_id = element.attribute("id");
+ m_type = strToInt(element.attribute("type"), 0);
+ m_texts.clear();
+ m_texts << getNodeContentText(element).split(",", QString::SkipEmptyParts);
+}
+
+XoapWeatherResponse::XoapWeatherResponse()
+{
+}
+
+void XoapWeatherResponse::read(QDomElement element)
+{
+ m_version = element.attribute("ver");
+ for (int i = 0; i < element.childNodes().count(); ++i) {
+ if (element.childNodes().at(i).isElement()) {
+ Item item;
+ item.read(element.childNodes().at(i).toElement());
+ m_list.append(item);
+ }
+ }
+}
+
+void XoapWeatherResponse::print()
+{
+ /*
+ qDebug() << m_version << m_list.count() << " items";
+ for (int i = 0; i < m_list.count(); ++i)
+ qDebug() << "item " << i << " : " << m_list[i].id() << m_list[i].type() << m_list[i].texts();
+ */
+}
diff --git a/weather/src/xoapweatherresponse.h b/weather/src/xoapweatherresponse.h
new file mode 100644
index 0000000..1fd13cd
--- /dev/null
+++ b/weather/src/xoapweatherresponse.h
@@ -0,0 +1,43 @@
+#ifndef XOAPWEATHERRESPONSE_H
+#define XOAPWEATHERRESPONSE_H
+
+#include <QString>
+#include <QStringList>
+#include <QDomElement>
+#include <QTime>
+#include <QDate>
+#include <QDateTime>
+
+class XoapWeatherResponse
+{
+public:
+ class Item
+ {
+ public:
+ Item() : m_type(0) {}
+ void read(QDomElement element);
+
+ QStringList texts() const { return m_texts; }
+ int type() const { return m_type; }
+ QString id() const { return m_id; }
+
+ private:
+ QStringList m_texts;
+ int m_type;
+ QString m_id;
+
+ };
+ XoapWeatherResponse();
+ void read(QDomElement element);
+ void print();
+
+ QString versionid() const { return m_version; }
+ const QList<Item> &items() const { return m_list; }
+
+
+private:
+ QString m_version;
+ QList<Item> m_list;
+};
+
+#endif // XOAPWEATHERRESPONSE_H
diff --git a/weather/src/yahooweatherresponse.cpp b/weather/src/yahooweatherresponse.cpp
new file mode 100644
index 0000000..652d2c8
--- /dev/null
+++ b/weather/src/yahooweatherresponse.cpp
@@ -0,0 +1,165 @@
+#include "yahooweatherresponse.h"
+#include <QDebug>
+
+static inline int strToInt(const QString &str, int defaultValue)
+{
+ bool ok;
+ int result = str.toInt(&ok);
+ return ok ? result : defaultValue;
+}
+
+static inline float strToFloat(const QString &str, qreal defaultValue)
+{
+ bool ok;
+ float result = str.toFloat(&ok);
+ return ok ? result : defaultValue;
+}
+
+static inline YahooWeatherResponse::PressureState readPressure(const QString &str)
+{
+ switch (strToInt(str, 0)) {
+ case 1: return YahooWeatherResponse::Rising;
+ case 2: return YahooWeatherResponse::Falling;
+ default: return YahooWeatherResponse::Steady;
+ }
+}
+
+static inline QString getNodeContentText(QDomNode node)
+{
+ if (node.childNodes().count() == 0 || !node.childNodes().at(0).isText())
+ return QString();
+ return node.childNodes().at(0).nodeValue();
+}
+
+static const QDateTime readDateTime(const QString &str)
+{
+ QString text = str.right(str.length() - str.indexOf(',') - 1);
+ int len = text.indexOf("am", 15);
+ len = len < 0 ? text.indexOf("pm", 15) : len;
+ text = text.left(len + 2).trimmed();
+ return QDateTime::fromString(text, "dd MMM yyyy h:mm ap");
+}
+
+YahooWeatherResponse::Location::Location(QDomElement element)
+ : m_city(element.attribute("city"))
+ , m_region(element.attribute("region"))
+ , m_country(element.attribute("country"))
+{}
+
+YahooWeatherResponse::Units::Units(QDomElement element)
+ : m_temperature(element.attribute("temperature") == "f" ? Fahrenheit : Celsius)
+ , m_distance(element.attribute("distance") == "mi" ? Miles : Kilometers)
+ , m_pressure(element.attribute("pressure") == "in" ? PoundsPerSquare : Millibars)
+ , m_speed(element.attribute("speed") == "mph" ? MilesPerHour : KilometersPerHour)
+{}
+
+YahooWeatherResponse::Wind::Wind(QDomElement element)
+ : m_chill(strToInt(element.attribute("chill"), 0))
+ , m_direction(strToInt(element.attribute("direction"), 0))
+ , m_speed(strToInt(element.attribute("speed"), 0))
+{}
+
+YahooWeatherResponse::Atmosphere::Atmosphere(QDomElement element)
+ : m_humidity(strToInt(element.attribute("humidity"), 0))
+ , m_visibility(strToFloat(element.attribute("visibility"), 0.0) * 100)
+ , m_pressure(strToFloat(element.attribute("pressure"), 0.0))
+ , m_pressureState(readPressure(element.attribute("rising")))
+{}
+
+YahooWeatherResponse::Astronomy::Astronomy(QDomElement element)
+ : m_sunrise(QTime::fromString(element.attribute("sunrise"), "h:mm ap"))
+ , m_sunset(QTime::fromString(element.attribute("sunset"), "h:mm ap"))
+{}
+
+YahooWeatherResponse::Condition::Condition(QDomElement element)
+ : m_text(element.attribute("text"))
+ , m_code(strToInt(element.attribute("code"), 3200))
+ , m_temperature(strToInt(element.attribute("temp"), 0))
+ , m_date(readDateTime(element.attribute("date")))
+{}
+
+YahooWeatherResponse::Forecast::Forecast(QDomElement element)
+ : m_date(QDate::fromString(element.attribute("date"), "dd MMM yyyy"))
+ , m_low(strToInt(element.attribute("low"), 0))
+ , m_high(strToInt(element.attribute("high"), 0))
+ , m_text(element.attribute("text"))
+ , m_code(strToInt(element.attribute("code"), 3200))
+{}
+
+void YahooWeatherResponse::readItem(QDomElement item)
+{
+ int idx = 0;
+ for (int i = 0; i < item.childNodes().count(); ++i) {
+ QDomNode node = item.childNodes().at(i);
+ if (node.nodeName() == "geo:lat")
+ m_latitude = strToFloat(getNodeContentText(node), 0.0);
+ if (node.nodeName() == "geo:long")
+ m_longitude = strToFloat(getNodeContentText(node), 0.0);
+ if (node.nodeName() == "pubDate")
+ m_pubDate = readDateTime(getNodeContentText(node));
+ if (node.nodeName() == "yweather:condition")
+ m_condition = Condition(node.toElement());
+ if (node.nodeName() == "yweather:forecast")
+ m_forecast[idx++] = Forecast(node.toElement());
+ idx = idx > 1 ? 0 : idx;
+ }
+}
+
+YahooWeatherResponse::YahooWeatherResponse(const QString &locationCode, QDomElement element)
+ : m_locationCode(locationCode)
+{
+ QDomNodeList channels = element.elementsByTagName("channel");
+ if (channels.count() == 0)
+ return;
+ QDomElement channel = channels.item(0).toElement();
+ for (int i = 0; i < channel.childNodes().count(); ++i) {
+ QDomNode node = channel.childNodes().at(i);
+
+ if (node.nodeName() == "lastBuildDate")
+ m_buildDate = readDateTime(getNodeContentText(node));
+
+ if (node.nodeName() == "ttl")
+ m_timeToLive = strToInt(getNodeContentText(node), 30);
+
+ if (node.nodeName() == "yweather:location")
+ m_location = Location(node.toElement());
+
+ if (node.nodeName() == "yweather:units")
+ m_units = Units(node.toElement());
+
+ if (node.nodeName() == "yweather:wind")
+ m_wind = Wind(node.toElement());
+
+ if (node.nodeName() == "yweather:atmosphere")
+ m_atmosphere = Atmosphere(node.toElement());
+
+ if (node.nodeName() == "yweather:astronomy")
+ m_astronomy = Astronomy(node.toElement());
+
+ if (node.nodeName() == "item")
+ readItem(node.toElement());
+
+ }
+}
+
+void YahooWeatherResponse::print()
+{
+ /*
+ qDebug() << "location code" << m_locationCode;
+ qDebug() << "buildDate" << m_buildDate;
+ qDebug() << "timeToLive" << m_timeToLive;
+
+ qDebug() << "latitude" << m_latitude;
+ qDebug() << "longitude" << m_longitude;
+ qDebug() << "pubDate" << m_pubDate;
+
+ qDebug() << "astronomy" << m_astronomy.sunrise() << m_astronomy.sunset();
+ qDebug() << "atmosphere" << m_atmosphere.humidity() << m_atmosphere.visibility() << m_atmosphere.pressure() << m_atmosphere.pressureState();
+ qDebug() << "condition" << m_condition.text() << m_condition.code() << m_condition.temperature() << m_condition.date();
+ qDebug() << "forecast" << m_forecast[0].text() << m_forecast[0].code() << m_forecast[0].low() << m_forecast[0].high() << m_forecast[0].date();
+ qDebug() << "forecast" << m_forecast[1].text() << m_forecast[1].code() << m_forecast[1].low() << m_forecast[1].high() << m_forecast[1].date();
+ qDebug() << "location" << m_location.city() << m_location.region() << m_location.country();
+ qDebug() << "units" << m_units.distance() << m_units.pressure() << m_units.speed() << m_units.temperature();
+ qDebug() << "wind" << m_wind.chill() << m_wind.direction() << m_wind.speed();
+ */
+}
diff --git a/weather/src/yahooweatherresponse.h b/weather/src/yahooweatherresponse.h
new file mode 100644
index 0000000..ac9d20e
--- /dev/null
+++ b/weather/src/yahooweatherresponse.h
@@ -0,0 +1,181 @@
+#ifndef YAHOOWEATHERRESPONSE_H
+#define YAHOOWEATHERRESPONSE_H
+
+#include <QString>
+#include <QDomElement>
+#include <QTime>
+#include <QDate>
+#include <QDateTime>
+
+class YahooWeatherResponse
+{
+public:
+ enum TemperatureUnit { Fahrenheit, Celsius };
+ enum DistanceUnit { Miles, Kilometers };
+ enum PressureUnit { PoundsPerSquare, Millibars };
+ enum SpeedUnit { MilesPerHour, KilometersPerHour };
+ enum PressureState { Steady, Rising, Falling };
+
+ class Location
+ {
+ public:
+ Location() {};
+ Location(QDomElement element);
+ QString city() const { return m_city; }
+ QString region() const { return m_region; }
+ QString country() const { return m_country; }
+
+ private:
+ QString m_city;
+ QString m_region;
+ QString m_country;
+ };
+
+ class Units
+ {
+ public:
+ Units() {};
+ Units(QDomElement element);
+ TemperatureUnit temperature() const { return m_temperature; }
+ DistanceUnit distance() const { return m_distance; }
+ PressureUnit pressure() const { return m_pressure; }
+ SpeedUnit speed() const { return m_speed; }
+
+ private:
+ TemperatureUnit m_temperature;
+ DistanceUnit m_distance;
+ PressureUnit m_pressure;
+ SpeedUnit m_speed;
+ };
+
+ class Wind
+ {
+ public:
+ Wind() {};
+ Wind(QDomElement element);
+ int chill() const { return m_chill; }
+ int direction() const { return m_direction; }
+ int speed() const { return m_speed; }
+
+ private:
+ int m_chill;
+ int m_direction;
+ int m_speed;
+ };
+
+ class Atmosphere
+ {
+ public:
+ Atmosphere() {};
+ Atmosphere(QDomElement element);
+ int humidity() const { return m_humidity; }
+ float visibility() const { return m_visibility; }
+ float pressure() const { return m_pressure; }
+ PressureState pressureState() const { return m_pressureState; }
+
+ private:
+ int m_humidity;
+ float m_visibility;
+ float m_pressure;
+ PressureState m_pressureState;
+ };
+
+ class Astronomy
+ {
+ public:
+ Astronomy() {};
+ Astronomy(QDomElement element);
+ QTime sunrise() const { return m_sunrise; }
+ QTime sunset() const { return m_sunset; }
+
+ private:
+ QTime m_sunrise;
+ QTime m_sunset;
+ };
+
+ class Condition
+ {
+ public:
+ Condition() {};
+ Condition(QDomElement element);
+ QString text() const { return m_text; }
+ int code() const { return m_code; }
+ int temperature() const { return m_temperature; }
+ QDateTime date() const { return m_date; }
+
+ private:
+ QString m_text;
+ int m_code;
+ int m_temperature;
+ QDateTime m_date;
+ };
+
+ class Forecast
+ {
+ public:
+ Forecast() {};
+ Forecast(QDomElement element);
+
+ QDate date() const { return m_date; }
+ int low() const { return m_low; }
+ int high() const { return m_high; }
+ QString text() const { return m_text; }
+ int code() const { return m_code; }
+
+ private:
+ QDate m_date;
+ int m_low;
+ int m_high;
+ QString m_text;
+ int m_code;
+ };
+
+public:
+ YahooWeatherResponse(const QString &locationCode, QDomElement element);
+
+ QString locationCode() const { return m_locationCode; }
+
+ Location location() const { return m_location; }
+ Units units() const { return m_units; }
+ Wind wind() const { return m_wind; }
+ Atmosphere atmosphere() const { return m_atmosphere; }
+ Astronomy astronomy() const { return m_astronomy; }
+ Condition condition() const { return m_condition; }
+ Forecast forecast(int idx) const { return m_forecast[idx]; }
+
+ QDateTime buildDate() const { return m_buildDate; }
+ int timeToLive() const { return m_timeToLive; }
+ float latitude() const { return m_latitude; }
+ float longitude() const { return m_longitude; }
+ QDateTime pubDate() const { return m_pubDate; }
+
+ void print();
+
+
+private:
+ QString m_locationCode;
+ QDateTime m_buildDate;
+ int m_timeToLive;
+ float m_latitude;
+ float m_longitude;
+ QDateTime m_pubDate;
+
+ Location m_location;
+ Units m_units;
+ Wind m_wind;
+ Atmosphere m_atmosphere;
+ Astronomy m_astronomy;
+ Condition m_condition;
+ Forecast m_forecast[2];
+
+ void readItem(QDomElement item);
+
+// <guid isPermaLink="false">USCA1116_2009_11_08_18_56_PST</guid>
+
+
+
+
+
+};
+
+#endif // YAHOOWEATHERRESPONSE_H