Added theme scheduler, poi support and speed alarm features.
authoreshe <jessehakanen@gmail.com>
Sun, 25 Jul 2010 21:15:36 +0000 (22:15 +0100)
committereshe <jessehakanen@gmail.com>
Sun, 25 Jul 2010 21:15:36 +0000 (22:15 +0100)
43 files changed:
jspeed.pro
src/buttonbox.cpp [new file with mode: 0644]
src/buttonbox.h [new file with mode: 0644]
src/buttonselector.cpp
src/buttonselector.h
src/fileselector.cpp [new file with mode: 0644]
src/fileselector.h [new file with mode: 0644]
src/graphicsscene.h
src/main.cpp
src/mainmenu.cpp
src/mainmenu.h
src/mainwindow.cpp
src/mediaplayer.cpp [new file with mode: 0644]
src/mediaplayer.h [new file with mode: 0644]
src/odometer.cpp
src/odometer.h
src/poialerts.cpp [new file with mode: 0644]
src/poialerts.h [new file with mode: 0644]
src/poiascreader.cpp [new file with mode: 0644]
src/poiascreader.h [new file with mode: 0644]
src/poireader.cpp [new file with mode: 0644]
src/poireader.h [new file with mode: 0644]
src/poisettings.cpp [new file with mode: 0644]
src/poisettings.h [new file with mode: 0644]
src/resources.qrc
src/resources/sounds/beep.wav [new file with mode: 0644]
src/soundselector.cpp [new file with mode: 0644]
src/soundselector.h [new file with mode: 0644]
src/speedalarm.cpp [new file with mode: 0644]
src/speedalarm.h [new file with mode: 0644]
src/speedalarmsettings.cpp [new file with mode: 0644]
src/speedalarmsettings.h [new file with mode: 0644]
src/themeloader.cpp
src/themeloader.h
src/themepicker.cpp [new file with mode: 0644]
src/themepicker.h [new file with mode: 0644]
src/themescheduler.cpp [new file with mode: 0644]
src/themescheduler.h [new file with mode: 0644]
src/themeschedulersettings.cpp [new file with mode: 0644]
src/themeschedulersettings.h [new file with mode: 0644]
src/themeselector.cpp
src/themeselector.h
src/unitselector.cpp

index f3014b6..ad563e1 100644 (file)
@@ -1,6 +1,8 @@
 QT += maemo5 xml
 TARGET = jspeed
 TEMPLATE = app
+CONFIG += mobility
+MOBILITY += multimedia
 SOURCES += src/main.cpp \ 
            src/mainwindow.cpp \
            src/mainwindowstack.cpp \
@@ -30,7 +32,20 @@ SOURCES += src/main.cpp \
            src/effect.cpp \
            src/blureffect.cpp \
            src/opacityeffect.cpp \
-           src/dropshadoweffect.cpp
+           src/dropshadoweffect.cpp \
+           src/poialerts.cpp \
+           src/poireader.cpp \
+           src/poiascreader.cpp \
+           src/poisettings.cpp \
+           src/speedalarmsettings.cpp \
+           src/speedalarm.cpp \
+           src/fileselector.cpp \
+           src/themescheduler.cpp \
+           src/themeschedulersettings.cpp \
+           src/themepicker.cpp \
+           src/mediaplayer.cpp \
+           src/soundselector.cpp \
+           src/buttonbox.cpp
 HEADERS += src/mainwindow.h \
            src/mainwindowstack.h \
            src/location.h \
@@ -59,7 +74,20 @@ HEADERS += src/mainwindow.h \
            src/effect.h \
            src/blureffect.h \
            src/opacityeffect.h \
-           src/dropshadoweffect.h
+           src/dropshadoweffect.h \
+           src/poialerts.h \
+           src/poireader.h \
+           src/poiascreader.h \
+           src/poisettings.h \
+           src/speedalarmsettings.h \
+           src/speedalarm.h \
+           src/fileselector.h \
+           src/themescheduler.h \
+           src/themeschedulersettings.h \
+           src/themepicker.h \
+           src/mediaplayer.h \
+           src/soundselector.h \
+           src/buttonbox.h
 RESOURCES = src/resources.qrc
 CONFIG += link_pkgconfig
 PKGCONFIG += liblocation libzip
diff --git a/src/buttonbox.cpp b/src/buttonbox.cpp
new file mode 100644 (file)
index 0000000..4636aae
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtGui/QPushButton>
+#include "buttonbox.h"
+
+ButtonBox::ButtonBox(QWidget* parent): QDialogButtonBox(parent)
+{
+    setCenterButtons(false);
+    setOrientation(Qt::Vertical);
+}
+
+QPushButton* ButtonBox::addButton(QString const& text,
+                                  const QObject* receiver,
+                                  const char* member,
+                                  QDialogButtonBox::ButtonRole role)
+{
+    QPushButton* button = new QPushButton(text);
+    connect(button, SLOT(clicked(bool)), receiver, member);
+    QDialogButtonBox::addButton(button, role);
+    return button;
+}
diff --git a/src/buttonbox.h b/src/buttonbox.h
new file mode 100644 (file)
index 0000000..6a3771c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BUTTONBOX_H
+#define BUTTONBOX_H
+
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QPushButton>
+#include <QtCore/QString>
+
+class ButtonBox : public QDialogButtonBox
+{
+    Q_OBJECT
+
+public:
+    ButtonBox(QWidget* parent = 0);
+    QPushButton* addButton(QString const& text,
+                           const QObject* receiver,
+                           const char* member,
+                           QDialogButtonBox::ButtonRole role = QDialogButtonBox::AcceptRole);
+};
+
+#endif
index c1adde3..22fa6d4 100644 (file)
@@ -142,6 +142,11 @@ bool ButtonSelector::removeItem(unsigned int index)
     return model_->removeRow(index);
 }
 
+int ButtonSelector::size() const
+{
+    return model_->rowCount();
+}
+
 void ButtonSelector::onSelected(QString const& text)
 {
     emit selected(currentIndex(), text, value());
index e36c159..6b92b24 100644 (file)
@@ -39,6 +39,7 @@ public:
     int indexOfValue(QVariant const& value) const;
     bool selectByValue(QVariant const& value);
     int currentIndex() const;
+    int size() const;
     bool changeItem(unsigned int index, QString const& text);
     bool changeItem(unsigned int index, QString const& text, QVariant const& value);
     bool removeItem(unsigned int index);
diff --git a/src/fileselector.cpp b/src/fileselector.cpp
new file mode 100644 (file)
index 0000000..e7e9887
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtGui/QFileDialog>
+#include <QtGui/QMessageBox>
+#include <QtCore/QString>
+#include <QtCore/QDir>
+#include <QtCore/QStringList>
+#include "fileselector.h"
+
+FileSelector::FileSelector(QString const& text, QWidget* parent): ButtonSelector(text, parent)
+{
+}
+
+bool FileSelector::loadFiles(QString const& dir, QString const& pattern)
+{
+    QDir directory(dir);
+
+    if(directory.exists() && directory.isReadable())
+    {
+        QStringList filters;
+        filters << pattern;
+        directory.setNameFilters(filters);
+        directory.setFilter(QDir::Files);
+        QStringList files = directory.entryList();
+
+        for(int i = 0; i < files.size(); i++)
+        {
+            addItem(files.at(i));
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+bool FileSelector::importFile(QString const& dir,
+                              QString const& name,
+                              QString const& pattern,
+                              bool add,
+                              QString* addedFile)
+{
+    QString filename = QFileDialog::getOpenFileName(this, tr("Open file"),
+                                                    QDir::home().path(),
+                                                    name + "(" + pattern + ")");
+
+    if(filename.isEmpty())
+    {
+        return true;
+    }
+
+    QString basename;
+
+    int i = filename.lastIndexOf(QDir::separator());
+
+    if(i == -1)
+    {
+        basename = filename;
+    }
+    else
+    {
+        basename = filename.mid(i + 1);
+    }
+
+    QString targetFile = dir + basename;
+
+    QDir targetDir(dir);
+
+    if(!targetDir.exists())
+    {
+        if(!targetDir.mkpath(dir))
+        {
+            return false;
+        }
+    }
+
+    bool ok = true;
+
+    if(QFile::exists(targetFile))
+    {
+        ok = confirmCopy(basename);
+
+        if(ok)
+        {
+            QFile::remove(targetFile);
+        }
+    }
+
+    if(ok)
+    {
+        if(!QFile::copy(filename, targetFile))
+        {
+            return false;
+        }
+
+        if(addedFile)
+        {
+            *addedFile = basename;
+        }
+
+        if(add)
+        {
+            addItem(basename);
+            selectByValue(basename);
+        }
+
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool FileSelector::confirmCopy(QString const& filename)
+{
+    QMessageBox::StandardButton result =  QMessageBox::question(this, tr("File exists"),
+                                                                tr("File %1 already exists in directory, overwrite?").arg(filename),
+                                                                QMessageBox::Yes | QMessageBox::No);
+
+    return (result == QMessageBox::Yes);
+}
+
diff --git a/src/fileselector.h b/src/fileselector.h
new file mode 100644 (file)
index 0000000..1a2ccb3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef FILESELECTOR_H
+#define FILESELECTOR_H
+
+#include "buttonselector.h"
+
+class QString;
+
+class FileSelector : public ButtonSelector
+{
+    Q_OBJECT
+
+public:
+    FileSelector(QString const& text, QWidget* parent = 0);
+    bool loadFiles(QString const& dir, QString const& pattern);
+    bool importFile(QString const& dir, QString const& name,
+                    QString const& pattern, bool add = true,
+                    QString* addedFile = 0);
+
+private:
+    bool confirmCopy(QString const& filename);
+
+};
+
+#endif
index d5ac6d7..0a8733e 100644 (file)
@@ -29,7 +29,7 @@ class GraphicsScene : public QGraphicsScene
 
 public:
     GraphicsScene(QRectF const& rect);
-    static int const CLICK_MARGIN = 45;
+    static int const CLICK_MARGIN = 48;
 
 signals:
     void clicked();
index cee1700..9838096 100644 (file)
 #include <QtGui/QApplication>
 #include "mainwindow.h"
 
+#include "poiascreader.h"
+#include <QtCore/QDebug>
+#include "poialerts.h"
+
 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
 
+   /* PoiAscReader reader("/home/maemo/workspace/jspeed/test.asc");
+
+    QList<PoiAscReader::Poi> pois;
+
+    if(reader.read(pois))
+    {
+        qDebug() << "Kyl";
+
+       for(int i = 0; i < pois.size(); i++)
+        {
+            qDebug() << pois.at(i).latitude << pois.at(i).longitude << pois.at(i).name;
+        }
+    }
+    else
+    {
+        qDebug() << "Ei";
+    }*/
+
+    //PoiAlerts::instance().startTest();
+
     MainWindow window;
 
     QObject::connect(&window, SIGNAL(quit()), &app, SLOT(quit()));
index a10d69b..da6af01 100644 (file)
 #include "mainmenu.h"
 #include "themeselector.h"
 #include "unitselector.h"
+#include "poisettings.h"
+#include "speedalarmsettings.h"
 
 MainMenu::MainMenu(QWidget* parent): QDialog(parent), itemCount_(0),
-currentRow_(0), themeSelector_(0), unitSelector_(0), aboutDialog_(0)
+currentRow_(0), themeSelector_(0), unitSelector_(0), poiSettings_(0),
+speedAlarmSettings_(0), aboutDialog_(0)
 {
     layout_ = new QVBoxLayout;
     setLayout(layout_);
@@ -35,8 +38,10 @@ currentRow_(0), themeSelector_(0), unitSelector_(0), aboutDialog_(0)
     setWindowTitle(tr("Menu"));
     addAction(tr("Reset trip"), this, SIGNAL(resetTrip()));
     addAction(tr("Reset all"), this, SLOT(confirmReset()));
-    addAction(tr("Select theme"), this, SLOT(selectTheme()));
+    addAction(tr("Theme"), this, SLOT(selectTheme()));
     addAction(tr("Set unit"), this, SLOT(selectUnit()));
+    addAction(tr("Speed camera alerts"), this, SLOT(openPoiAlerts()));
+    addAction(tr("Speed alarm"), this, SLOT(openSpeedAlarm()));
     addAction(tr("Flip screen"), this, SIGNAL(flip()));
     addAction(tr("About"), this, SLOT(showAbout()));
 }
@@ -52,6 +57,26 @@ void MainMenu::selectTheme()
     themeSelector_->show();
 }
 
+void MainMenu::openPoiAlerts()
+{
+    if(!poiSettings_)
+    {
+        poiSettings_ = new PoiSettings(this);
+    }
+
+    poiSettings_->show();
+}
+
+void MainMenu::openSpeedAlarm()
+{
+    if(!speedAlarmSettings_)
+    {
+        speedAlarmSettings_ = new SpeedAlarmSettings(this);
+    }
+
+    speedAlarmSettings_->show();
+}
+
 void MainMenu::selectUnit()
 {
     if(!unitSelector_)
index 44e8b0c..39dbb5d 100644 (file)
@@ -25,6 +25,8 @@ class QVBoxLayout;
 class QHBoxLayout;
 class ThemeSelector;
 class UnitSelector;
+class PoiSettings;
+class SpeedAlarmSettings;
 
 class MainMenu : public QDialog
 {
@@ -46,6 +48,8 @@ private slots:
     void selectUnit();
     void showAbout();
     void confirmReset();
+    void openPoiAlerts();
+    void openSpeedAlarm();
 
 private:
     QVBoxLayout* layout_;
@@ -53,6 +57,8 @@ private:
     QHBoxLayout* currentRow_;
     ThemeSelector* themeSelector_;
     UnitSelector* unitSelector_;
+    PoiSettings* poiSettings_;
+    SpeedAlarmSettings* speedAlarmSettings_;
     QDialog* aboutDialog_;
 };
 
index 39b41b3..0d1294a 100644 (file)
 #include "mainmenu.h"
 #include "odometer.h"
 #include "widgetscreen.h"
+#include "poialerts.h"
+#include "speedalarm.h"
 
 MainWindow::MainWindow(): QMainWindow(0), menu_(0), themeLoader_(0), mainScreen_(0)
 {
     setWindowTitle(tr("jSpeed"));
     showFullScreen();
     Odometer::instance().start();
+    PoiAlerts::instance().start();
+    SpeedAlarm::instance().start();
     addScreens();
     startBacklight();
 }
@@ -115,6 +119,8 @@ void MainWindow::openMenu()
         connect(menu_, SIGNAL(flip()), stack_, SLOT(flip()));
         connect(menu_, SIGNAL(themeChanged()), this, SLOT(loadTheme()));
         connect(menu_, SIGNAL(unitChanged()), &(Odometer::instance()), SLOT(updateUnit()));
+        connect(menu_, SIGNAL(poiSettingsChanged()), &(PoiAlerts::instance()), SLOT(loadConfig()));
+        connect(menu_, SIGNAL(speedAlarmSettingsChanged()), &(SpeedAlarm::instance()), SLOT(loadConfig()));
     }
 
     menu_->show();
diff --git a/src/mediaplayer.cpp b/src/mediaplayer.cpp
new file mode 100644 (file)
index 0000000..3d09112
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QUrl>
+#include <QtCore/QDir>
+#include <QMediaContent>
+#include "mediaplayer.h"
+#include "settings.h"
+
+namespace
+{
+    QString const FORMATS[MediaPlayer::FORMAT_COUNT] = {"mp3", "aac", "wav"};
+    QString const LOCAL_SOUND_DIR = ":/resources/sounds/";
+}
+
+QMediaPlayer MediaPlayer::player_;
+
+bool MediaPlayer::play(QString const& file)
+{
+    QMediaContent media(QUrl::fromLocalFile(file));
+
+    if(media.isNull())
+    {
+        return false;
+    }
+
+    player_.setMedia(media);
+    player_.play();
+    return true;
+}
+
+void MediaPlayer::getSupportedFormats(QList<QString>& formats)
+{
+    formats.clear();
+
+    for(int i = 0; i < FORMAT_COUNT; i++)
+    {
+        formats.push_back(FORMATS[i]);
+    }
+}
+
+QString MediaPlayer::getFormatPattern()
+{
+   QString result = "";
+
+   for(int i = 0; i < FORMAT_COUNT; i++)
+   {
+       result += "*." + FORMATS[i] + " ";
+   }
+
+   return result.trimmed();
+}
+
+QString MediaPlayer::getSoundDir()
+{
+    return Settings::getDir() + "sounds" + QDir::separator();
+}
+
+QString const& MediaPlayer::getLocalSoundDir()
+{
+    return LOCAL_SOUND_DIR;
+}
+
diff --git a/src/mediaplayer.h b/src/mediaplayer.h
new file mode 100644 (file)
index 0000000..abd3303
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MEDIAPLAYER_H
+#define MEDIAPLAYER_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QMediaPlayer>
+
+class MediaPlayer
+{
+public:
+    enum Format {MP3, WAV, AAC, FORMAT_COUNT};
+    static bool play(QString const& file);
+    static void getSupportedFormats(QList<QString>& formats);
+    static QString getFormatPattern();
+    static QString getSoundDir();
+    static QString const& getLocalSoundDir();
+
+private:
+    static QMediaPlayer player_;
+};
+
+#endif
index d68fb1f..4d81136 100644 (file)
@@ -284,6 +284,11 @@ QString const& Odometer::getSpeedUnit()
     }
 }
 
+double Odometer::getUnitMultiplier()
+{
+    return Location::getUnitMultiplier();
+}
+
 void Odometer::updateUnit()
 {
     QString unit = Settings::instance().value("unit", "km").toString();
index c68fee3..83116bb 100644 (file)
@@ -44,6 +44,7 @@ public:
     double getSignalStrength() const;
     static QString const& getUnit();
     static QString const& getSpeedUnit();
+    static double getUnitMultiplier();
 
 signals:
     void dataUpdated();
diff --git a/src/poialerts.cpp b/src/poialerts.cpp
new file mode 100644 (file)
index 0000000..dcc847e
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QString>
+#include <QtCore/QDir>
+#include <QtCore/QTimer>
+#include <QtCore/QFile>
+#include <QtCore/QDebug>
+#include <math.h>
+#include "poialerts.h"
+#include "odometer.h"
+#include "settings.h"
+#include "location.h"
+#include "mediaplayer.h"
+
+namespace
+{
+    static double const EARTH_MEAN_RADIUS = 6371.0072;
+    static double const PI = 3.14159265;
+}
+
+inline static double degToRad(double deg)
+{
+    return deg * PI / 180;
+}
+
+inline static double radToDeg(double rad)
+{
+    return rad * 180 / PI;
+}
+
+PoiAlerts::PoiAlerts(): QObject(0), enabled_(false), currentPoi_(0), loaded_(false)
+{
+}
+
+PoiAlerts::~PoiAlerts()
+{
+    end();
+}
+
+PoiAlerts& PoiAlerts::instance()
+{
+    static PoiAlerts instance;
+    return instance;
+}
+
+bool PoiAlerts::start()
+{
+    if(!loaded_)
+    {
+        if(!loadConfig())
+        {
+            return false;
+        }
+    }
+
+    if(enabled_)
+    {
+        connect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
+    }
+
+    return true;
+}
+
+void PoiAlerts::end()
+{
+    if(enabled_)
+    {
+        disconnect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
+    }
+
+    pois_.clear();
+    playedSounds_.clear();
+    travelled_ = 0;
+    enabled_ = false;
+    currentPoi_ = 0;
+}
+
+bool PoiAlerts::loadConfig()
+{
+    loaded_ = true;
+
+    bool enabled = Settings::instance().value("alert_enabled", false).toBool();
+
+    if(!enabled)
+    {
+        end();
+        enabled_ = false;
+        return true;
+    }
+
+    distance_ = Settings::instance().value("alert_distance", 300).toBool();
+    onlyOnRoute_ = Settings::instance().value("alert_only_on_route", true).toBool();
+
+    if(distance_ < 0)
+    {
+        distance_ = 0;
+    }
+
+    QString filename = Settings::instance().value("alert_sound", "").toString();
+
+    if(filename.isEmpty())
+    {
+        filename = "alert.wav";
+    }
+
+    QString soundDir = MediaPlayer::getSoundDir();
+    QString localDir = MediaPlayer::getLocalSoundDir();
+
+    if(!filename.isEmpty() && QFile::exists(soundDir + filename))
+    {
+        file_ = soundDir + filename;
+    }
+    else if(!filename.isEmpty() && QFile::exists(localDir + filename))
+    {
+        file_ = localDir + filename;
+    }
+    else
+    {
+        enabled_ = false;
+        return false;
+    }
+
+    if(!loadPois())
+    {
+        enabled_ = false;
+        return false;
+    }
+
+    if(!enabled_)
+    {
+        enabled_ = true;
+        start();
+    }
+
+    return true;
+}
+
+bool PoiAlerts::loadPois()
+{
+    QString filename = Settings::instance().value("alert_poi_file", "").toString();
+
+    QString poiFile = getPoiDir() + filename;
+
+    if(filename.isEmpty() || !QFile::exists(poiFile))
+    {
+        error_ = "Poi file doesn't exist";
+        return false;
+    }
+
+    PoiReader* reader = PoiReader::getReader(poiFile);
+
+    if(!reader)
+    {
+        error_ = "Unknown file format: " + poiFile;
+        return false;
+    }
+
+    if(!reader->read(pois_))
+    {
+        error_ = reader->error();
+        return false;
+    }
+
+    return true;
+}
+
+double PoiAlerts::getCurrentDistance() const
+{
+    if(!currentPoi_)
+    {
+        return 0.0;
+    }
+
+    return currentDistance_;
+}
+
+QString PoiAlerts::getCurrentPoi() const
+{
+    if(currentPoi_)
+    {
+        return currentPoi_->name;
+    }
+
+    return "";
+}
+
+bool PoiAlerts::poiInView() const
+{
+    return currentPoi_ != 0;
+}
+
+QString const& PoiAlerts::error() const
+{
+    return error_;
+}
+
+void PoiAlerts::onDataUpdated()
+{
+    qDebug() << "Data update";
+
+    static int i = 0;
+    i++;
+
+    double travelled = Odometer::instance().getTotal();
+    const Location::Fix* fix = &(Odometer::instance().getLatestFix());
+
+    if(fix->latitude < 0.01 || fix->longitude < 0.01 || fix->kmSpeed < 0.01)
+    {
+        return;
+    }
+    else
+    {
+        /*if(i < 5)
+        {
+            pois_[0].latitude = fix->latitude;
+            pois_[0].longitude = fix->longitude;
+        }*/
+    }
+
+    double distance;
+    double inRouteMargin = IN_ROUTE_MARGIN + (fix->eph / 1000.0);
+
+    qDebug() << "Eph: " << fix->eph;
+    qDebug() << "In route margin: " << inRouteMargin;
+
+    if(1 == 1 || abs(travelled - travelled_) > 0.03)
+    {
+        travelled_ = travelled;
+
+        for(int i = 0; i < pois_.size(); i++)
+        {
+            if((distance = calculateDistance(pois_.at(i).latitude, pois_.at(i).longitude,
+                                             fix->latitude, fix->longitude)) <= distance_)
+            {
+                qDebug() << "Distance: " << distance;
+
+                if(onlyOnRoute_)
+                {
+                    double track = abs(calculateTrack(fix->latitude, fix->longitude,
+                                                      pois_.at(i).latitude, pois_.at(i).longitude) - fix->track);
+
+                    if(track > 180)
+                    {
+                        track = 360.0 - track;
+                    }
+
+                    qDebug() << "Real track: " << track;
+                    qDebug() << "Epd: " << fix->epd;
+
+                    double trackLimit;
+
+                    if(distance < (inRouteMargin * 2.0))
+                    {
+                        trackLimit = 60.0;
+                    }
+                    else
+                    {
+                        trackLimit = 90.0 - radToDeg(acos((inRouteMargin + (distance * 0.15)) / distance));
+                    }
+
+                    qDebug() << "Tracklimit: " << trackLimit;
+
+                    if(track < trackLimit)
+                    {
+                        currentPoi_ = &pois_[i];
+                        currentDistance_ = distance;
+                        playSound(i);
+                    }
+                    else
+                    {
+                        currentPoi_ = 0;
+                    }
+
+                }
+                else
+                {
+                    currentPoi_ = &pois_[i];
+                    currentDistance_ = distance;
+                    playSound(i);
+                }
+
+                break;
+            }
+            else
+            {
+                currentPoi_ = 0;
+                qDebug() << "Distance: " << distance;
+            }
+        }
+    }
+
+    qDebug() << '\n';
+}
+
+void PoiAlerts::startTest()
+{
+    Odometer::instance().start();
+    enabled_ = true;
+    distance_ = 100;
+    PoiReader::Poi poi;
+    poi.latitude = 0.0;
+    poi.longitude = 0.0;
+    poi.name = "Mokki";
+    pois_.push_back(poi);
+    onlyOnRoute_ = true;
+    start();
+}
+
+double PoiAlerts::calculateDistance(double latitude1, double longitude1,
+                         double latitude2, double longitude2)
+{
+    double dlat = degToRad(latitude1 - latitude2);
+    double dlon = degToRad(longitude1 - longitude2);
+    double y = sin(dlat / 2.0) * sin(dlat / 2.0)
+        + cos(degToRad(latitude2))
+        * cos(degToRad(latitude1))
+        * sin(dlon / 2.0) * sin(dlon / 2.0);
+    double x = 2 * atan2(sqrt(y), sqrt(1 - y));
+    return x * EARTH_MEAN_RADIUS * 1000;
+}
+
+double PoiAlerts::calculateTrack(double latitude1, double longitude1,
+                                 double latitude2, double longitude2)
+{
+    double dlon = degToRad(longitude2 - longitude1);
+    double lat1Rad = degToRad(latitude1);
+    double lat2Rad = degToRad(latitude2);
+    double y = sin(dlon) * cos(lat2Rad);
+    double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);
+    double whole;
+    double fraction = modf(radToDeg(atan2(y, x)), &whole);
+    return (int(whole + 360) % 360) + fraction;
+}
+
+void PoiAlerts::playSound(int poiIndex)
+{
+    qDebug() << "Almost play sound";
+
+    if(playedSounds_.indexOf(poiIndex) == -1)
+    {
+        playedSounds_.enqueue(poiIndex);
+
+        qDebug() << "Play sound";
+        MediaPlayer::play(file_);
+
+        QTimer::singleShot(POI_ALERT_INTERVAL * 1000, this, SLOT(removePlayed()));
+    }
+
+}
+
+void PoiAlerts::removePlayed()
+{
+    if(!playedSounds_.isEmpty())
+    {
+        int removed = playedSounds_.dequeue();
+        qDebug() << "Removed: " << removed;
+    }
+}
+
+QString PoiAlerts::getPoiDir()
+{
+    return Settings::getDir() + "pois" + QDir::separator();
+}
+
diff --git a/src/poialerts.h b/src/poialerts.h
new file mode 100644 (file)
index 0000000..8adc163
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POIALERTS_H
+#define POIALERTS_H
+
+#include <QtCore/QList>
+#include <QtCore/QSet>
+#include <QtCore/QQueue>
+#include "poireader.h"
+
+class QString;
+
+class PoiAlerts : public QObject
+{
+    Q_OBJECT
+
+public:
+    static int const POI_ALERT_INTERVAL = 120;
+    static int const IN_ROUTE_MARGIN = 20;
+    static PoiAlerts& instance();
+    static QString getPoiDir();
+    ~PoiAlerts();
+    bool start();
+    void end();
+    void startTest();
+    double getCurrentDistance() const;
+    QString getCurrentPoi() const;
+    bool poiInView() const;
+    QString const& error() const;
+
+public slots:
+    bool loadConfig();
+
+private slots:
+    void onDataUpdated();
+    void removePlayed();
+
+private:
+    PoiAlerts();
+    bool loadPois();
+    void playSound(int poiIndex);
+    static double calculateDistance(double latitude1, double longitude1,
+                                    double latitude2, double longitude2);
+    static double calculateTrack(double latitude1, double longitude1,
+                                 double latitude2, double longitude2);
+    QString file_;
+    bool onlyOnRoute_;
+    QList<PoiReader::Poi> pois_;
+    QQueue<int> playedSounds_;
+    bool enabled_;
+    int distance_;
+    double travelled_;
+    PoiReader::Poi* currentPoi_;
+    double currentDistance_;
+    bool loaded_;
+    QString error_;
+
+};
+
+#endif
diff --git a/src/poiascreader.cpp b/src/poiascreader.cpp
new file mode 100644 (file)
index 0000000..0b44e7f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QFile>
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QRegExp>
+#include "poiascreader.h"
+
+PoiAscReader::PoiAscReader(QString filename): PoiReader(), filename_(filename)
+{
+}
+
+bool PoiAscReader::read(QList<Poi>& pois)
+{
+    QFile file(filename_);
+
+    if(!file.open(QIODevice::ReadOnly))
+    {
+        setError("Unable to open file (check permissions)");
+        return false;
+    }
+
+    QString data = QString(file.readAll());
+    static QRegExp matcher("([0-9]{1,3})\\.([0-9]+), ([0-9]{1,3})\\.([0-9]+), \"(.*)\"");
+    matcher.setMinimal(true);
+
+    int pos = 0;
+
+    while((pos = matcher.indexIn(data, pos)) != -1)
+    {
+        pos += matcher.matchedLength();
+
+        Poi poi;
+
+        bool ok;
+
+        QString latitude = matcher.cap(1) + "." + matcher.cap(2);
+        QString longitude = matcher.cap(3) + "." + matcher.cap(4);
+
+        double doubleLatitude = latitude.toDouble(&ok);
+
+        if(!ok)
+        {
+            continue;
+        }
+
+        double doubleLongitude = longitude.toDouble(&ok);
+
+        if(!ok)
+        {
+            continue;
+        }
+
+        poi.latitude = doubleLatitude;
+        poi.longitude = doubleLongitude;
+        poi.name = matcher.cap(5);
+
+        pois.push_back(poi);
+
+    }
+
+    if(pois.size() == 0)
+    {
+        setError("No valid poi data found from file");
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/poiascreader.h b/src/poiascreader.h
new file mode 100644 (file)
index 0000000..237cf5d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POIASCREADER_H
+#define POIASCREADER_H
+
+#include "poireader.h"
+
+class PoiAscReader : public PoiReader
+{
+public:
+    PoiAscReader(QString filename);
+    virtual bool read(QList<Poi>& pois);
+
+private:
+    QString filename_;
+};
+
+#endif
diff --git a/src/poireader.cpp b/src/poireader.cpp
new file mode 100644 (file)
index 0000000..3078ec6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "poireader.h"
+#include "poiascreader.h"
+
+namespace
+{
+    QString const READER_FILETYPES[PoiReader::READER_COUNT] = {"asc"};
+}
+
+PoiReader::PoiReader()
+{
+}
+
+PoiReader* PoiReader::getReader(QString const& filename)
+{
+    QString lower = filename.toLower();
+    QString type;
+    Reader reader = READER_COUNT;
+
+    for(int i = 0; i < READER_COUNT; i++)
+    {
+        if(lower.right(READER_FILETYPES[i].size()) == READER_FILETYPES[i])
+        {
+            reader = static_cast<Reader>(i);
+            break;
+        }
+    }
+
+    switch(reader)
+    {
+    case ASCREADER:
+        return new PoiAscReader(filename);
+        break;
+    default:
+        return 0;
+    }
+}
+
+void PoiReader::setError(QString const& error)
+{
+    error_ = error;
+}
+
+QString const& PoiReader::error()
+{
+    return error_;
+}
+
+QString PoiReader::getFormatPattern()
+{
+    QString data = "";
+
+    for(int i = 0; i < READER_COUNT; i++)
+    {
+        data += "*." + READER_FILETYPES[i] + " ";
+    }
+
+    return data.trimmed();
+}
+
+void PoiReader::getSupportedFormats(QList<QString>& formats)
+{
+    formats.clear();
+
+    for(int i = 0; i < READER_COUNT; i++)
+    {
+        formats.push_back(READER_FILETYPES[i]);
+    }
+}
diff --git a/src/poireader.h b/src/poireader.h
new file mode 100644 (file)
index 0000000..fced29e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POIREADER_H
+#define POIREADER_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+class PoiReader
+{
+public:
+
+    struct Poi
+    {
+        double latitude;
+        double longitude;
+        QString name;
+    };
+
+    enum Reader {ASCREADER, READER_COUNT};
+
+    static PoiReader* getReader(QString const& filename);
+    PoiReader();
+    virtual bool read(QList<Poi>& pois) = 0;
+    QString const& error();
+    static QString getFormatPattern();
+    static void getSupportedFormats(QList<QString>& formats);
+
+protected:
+    void setError(QString const& error);
+
+private:
+    QString error_;
+
+};
+
+#endif
diff --git a/src/poisettings.cpp b/src/poisettings.cpp
new file mode 100644 (file)
index 0000000..09a4043
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtGui/QFileDialog>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QPushButton>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QLabel>
+#include <QtGui/QCheckBox>
+#include <QtGui/QLineEdit>
+#include <QtGui/QIntValidator>
+#include <QMaemo5InformationBox>
+#include "poisettings.h"
+#include "fileselector.h"
+#include "poialerts.h"
+#include "settings.h"
+#include "mediaplayer.h"
+#include "poireader.h"
+#include "buttonbox.h"
+#include "soundselector.h"
+
+
+PoiSettings::PoiSettings(QWidget* parent): QDialog(parent)
+{
+    setWindowTitle(tr("Speed camera alerts"));
+    enabled_ = new QCheckBox(tr("Enabled"));
+    enabled_->setChecked(Settings::instance().value("alert_enabled", false).toBool());
+
+    QHBoxLayout* poiLayout = new QHBoxLayout;
+    poiFileSelector_ = new FileSelector(tr("Poi file (.asc)"));
+    QPushButton* importButton = new QPushButton(tr("Import"));
+    connect(importButton, SIGNAL(clicked(bool)), this, SLOT(importFile()));
+    poiLayout->addWidget(poiFileSelector_, Qt::AlignLeft);
+    poiLayout->addWidget(importButton);
+
+    soundSelector_ = new SoundSelector;
+
+    QLabel* distanceLabel = new QLabel(tr("Alert distance"));
+    distance_ = new QLineEdit;
+    distance_->setText(QString::number(Settings::instance().value("alert_distance", 300).toInt()));
+    distance_->setValidator(new QIntValidator(0, 5000, this));
+    QHBoxLayout* distance = new QHBoxLayout;
+    distance->addWidget(distanceLabel);
+    distance->addWidget(distance_);
+    onlyOnRoute_ = new QCheckBox(tr("Alert only if poi is in route"));
+    onlyOnRoute_->setChecked(Settings::instance().value("alert_only_on_route", true).toBool());
+
+    ButtonBox* buttons = new ButtonBox;
+    buttons->addButton(tr("Save"), this, SLOT(saveSettings()), QDialogButtonBox::AcceptRole);
+
+    QHBoxLayout* layout = new QHBoxLayout;
+    QVBoxLayout* left = new QVBoxLayout;
+
+    left->addWidget(enabled_);
+    left->addLayout(poiLayout);
+    left->addWidget(soundSelector_);
+    left->addLayout(distance);
+    left->addWidget(onlyOnRoute_);
+
+    layout->addLayout(left, Qt::AlignLeft);
+    layout->addWidget(buttons);
+
+    setLayout(layout);
+}
+
+
+void PoiSettings::importFile()
+{
+    if(!poiFileSelector_->importFile(PoiAlerts::getPoiDir(),
+                                     "Poi files",
+                                     PoiReader::getFormatPattern(),
+                                     true))
+    {
+        qDebug() << "Unable to import";
+    }
+}
+
+void PoiSettings::loadFiles()
+{
+    QDir poiDir(PoiAlerts::getPoiDir());
+
+    QString selectedSound = Settings::instance().value("alert_sound", "").toString();
+    soundSelector_->setValue(selectedSound);
+
+    poiFileSelector_->clear();
+    QString selectedPoi = Settings::instance().value("alert_poi_file", "").toString();
+    poiFileSelector_->loadFiles(PoiAlerts::getPoiDir(), PoiReader::getFormatPattern());
+    poiFileSelector_->selectByValue(selectedPoi);
+
+}
+
+void PoiSettings::setVisible(bool visible)
+{
+    if(visible)
+    {
+        loadFiles();
+    }
+
+    QDialog::setVisible(visible);
+}
+
+void PoiSettings::saveSettings()
+{
+    if(poiFileSelector_->currentIndex() < 0 && enabled_->isChecked())
+    {
+        QMaemo5InformationBox::information(this, tr("No poi file selected"));
+        return;
+    }
+
+    Settings::instance().setValue("alert_enabled", enabled_->isChecked());
+    Settings::instance().setValue("alert_only_on_route", onlyOnRoute_->isChecked());
+    Settings::instance().setValue("alert_distance", distance_->text().toInt());
+    Settings::instance().setValue("alert_sound", soundSelector_->value());
+    Settings::instance().setValue("alert_poi_file", poiFileSelector_->value());
+
+    hide();
+
+    if(!PoiAlerts::instance().loadConfig())
+    {
+        QMaemo5InformationBox::information(0, tr("Unable to load poi file: %1.").arg(PoiAlerts::instance().error()),
+                                           QMaemo5InformationBox::NoTimeout);
+    }
+}
diff --git a/src/poisettings.h b/src/poisettings.h
new file mode 100644 (file)
index 0000000..67cfc7f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POISETTINGS_H
+#define POISETTINGS_H
+
+#include <QtGui/QDialog>
+
+class QLineEdit;
+class QCheckBox;
+class SoundSelector;
+class FileSelector;
+
+class PoiSettings : public QDialog
+{
+    Q_OBJECT
+
+public:
+    PoiSettings(QWidget* parent = 0);
+
+signals:
+    void saved();
+
+protected:
+    virtual void setVisible(bool visible);
+
+private slots:
+    void saveSettings();
+    void importFile();
+
+private:
+    void loadFiles();
+    void loadSounds(QString const& dir, QString const& selected);
+    QCheckBox* enabled_;
+    FileSelector* poiFileSelector_;
+    SoundSelector* soundSelector_;
+    QLineEdit* distance_;
+    QCheckBox* onlyOnRoute_;
+
+};
+
+#endif
index 24f58d4..a01fadf 100644 (file)
@@ -12,6 +12,7 @@
     <file>resources/signal_2.png</file>
     <file>resources/signal_3.png</file>
     <file>resources/signal_4.png</file>
+    <file>resources/sounds/beep.wav</file>
     <file>resources/themes/default/theme.xml</file>
     <file>resources/themes/default/digital7.ttf</file>
     <file>resources/themes/default/background.png</file>
diff --git a/src/resources/sounds/beep.wav b/src/resources/sounds/beep.wav
new file mode 100644 (file)
index 0000000..678fbec
Binary files /dev/null and b/src/resources/sounds/beep.wav differ
diff --git a/src/soundselector.cpp b/src/soundselector.cpp
new file mode 100644 (file)
index 0000000..1d80dbf
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QFile>
+#include <QtGui/QHBoxLayout>
+#include "soundselector.h"
+#include "fileselector.h"
+#include "mediaplayer.h"
+
+SoundSelector::SoundSelector(QWidget* parent): QWidget(parent)
+{
+    selector_ = new FileSelector(tr("Sound"));
+    QPushButton* playButton = new QPushButton(tr("Play"));
+    connect(playButton, SIGNAL(clicked(bool)), this, SLOT(playSound()));
+    QHBoxLayout* layout = new QHBoxLayout;
+    layout->addWidget(selector_, Qt::AlignLeft);
+    layout->addWidget(playButton);
+    setLayout(layout);
+}
+
+void SoundSelector::playSound()
+{
+    QString sound = selector_->value().toString();
+
+    if(sound.isEmpty())
+    {
+        return;
+    }
+
+    QString soundDir = MediaPlayer::getSoundDir();
+    QString localDir = MediaPlayer::getLocalSoundDir();
+
+    if(QFile::exists(soundDir + sound))
+    {
+        MediaPlayer::play(soundDir + sound);
+    }
+    else if(QFile::exists(localDir + sound))
+    {
+        MediaPlayer::play(localDir + sound);
+    }
+}
+
+void SoundSelector::setVisible(bool visible)
+{
+    if(visible)
+    {
+        QString currentValue = selector_->value().toString();
+        selector_->clear();
+        QString pattern = MediaPlayer::getFormatPattern();
+        selector_->loadFiles(MediaPlayer::getLocalSoundDir(), pattern);
+        selector_->loadFiles(MediaPlayer::getSoundDir(), pattern);
+        setValue(currentValue);
+    }
+
+    QWidget::setVisible(visible);
+}
+
+void SoundSelector::setValue(QString const& value)
+{
+    selector_->selectByValue(value);
+}
+
+QString SoundSelector::value() const
+{
+    return selector_->value().toString();
+}
diff --git a/src/soundselector.h b/src/soundselector.h
new file mode 100644 (file)
index 0000000..c9247e9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SOUNDSELECTOR_H
+#define SOUNDSELECTOR_H
+
+#include <QtGui/QWidget>
+
+class FileSelector;
+
+class SoundSelector : public QWidget
+{
+    Q_OBJECT
+
+public:
+    SoundSelector(QWidget* parent = 0);
+
+public slots:
+    void setValue(QString const& value);
+    QString value() const;
+
+protected:
+    virtual void setVisible(bool visible);
+
+private slots:
+    void playSound();
+
+private:
+    FileSelector* selector_;
+};
+
+#endif
diff --git a/src/speedalarm.cpp b/src/speedalarm.cpp
new file mode 100644 (file)
index 0000000..b4908e7
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QFile>
+#include <QMediaPlayer>
+#include "speedalarm.h"
+#include "settings.h"
+#include "odometer.h"
+#include "poialerts.h"
+#include "mediaplayer.h"
+
+SpeedAlarm::SpeedAlarm(): QObject(0), loaded_(false), enabled_(false), isOver_(false)
+{
+}
+
+SpeedAlarm& SpeedAlarm::instance()
+{
+    static SpeedAlarm instance;
+    return instance;
+}
+
+void SpeedAlarm::start()
+{
+    if(!loaded_)
+    {
+        loadConfig();
+    }
+
+    if(enabled_)
+    {
+        connect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
+    }
+
+}
+
+void SpeedAlarm::loadConfig()
+{
+    loaded_ = true;
+
+    bool enabled = Settings::instance().value("alarm_enabled", false).toBool();
+
+    if(enabled)
+    {
+        enabled_ = true;
+
+        QString sound = Settings::instance().value("alarm_sound", "").toString();
+
+        if(sound.isEmpty())
+        {
+            enabled_ = false;
+            return;
+        }
+
+        QString soundDir = MediaPlayer::getSoundDir();
+
+        if(QFile::exists(soundDir + sound))
+        {
+            soundFile_ = soundDir + sound;
+        }
+        else if(QFile::exists(MediaPlayer::getLocalSoundDir() + sound))
+        {
+            soundFile_ = MediaPlayer::getLocalSoundDir() + sound;
+        }
+        else
+        {
+            enabled_ = false;
+            return;
+        }
+
+        threshold_ = Settings::instance().value("alarm_threshold", 0).toInt();
+
+        start();
+    }
+    else
+    {
+        end();
+        enabled_ = false;
+    }
+}
+
+void SpeedAlarm::end()
+{
+    if(enabled_)
+    {
+        disconnect(&(Odometer::instance()), SIGNAL(dataUpdated()), this, SLOT(onDataUpdated()));
+    }
+}
+
+void SpeedAlarm::onDataUpdated()
+{
+    if(Odometer::instance().getLatestFix().kmSpeed > threshold_)
+    {
+        if(!isOver_)
+        {
+            MediaPlayer::play(soundFile_);
+            isOver_ = true;
+        }
+    }
+    else
+    {
+        isOver_ = false;
+    }
+}
diff --git a/src/speedalarm.h b/src/speedalarm.h
new file mode 100644 (file)
index 0000000..10e3581
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SPEEDALARM_H
+#define SPEEDALARM_H
+
+#include <QtCore/QObject>
+
+class SpeedAlarm : public QObject
+{
+    Q_OBJECT
+
+public:
+    static SpeedAlarm& instance();
+    void start();
+    void end();
+
+public slots:
+    void loadConfig();
+
+private slots:
+    void onDataUpdated();
+
+private:
+    SpeedAlarm();
+    bool loaded_;
+    QString soundFile_;
+    int threshold_;
+    bool enabled_;
+    bool isOver_;
+};
+
+#endif
diff --git a/src/speedalarmsettings.cpp b/src/speedalarmsettings.cpp
new file mode 100644 (file)
index 0000000..64e84c2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtGui/QLineEdit>
+#include <QtGui/QLabel>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QPushButton>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QCheckBox>
+#include <QtGui/QIntValidator>
+#include <math.h>
+#include "speedalarmsettings.h"
+#include "speedalarm.h"
+#include "odometer.h"
+#include "settings.h"
+#include "fileselector.h"
+#include "poialerts.h"
+#include "mediaplayer.h"
+#include "soundselector.h"
+#include "buttonbox.h"
+
+
+SpeedAlarmSettings::SpeedAlarmSettings(QWidget* parent): QDialog(parent)
+{
+    setWindowTitle(tr("Speed alarm"));
+
+    speedLabel_ = new QLabel;
+    speed_ = new QLineEdit;
+    speed_->setValidator(new QIntValidator(0, 999, this));
+
+    QPushButton* current = new QPushButton(tr("Current"));
+    connect(current, SIGNAL(clicked(bool)), this, SLOT(loadCurrentSpeed()));
+
+    QHBoxLayout* speedLayout = new QHBoxLayout;
+    speedLayout->addWidget(speedLabel_);
+    speedLayout->addWidget(speed_);
+    speedLayout->addWidget(current);
+
+    soundSelector_ = new SoundSelector;
+
+    enabled_ = new QCheckBox(tr("Enabled"));
+    enabled_->setChecked(Settings::instance().value("alarm_enabled", false).toBool());
+
+    ButtonBox* buttons = new ButtonBox;
+    buttons->addButton(tr("Save"), this, SLOT(saveSettings()), QDialogButtonBox::AcceptRole);
+
+    QHBoxLayout* layout = new QHBoxLayout;
+    QVBoxLayout* left = new QVBoxLayout;
+
+    left->addLayout(speedLayout);
+    left->addWidget(soundSelector_);
+    left->addWidget(enabled_);
+    layout->addLayout(left, Qt::AlignLeft);
+    layout->addWidget(buttons);
+
+    setLayout(layout);
+}
+
+
+void SpeedAlarmSettings::loadData()
+{
+    speedLabel_->setText(tr("Speed threshold (%1)").arg(Odometer::getSpeedUnit()));
+    int speedValue = round(Settings::instance().value("alarm_threshold", 100).toDouble() * Odometer::getUnitMultiplier());
+    speed_->setText(QString::number(static_cast<int>(speedValue)));
+
+    QString selected = Settings::instance().value("alarm_sound", "").toString();
+    soundSelector_->setValue(selected);
+}
+
+void SpeedAlarmSettings::loadCurrentSpeed()
+{
+    double speed = round(Odometer::instance().getLatestFix().speed);
+    speed_->setText(QString::number(static_cast<int>(speed)));
+}
+
+void SpeedAlarmSettings::saveSettings()
+{
+    double kmSpeed = speed_->text().toInt() / Odometer::getUnitMultiplier();
+
+    Settings::instance().setValue("alarm_threshold", kmSpeed);
+    Settings::instance().setValue("alarm_enabled", enabled_->isChecked());
+    Settings::instance().setValue("alarm_sound", soundSelector_->value());
+
+    SpeedAlarm::instance().loadConfig();
+
+    hide();
+}
+
+void SpeedAlarmSettings::setVisible(bool visible)
+{
+    if(visible)
+    {
+        loadData();
+    }
+
+    QDialog::setVisible(visible);
+}
diff --git a/src/speedalarmsettings.h b/src/speedalarmsettings.h
new file mode 100644 (file)
index 0000000..aa7d97e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SPEEDALARMSETTINGS_H
+#define SPEEDALARMSETTINGS_H
+
+#include <QtGui/QDialog>
+
+class QLineEdit;
+class QLabel;
+class QCheckBox;
+class FileSelector;
+class SoundSelector;
+
+class SpeedAlarmSettings : public QDialog
+{
+    Q_OBJECT
+
+public:
+    SpeedAlarmSettings(QWidget* parent = 0);
+
+protected:
+    virtual void setVisible(bool visible);
+
+private slots:
+    void saveSettings();
+    void loadCurrentSpeed();
+
+private:
+    void loadData();
+    QLabel* speedLabel_;
+    QLineEdit* speed_;
+    SoundSelector* soundSelector_;
+    QCheckBox* enabled_;
+};
+
+#endif
index b554088..768999b 100644 (file)
@@ -33,6 +33,7 @@
 #include "detailscreen.h"
 #include "settings.h"
 #include "widgetscreen.h"
+#include "themescheduler.h"
 
 namespace
 {
@@ -51,13 +52,25 @@ ThemeLoader::~ThemeLoader()
 
 bool ThemeLoader::load()
 {
-    QString theme = Settings::instance().value("theme", "default").toString();
+    if(theme_.isEmpty())
+    {
+        connect(&(ThemeScheduler::instance()), SIGNAL(themeChanged()), this, SLOT(load()));
+    }
+
+    QString theme = ThemeScheduler::instance().currentTheme();
+
+    if(theme == theme_)
+    {
+        return true;
+    }
+
+    theme_ = theme;
 
     QString themeDir = getThemeDir();
 
-    if(QFile::exists(themeDir + theme + THEME_SUFFIX))
+    if(QFile::exists(themeDir + theme_ + THEME_SUFFIX))
     {
-        reader_ = new ZipReader(themeDir + theme + THEME_SUFFIX);
+        reader_ = new ZipReader(themeDir + theme_ + THEME_SUFFIX);
 
         if(read())
         {
@@ -71,17 +84,17 @@ bool ThemeLoader::load()
         }
     }
 
-    theme = "default";
-    Settings::instance().setValue("theme", theme);
+    theme_ = ThemeScheduler::instance().getDefaultTheme();
+    Settings::instance().setValue("theme", theme_);
 
-    if(QFile::exists(RESOURCE_DIR + theme))
+    if(QFile::exists(RESOURCE_DIR + theme_))
     {
         if(reader_)
         {
             delete reader_;
         }
 
-        reader_ = new FileReader(RESOURCE_DIR + theme);
+        reader_ = new FileReader(RESOURCE_DIR + theme_);
         return read();
     }
 
index bdf2ce2..f5797a2 100644 (file)
@@ -28,17 +28,22 @@ class WidgetScreen;
 
 class ThemeLoader : public QObject
 {
+    Q_OBJECT
+
 public:
     ThemeLoader(WidgetScreen* mainScreen, WidgetScreen* detailScreen, QObject* parent = 0);
     ~ThemeLoader();
-    bool load();
     QString const& error() const;
     static QString getThemeDir();
     static QString const& getThemeSuffix();
 
+public slots:
+    bool load();
+
 private:
     bool read();
     bool loadScreen(QString const& tag, QDomDocument const& doc, WidgetScreen* screen);
+    QString theme_;
     Reader* reader_;
     QString error_;
     WidgetScreen* mainScreen_;
diff --git a/src/themepicker.cpp b/src/themepicker.cpp
new file mode 100644 (file)
index 0000000..f7d3e44
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include "themepicker.h"
+#include "themescheduler.h"
+#include "themeloader.h"
+#include "settings.h"
+
+ThemePicker::ThemePicker(QString const& text, QWidget* parent):
+FileSelector(text, parent)
+{
+}
+
+void ThemePicker::setVisible(bool visible)
+{
+    if(visible)
+    {
+        loadThemes();
+    }
+
+    FileSelector::setVisible(visible);
+}
+
+void ThemePicker::loadThemes()
+{
+    QString current = Settings::instance().value("theme", ThemeScheduler::getDefaultTheme()).toString();
+    clear();
+    addItem(tr("Default"), ThemeScheduler::getDefaultTheme());
+
+    QDir themeDir(ThemeLoader::getThemeDir());
+
+    if(!themeDir.exists() || !themeDir.isReadable())
+    {
+        qDebug() << "Warning: theme dir (" + ThemeLoader::getThemeDir() + ") doesn't exist or is read protected";
+        return;
+    }
+
+    QStringList filters;
+    filters << "*" + ThemeLoader::getThemeSuffix();
+    themeDir.setNameFilters(filters);
+    themeDir.setFilter(QDir::Files);
+    QStringList files = themeDir.entryList();
+
+    for(int i = 0; i < files.size(); i++)
+    {
+        QString name;
+        QString id;
+        getThemeDetails(files.at(i), name, id);
+        addItem(name, id);
+
+        if(id == current)
+        {
+            setCurrentIndex(i + 1);
+        }
+    }
+}
+
+bool ThemePicker::selectTheme(QString const& filename)
+{
+    QString name;
+    QString id;
+    getThemeDetails(filename, name, id);
+    return selectByValue(id);
+}
+
+void ThemePicker::getThemeDetails(QString filename,
+                                  QString& name,
+                                  QString& id)
+{
+    static QRegExp cleaner(QRegExp::escape(ThemeLoader::getThemeSuffix()) + "$");
+    filename = filename.replace(cleaner, "");
+
+    id = filename;
+    name = filename.at(0).toUpper() + filename.mid(1);
+}
diff --git a/src/themepicker.h b/src/themepicker.h
new file mode 100644 (file)
index 0000000..5339faa
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef THEMEPICKER_H
+#define THEMEPICKER_H
+
+#include "fileselector.h"
+
+class ThemePicker : public FileSelector
+{
+    Q_OBJECT
+
+public:
+    ThemePicker(QString const& text = tr("Theme"), QWidget* parent = 0);
+    bool selectTheme(QString const& name);
+
+public slots:
+    void loadThemes();
+
+protected:
+    virtual void setVisible(bool visible);
+
+private:
+    void getThemeDetails(QString filename, QString& name, QString& id);
+    QString theme_;
+};
+
+#endif
diff --git a/src/themescheduler.cpp b/src/themescheduler.cpp
new file mode 100644 (file)
index 0000000..a09225f
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include "themescheduler.h"
+#include "settings.h"
+
+namespace
+{
+    QString const DEFAULT_THEME = "default";
+}
+
+ThemeScheduler::ThemeScheduler(): QObject(0), enabled_(false)
+{
+    loadConfig();
+}
+
+ThemeScheduler::~ThemeScheduler()
+{
+    store();
+    clear();
+}
+
+ThemeScheduler& ThemeScheduler::instance()
+{
+    static ThemeScheduler instance;
+    return instance;
+}
+
+void ThemeScheduler::addItem(QTime const& time, QString const& theme)
+{
+    removeItem(time);
+    ItemDetails* details = new ItemDetails;
+    details->time = time;
+    details->theme = theme;
+
+    int msecs = QTime::currentTime().msecsTo(time);
+
+    if(msecs < 0)
+    {
+        msecs += 86400000;
+    }
+
+    details->timer.setSingleShot(true);
+    details->timer.start(msecs + 500);
+    connect(&details->timer, SIGNAL(timeout()), this, SLOT(emitThemeChanged()));
+
+    for(int i = 0; i < items_.size(); i++)
+    {
+        if(items_.at(i)->time > time)
+        {
+            items_.insert(i, details);
+            return;
+        }
+    }
+
+    items_.push_back(details);
+}
+
+void ThemeScheduler::removeItem(QTime const& time)
+{
+    for(int i = 0; i < items_.size(); i++)
+    {
+        if(items_.at(i)->time == time)
+        {
+            delete items_.at(i);
+            items_.removeAt(i);
+            break;
+        }
+    }
+}
+
+void ThemeScheduler::store()
+{
+    if(items_.isEmpty())
+    {
+        Settings::instance().remove("theme_scheduler");
+        return;
+    }
+
+    QList<QVariant> list;
+
+    for(int i = 0; i < items_.size(); i++)
+    {
+        QMap<QString, QVariant> map;
+        map["time"] = items_.at(i)->time.toString();
+        map["theme"] = items_.at(i)->theme;
+        list.push_back(map);
+    }
+
+    Settings::instance().setValue("theme_scheduler", list);
+}
+
+void ThemeScheduler::loadConfig()
+{
+    enabled_ = Settings::instance().value("theme_scheduler_enabled", false).toBool();
+
+    QList<QVariant> list = Settings::instance().value("theme_scheduler").toList();
+
+    for(int i = 0; i < list.size(); i++)
+    {
+        QMap<QString, QVariant> map = list.at(i).toMap();
+        QTime time = QTime::fromString(map["time"].toString());
+        addItem(time, map["theme"].toString());
+    }
+}
+
+void ThemeScheduler::setEnabled(bool enabled)
+{
+    enabled_ = enabled;
+}
+
+bool ThemeScheduler::isEnabled() const
+{
+    return enabled_;
+}
+
+QString ThemeScheduler::currentTheme() const
+{
+    if(enabled_)
+    {
+        QTime time = QTime::currentTime();
+
+        for(int i = 0; i < items_.size(); i++)
+        {
+            if(items_.at(i)->time >= time)
+            {
+                int index = i - 1;
+
+                if(index < 0)
+                {
+                    index = items_.size() - 1;
+                }
+
+                return items_.at(index)->theme;
+            }
+        }
+
+        if(items_.size() > 0)
+        {
+            return items_.at(items_.size() - 1)->theme;
+        }
+    }
+
+    QString theme = Settings::instance().value("theme", DEFAULT_THEME).toString();
+
+    if(theme.isEmpty())
+    {
+        return DEFAULT_THEME;
+    }
+
+    return theme;
+}
+
+QString const& ThemeScheduler::getDefaultTheme()
+{
+    return DEFAULT_THEME;
+}
+
+void ThemeScheduler::clear()
+{
+    for(int i = 0; i < items_.size(); i++)
+    {
+        delete items_.at(i);
+    }
+
+    items_.clear();
+}
+
+void ThemeScheduler::getItems(QList<SchedulerItem>& items)
+{
+    items.clear();
+
+    for(int i = 0; i < items_.size(); i++)
+    {
+        SchedulerItem item;
+        item.time = items_.at(i)->time;
+        item.theme = items_.at(i)->theme;
+        items.push_back(item);
+    }
+}
+
+void ThemeScheduler::emitThemeChanged()
+{
+    if(enabled_)
+    {
+        emit themeChanged();
+    }
+}
diff --git a/src/themescheduler.h b/src/themescheduler.h
new file mode 100644 (file)
index 0000000..0501964
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef THEMESCHEDULER_H
+#define THEMESCHEDULER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QList>
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
+
+class QString;
+
+class ThemeScheduler : public QObject
+{
+    Q_OBJECT
+
+public:
+
+    struct SchedulerItem
+    {
+        QTime time;
+        QString theme;
+    };
+
+    ~ThemeScheduler();
+    static ThemeScheduler& instance();
+    static QString const& getDefaultTheme();
+    void addItem(QTime const& time, QString const& theme);
+    void removeItem(QTime const& time);
+    void setEnabled(bool enabled);
+    bool isEnabled() const;
+    QString currentTheme() const;
+    void clear();
+    void getItems(QList<SchedulerItem>& items);
+
+public slots:
+    void store();
+
+signals:
+    void themeChanged();
+
+private slots:
+    void emitThemeChanged();
+
+private:
+    struct ItemDetails
+    {
+        QTime time;
+        QString theme;
+        QTimer timer;
+    };
+
+    ThemeScheduler();
+    void loadConfig();
+    void sort();
+    bool enabled_;
+    QList<ItemDetails*> items_;
+
+
+};
+
+#endif
diff --git a/src/themeschedulersettings.cpp b/src/themeschedulersettings.cpp
new file mode 100644 (file)
index 0000000..f736626
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtCore/QDebug>
+#include <QtGui/QMenu>
+#include <QtGui/QApplication>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QCheckBox>
+#include <QtGui/QLabel>
+#include <QtGui/QScrollArea>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QPushButton>
+#include <QtGui/QListWidget>
+#include <QMaemo5ValueButton>
+#include <QMaemo5TimePickSelector>
+#include "themeschedulersettings.h"
+#include "themescheduler.h"
+#include "themepicker.h"
+#include "buttonbox.h"
+
+namespace
+{
+    QString const TIME_FORMAT = "hh:mm";
+}
+
+ThemeSchedulerSettings::ThemeSchedulerSettings(QWidget* parent):
+QDialog(parent), currentWidget_(0), addDialog_(0), itemList_(0)
+{
+    setWindowTitle(tr("Theme scheduler"));
+
+    QHBoxLayout* layout = new QHBoxLayout;
+    layout_ = new QVBoxLayout;
+
+    enabled_ = new QCheckBox(tr("Enabled"));
+    enabled_->setChecked(ThemeScheduler::instance().isEnabled());
+
+    QLabel* info = new QLabel(tr("Scheduled themes:"));
+
+    layout_->addWidget(enabled_);
+    layout_->addWidget(info);
+
+    loadItems();
+
+    ButtonBox* buttons = new ButtonBox;
+    buttons->addButton(tr("Save"), this, SLOT(saveSettings()), QDialogButtonBox::AcceptRole);
+    buttons->addButton(tr("Add new theme"), this, SLOT(openAddDialog()), QDialogButtonBox::ActionRole);
+    buttons->addButton(tr("Clear scheduler"), this, SLOT(clearScheduler()), QDialogButtonBox::ActionRole);
+
+    layout->addLayout(layout_, Qt::AlignLeft);
+    layout->addWidget(buttons);
+
+    setLayout(layout);
+
+}
+
+void ThemeSchedulerSettings::openAddDialog()
+{
+    if(!addDialog_)
+    {
+        addDialog_ = new QDialog(this);
+        addDialog_->setWindowTitle("Add scheduled theme");
+        QHBoxLayout* layout = new QHBoxLayout;
+        QVBoxLayout* left = new QVBoxLayout;
+        timeButton_ = new QMaemo5ValueButton(tr("Start time"));
+        timeButton_->setValueLayout(QMaemo5ValueButton::ValueBesideText);
+        pickSelector_ = new QMaemo5TimePickSelector;
+        timeButton_->setPickSelector(pickSelector_);
+        themePicker_ = new ThemePicker(tr("Theme"));
+
+        ButtonBox* buttons = new ButtonBox;
+        buttons->addButton(tr("Add"), this, SLOT(addScheduledTheme()));
+
+        left->addWidget(timeButton_);
+        left->addWidget(themePicker_);
+
+        layout->addLayout(left);
+        layout->addWidget(buttons);
+
+        addDialog_->setLayout(layout);
+
+    }
+
+    pickSelector_->setCurrentTime(QTime::currentTime());
+    addDialog_->show();
+}
+
+void ThemeSchedulerSettings::addScheduledTheme()
+{
+    QTime time = QTime::fromString(timeButton_->valueText(), TIME_FORMAT);
+    QString theme = themePicker_->value().toString();
+    ThemeScheduler::instance().addItem(time, theme);
+    loadItems();
+    addDialog_->hide();
+}
+
+void ThemeSchedulerSettings::clearScheduler()
+{
+    ThemeScheduler::instance().clear();
+    loadItems();
+}
+
+void ThemeSchedulerSettings::loadItems()
+{
+    ThemeScheduler::instance().getItems(items_);
+    itemList_ = 0;
+
+    if(currentWidget_)
+    {
+        layout_->removeWidget(currentWidget_);
+        delete currentWidget_;
+        currentWidget_ = 0;
+    }
+
+    if(items_.isEmpty())
+    {
+        QLabel* label = new QLabel(tr("There are currently no scheduled themes."));
+        label->setWordWrap(true);
+        label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+        currentWidget_ = label;
+        layout_->addWidget(label);
+    }
+    else
+    {
+        itemList_ = new QListWidget;
+        itemList_->setContextMenuPolicy(Qt::CustomContextMenu);
+        connect(itemList_, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)));
+
+        for(int i = 0; i < items_.size(); i++)
+        {
+            const QTime* endTime = 0;
+
+            if(i < items_.size() - 1)
+            {
+                endTime = &(items_.at(i + 1).time);
+            }
+            else
+            {
+                endTime = &(items_.at(0).time);
+            }
+
+            QString theme = items_.at(i).theme;
+            QString text = items_.at(i).time.toString(TIME_FORMAT) + " - " +
+                              endTime->toString(TIME_FORMAT) + ": " +
+                              theme.at(0).toUpper() + theme.mid(1);
+            QListWidgetItem* item = new QListWidgetItem(text);
+            item->setData(Qt::UserRole, items_.at(i).time.toString(TIME_FORMAT));
+            itemList_->addItem(item);
+        }
+
+        currentWidget_ = itemList_;
+        layout_->addWidget(itemList_);
+    }
+
+    QApplication::processEvents();
+    adjustSize();
+    QApplication::processEvents();
+
+}
+
+void ThemeSchedulerSettings::saveSettings()
+{
+    ThemeScheduler::instance().setEnabled(enabled_->isChecked());
+    hide();
+}
+
+void ThemeSchedulerSettings::showContextMenu(QPoint const& point)
+{
+    if(!itemList_)
+    {
+        return;
+    }
+
+    QMenu* menu = new QMenu(itemList_);
+    menu->addAction(tr("Remove"), this, SLOT(removeCurrent()));
+    connect(menu, SIGNAL(aboutToHide()), this, SLOT(removeSelection()));
+    menu->popup(itemList_->mapToGlobal(point));
+}
+
+void ThemeSchedulerSettings::removeCurrent()
+{
+    if(itemList_)
+    {
+        QListWidgetItem* item = itemList_->currentItem();
+
+        if(item)
+        {
+            ThemeScheduler::instance().removeItem(QTime::fromString(item->data(Qt::UserRole).toString(), TIME_FORMAT));
+            loadItems();
+        }
+    }
+}
+
+void ThemeSchedulerSettings::removeSelection()
+{
+    if(itemList_)
+    {
+        itemList_->clearSelection();
+    }
+}
diff --git a/src/themeschedulersettings.h b/src/themeschedulersettings.h
new file mode 100644 (file)
index 0000000..c4a4fcd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of jSpeed.
+ *
+ * jSpeed is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * jSpeed is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jSpeed.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef THEMESCHEDULERSETTINGS_H
+#define THEMESCHEDULERSETTINGS_H
+
+#include <QtGui/QDialog>
+#include "themescheduler.h"
+
+class QCheckBox;
+class QScrollArea;
+class QMaemo5ValueButton;
+class QMaemo5TimePickSelector;
+class QVBoxLayout;
+class QListWidget;
+class ThemePicker;
+
+class ThemeSchedulerSettings : public QDialog
+{
+    Q_OBJECT
+
+public:
+    ThemeSchedulerSettings(QWidget* parent = 0);
+
+private slots:
+    void openAddDialog();
+    void addScheduledTheme();
+    void clearScheduler();
+    void saveSettings();
+    void showContextMenu(QPoint const& point);
+    void removeCurrent();
+    void removeSelection();
+
+private:
+    void loadItems();
+    QCheckBox* enabled_;
+    QVBoxLayout* layout_;
+    QWidget* currentWidget_;
+    QDialog* addDialog_;
+    QMaemo5TimePickSelector* pickSelector_;
+    QMaemo5ValueButton* timeButton_;
+    ThemePicker* themePicker_;
+    QList<ThemeScheduler::SchedulerItem> items_;
+    QListWidget* itemList_;
+};
+
+#endif
index e66d925..9f377ac 100644 (file)
 #include <QtGui/QVBoxLayout>
 #include <QtGui/QMessageBox>
 #include "themeselector.h"
-#include "buttonselector.h"
+#include "themepicker.h"
 #include "themeloader.h"
 #include "settings.h"
+#include "themeschedulersettings.h"
 
-ThemeSelector::ThemeSelector(QWidget* parent): QDialog(parent)
+ThemeSelector::ThemeSelector(QWidget* parent): QDialog(parent), themeScheduler_(0)
 {
     setWindowTitle(tr("Select theme"));
 
     QHBoxLayout* layout = new QHBoxLayout;
-    QHBoxLayout* left = new QHBoxLayout;
+    QVBoxLayout* left = new QVBoxLayout;
+    QHBoxLayout* first = new QHBoxLayout;
 
     QPushButton* saveButton = new QPushButton(tr("Save"));
     connect(saveButton, SIGNAL(clicked(bool)), this, SLOT(saveTheme()));
     QDialogButtonBox* buttons = new QDialogButtonBox;
     buttons->setCenterButtons(false);
+    buttons->setOrientation(Qt::Vertical);
     buttons->addButton(saveButton, QDialogButtonBox::AcceptRole);
 
-    selector_ = new ButtonSelector(tr("Theme"), this);
+    selector_ = new ThemePicker(tr("Theme"), this);
     theme_ = Settings::instance().value("theme", "default").toString();
 
     QPushButton* loadButton = new QPushButton(tr("Import"));
     connect(loadButton, SIGNAL(clicked(bool)), this, SLOT(loadFromFile()));
 
-    left->addWidget(selector_, Qt::AlignLeft);
-    left->addWidget(loadButton);
+    QPushButton* scheduler = new QPushButton(tr("Theme scheduler"));
+    connect(scheduler, SIGNAL(clicked(bool)), this, SLOT(openScheduler()));
+
+    first->addWidget(selector_, Qt::AlignLeft);
+    first->addWidget(loadButton);
+
+    left->addLayout(first);
+    left->addWidget(scheduler);
 
     layout->addLayout(left, Qt::AlignLeft);
     layout->addWidget(buttons);
@@ -78,115 +87,25 @@ void ThemeSelector::saveTheme()
 
 void ThemeSelector::loadFromFile()
 {
-    QString filename = QFileDialog::getOpenFileName(this, tr("Open file"),
-                                                    QDir::home().path(),
-                                                    tr("Theme files") + "(*" + ThemeLoader::getThemeSuffix() + ")");
-
-    if(filename.isEmpty())
-    {
-        return;
-    }
-
-    qDebug() << filename;
-
-    QString basename;
-    int i = filename.lastIndexOf(QDir::separator());
-
-    if(i == -1)
-    {
-        basename = filename;
-    }
-    else
-    {
-        basename = filename.mid(i + 1);
-    }
-
-    QString targetFile = ThemeLoader::getThemeDir() + basename;
-
-    qDebug() << targetFile;
-
-    bool ok = true;
-
-    if(QFile::exists(targetFile))
-    {
-        ok = confirmCopy(basename);
-
-        if(ok)
-        {
-            QFile::remove(targetFile);
-        }
-    }
-
-    if(ok)
-    {
-        QFile::copy(filename, targetFile);
-        QString name;
-        QString id;
-        getThemeDetails(basename, name, id);
-        theme_ = id;
-        loadThemes();
-    }
-}
-
-void ThemeSelector::loadThemes()
-{
-    selector_->clear();
-    selector_->addItem(tr("Default"), "default");
-    QDir themeDir(ThemeLoader::getThemeDir());
-
-    if(!themeDir.exists() || !themeDir.isReadable())
-    {
-        qDebug() << "Warning: theme dir (" + ThemeLoader::getThemeDir() + ") doesn't exist or is read protected";
-        return;
-    }
+    QString file;
 
-    QStringList filters;
-    filters << "*" + ThemeLoader::getThemeSuffix();
-    themeDir.setNameFilters(filters);
-    themeDir.setFilter(QDir::Files);
-    QStringList files = themeDir.entryList();
-
-    for(int i = 0; i < files.size(); i++)
+    if(selector_->importFile(ThemeLoader::getThemeDir(),
+                          "Theme files",
+                          "*" + ThemeLoader::getThemeSuffix(),
+                          false,
+                          &file))
     {
-        QString name;
-        QString id;
-        getThemeDetails(files.at(i), name, id);
-        selector_->addItem(name, id);
-
-        if(id == theme_)
-        {
-            selector_->setCurrentIndex(i + 1);
-        }
+        selector_->loadThemes();
+        selector_->selectTheme(file);
     }
-
-}
-
-void ThemeSelector::getThemeDetails(QString filename,
-                                    QString& name,
-                                    QString& id)
-{
-    static QRegExp cleaner(QRegExp::escape(ThemeLoader::getThemeSuffix()) + "$");
-    filename = filename.replace(cleaner, "");
-
-    id = filename;
-    name = filename.at(0).toUpper() + filename.mid(1);
-}
-
-bool ThemeSelector::confirmCopy(QString const& filename)
-{
-    QMessageBox::StandardButton result =  QMessageBox::question(this, tr("File exists"),
-             tr("Theme file %1 already exists in theme directory, overwrite?").arg(filename),
-             QMessageBox::Yes | QMessageBox::No);
-
-    return (result == QMessageBox::Yes);
 }
 
-void ThemeSelector::setVisible(bool visible)
+void ThemeSelector::openScheduler()
 {
-    if(visible)
+    if(!themeScheduler_)
     {
-        loadThemes();
+        themeScheduler_ = new ThemeSchedulerSettings(this);
     }
 
-    QDialog::setVisible(visible);
+    themeScheduler_->show();
 }
index 7d29ac8..ec5f943 100644 (file)
@@ -22,7 +22,8 @@
 #include <QtGui/QDialog>
 
 class QString;
-class ButtonSelector;
+class ThemePicker;
+class ThemeSchedulerSettings;
 
 class ThemeSelector : public QDialog
 {
@@ -37,16 +38,12 @@ signals:
 private slots:
     void saveTheme();
     void loadFromFile();
-
-protected:
-    virtual void setVisible(bool visible);
+    void openScheduler();
 
 private:
-    void loadThemes();
-    void getThemeDetails(QString filename, QString& name, QString& id);
-    bool confirmCopy(QString const& name);
-    ButtonSelector* selector_;
+    ThemePicker* selector_;
     QString theme_;
+    ThemeSchedulerSettings* themeScheduler_;
 };
 
 
index 90cadfa..61a0fb8 100644 (file)
@@ -23,6 +23,7 @@
 #include "unitselector.h"
 #include "buttonselector.h"
 #include "settings.h"
+#include "buttonbox.h"
 
 UnitSelector::UnitSelector(QWidget* parent): QDialog(parent)
 {
@@ -44,11 +45,8 @@ UnitSelector::UnitSelector(QWidget* parent): QDialog(parent)
 
     layout->addWidget(selector_, Qt::AlignLeft);
 
-    QPushButton* button = new QPushButton(tr("Save"));
-    connect(button, SIGNAL(clicked(bool)), this, SLOT(saveUnit()));
-    QDialogButtonBox* buttons = new QDialogButtonBox;
-    buttons->setCenterButtons(false);
-    buttons->addButton(button, QDialogButtonBox::AcceptRole);
+    ButtonBox* buttons = new ButtonBox;
+    buttons->addButton(tr("Save"), this, SLOT(saveUnit()));
 
     layout->addWidget(buttons);