summaryrefslogtreecommitdiffstats
path: root/examples/mediaplayer
diff options
context:
space:
mode:
authorNoam Rosenthal <nrosenth@nokia.com>2009-06-08 12:27:03 -0700
committerNoam Rosenthal <nrosenth@nokia.com>2009-06-08 12:27:03 -0700
commitd0441f605434a89b53735427e4e81182c65debbd (patch)
treeb96d25dc89cdb523c007a22bc0deed3a5aa5dd56 /examples/mediaplayer
parenta6553f68f17c28adca049857686496a69b4c1e7a (diff)
scxml for 4.6
Diffstat (limited to 'examples/mediaplayer')
-rw-r--r--examples/mediaplayer/main.cpp58
-rw-r--r--examples/mediaplayer/mediaplayer.pro23
-rw-r--r--examples/mediaplayer/mediaplayer.qrc5
-rw-r--r--examples/mediaplayer/mediaplayer.scxml240
-rw-r--r--examples/mediaplayer/mediaplayer.ui145
-rw-r--r--examples/mediaplayer/songdata.h15
-rw-r--r--examples/mediaplayer/spengine.cpp84
-rw-r--r--examples/mediaplayer/spengine.h41
-rw-r--r--examples/mediaplayer/spharvester.cpp68
-rw-r--r--examples/mediaplayer/spharvester.h25
-rw-r--r--examples/mediaplayer/spmodel.cpp274
-rw-r--r--examples/mediaplayer/spmodel.h64
-rw-r--r--examples/mediaplayer/spview.cpp97
-rw-r--r--examples/mediaplayer/spview.h36
14 files changed, 1175 insertions, 0 deletions
diff --git a/examples/mediaplayer/main.cpp b/examples/mediaplayer/main.cpp
new file mode 100644
index 0000000..1eb7b4b
--- /dev/null
+++ b/examples/mediaplayer/main.cpp
@@ -0,0 +1,58 @@
+#include "qscriptedstatemachine.h"
+#include "spview.h"
+#include "spmodel.h"
+#include "spengine.h"
+#include "spharvester.h"
+#include "math.h"
+#include "time.h"
+#include <QDebug>
+#include <QApplication>
+#include <QLabel>
+#include <QPushButton>
+#include <QScriptEngine>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QFileInfo>
+#include <QScriptContext>
+#include <QScriptEngine>
+#include <QMenu>
+#include <QMainWindow>
+
+
+int main( int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ QString dir;
+ bool recurse;
+ if (argc > 1) {
+ dir = QString(argv[1]);
+ if (argc > 2)
+ {
+ recurse = !strcmp(argv[2],"-recurse");
+ }
+ } else {
+ printf("Usage: stateplayer directory [-recurse]");
+ return 0;
+ }
+
+ app.setApplicationName("SCXML-mediaplayer");
+
+ SPView* view = new SPView(NULL);
+ QtScriptedStateMachine *sm = QtScriptedStateMachine::load(":/mediaplayer.scxml");
+ QObject::connect (sm, SIGNAL(finished()), &app, SLOT(quit()));
+ SPModel* model= new SPModel(NULL);
+ view->setModel(model);
+ model->setObjectName("model");
+ SPEngine* engine = new SPEngine(sm);
+ engine->setObjectName("engine");
+ SPHarvester* harvester = new SPHarvester (view);
+ QObject::connect (harvester, SIGNAL(foundTrack(SongData)), model, SLOT(addSong(SongData)));
+ harvester->harvest(dir,recurse);
+ view->setObjectName("view");
+ sm->registerObject(model);
+ sm->registerObject(engine);
+ sm->registerObject(view,"",true);
+ view->show ();
+ sm->start ();
+ return app.exec ();
+}
diff --git a/examples/mediaplayer/mediaplayer.pro b/examples/mediaplayer/mediaplayer.pro
new file mode 100644
index 0000000..355da25
--- /dev/null
+++ b/examples/mediaplayer/mediaplayer.pro
@@ -0,0 +1,23 @@
+TEMPLATE = app
+TARGET = scxmlplayer
+QT += script \
+ sql \
+ phonon
+include($$PWD/../../src/qtstatemachine.pri)
+
+HEADERS += spmodel.h \
+ spengine.h \
+ spview.h \
+ spharvester.h \
+ songdata.h
+SOURCES += main.cpp \
+ spmodel.cpp \
+ spengine.cpp \
+ spview.cpp \
+ spharvester.cpp
+FORMS += mediaplayer.ui
+RESOURCES += mediaplayer.qrc
+win32:CONFIG += console
+mac:CONFIG -= app_bundle
+INCLUDEPATH += .
+DEPENDPATH += .
diff --git a/examples/mediaplayer/mediaplayer.qrc b/examples/mediaplayer/mediaplayer.qrc
new file mode 100644
index 0000000..ff8226f
--- /dev/null
+++ b/examples/mediaplayer/mediaplayer.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource>
+ <file>mediaplayer.scxml</file>
+ </qresource>
+</RCC>
diff --git a/examples/mediaplayer/mediaplayer.scxml b/examples/mediaplayer/mediaplayer.scxml
new file mode 100644
index 0000000..323d9c9
--- /dev/null
+++ b/examples/mediaplayer/mediaplayer.scxml
@@ -0,0 +1,240 @@
+<scxml xmlns="http://www.w3.org/2005/07/scxml" initial="root">
+ <parallel id="root">
+ <state initial="gui">
+ <invoke targettype="q-bindings">
+ <content>[[listView,'styleSheet','QListView {font-size:20px}']]</content>
+ </invoke>
+ <state id="gui" initial="menu_tree">
+ <transition event="q-signal:backButton.clicked()" anchor="lastmenupos" />
+ <transition event="q-signal:homeButton.clicked()" target="mainmenu" />
+ <state id="menu_tree" initial="mainmenu">
+ <invoke type="q-binding"><content>[[stackedWidget,"currentIndex",0]]</content></invoke>
+ <state id="mainmenu">
+ <anchor type="lastmenupos" />
+ <onentry><script>
+ homeButton.enabled = false;
+ selectButton.enabled = false;
+ </script></onentry>
+ <onexit><script>
+ homeButton.enabled = true;
+ selectButton.enabled = true;
+ </script></onexit>
+ <invoke type="q-menu">
+ <content>
+ { "parent" : listView, "windowTitle" :
+ "Music Player", "styleSheet" : "QMenu {font-size:24px; width: 505;height:1000;}" +
+ "QMenu::item:hover {background-color: blue;color: black;}",
+ "children": function() { var c = [
+ {"type" : "action","id" : "artists", "text" : "Artists" },
+ {"type" : "action", "id" : "albums", "text" : "Albums" },
+ {"type" : "action", "id" : "genres", "text" : "Genres" },
+ {"type" : "action", "id" : "allsongs", "text" : "All Songs" }];
+ if (model.currentSongTitle != '')
+ c[c.length] = {type: "action", id: "curplaying", text: model.currentSongTitle};
+ return c; }()
+ }</content>
+ </invoke>
+ <transition event="menu.action.artists" target="artists" />
+ <transition event="menu.action.albums" target="albums">
+ <script>model.clearAlbumFilter ();</script>
+ </transition>
+ <transition event="menu.action.genres" target="genres" />
+ <transition event="menu.action.curplaying" target="playingwin" />
+ <transition event="menu.action.allsongs" target="songs">
+ <script>model.clearSongFilter ();</script>
+ </transition>
+ </state>
+ <state id="artists">
+ <anchor type="lastmenupos" />
+ <onentry>
+ <script>model.loadArtists (); view.showArtists();</script>
+ </onentry>
+ <transition event="q-signal:model.artistChanged(QString)">
+
+ <script>model.loadArtists();</script>
+ </transition>
+ <transition event="q-signal:selectButton.clicked()" target="albums" cond="view.currentIndex &gt;=0">
+ <script>
+ model.filterAlbumsByArtist(view.currentItem);
+ </script>
+ </transition>
+ </state>
+ <state id="albums">
+ <onentry>
+ <script>model.loadAlbums ();</script>
+ </onentry>
+ <transition cond="model.albumCount==1" target="songs" />
+ <transition cond="model.albumCount &gt; 1" target="show_albums" />
+ <transition cond="model.albumCount==0" target="songs" />
+ </state>
+ <state id="show_albums">
+ <anchor type="lastmenupos" />
+ <transition event="q-signal:model.albumChanged(QString)">
+
+ <script>model.loadAlbums();</script>
+ </transition>
+ <transition event="q-signal:selectButton.clicked()" cond="view.currentIndex &gt;=0" target="songs">
+ <script>
+ model.filterSongsByAlbum(view.currentItem);</script>
+ </transition>
+ <onentry>
+ <script>view.showAlbums ();</script>
+ </onentry>
+ </state>
+ <state id="genres">
+ <anchor type="lastmenupos" />
+ <onentry>
+ <script>model.loadGenres (); view.showGenres
+ ();</script>
+ </onentry>
+ <transition event="q-signal:model.genreChanged(QString)">
+
+ <script>model.loadGenres();</script>
+ </transition>
+ <transition event="q-signal:selectButton.clicked()" cond="view.currentIndex &gt;=0" target="songs">
+ <script>
+ model.filterSongsByGenre(view.currentItem);</script>
+ </transition>
+ </state>
+ <state id="songs">
+ <anchor type="lastmenupos" />
+ <onentry>
+ <script>model.loadSongs (); view.showSongs
+ ();</script>
+ </onentry>
+ <transition event="q-signal:model.songListChanged()">
+ <script>model.loadSongs();</script>
+ </transition>
+ <transition event="q-signal:selectButton.clicked()" cond="view.currentIndex &gt;=0" target="playingwin">
+ <script>
+ model.selectSong (view.currentItem);
+ engine.setTrack(model.currentSong);
+ </script>
+ <raise event="playIntent" />
+ <raise event="songSelected" />
+ </transition>
+ </state>
+ </state>
+ <state id="playingwin">
+ <anchor type="lastmenupos" />
+ <invoke type="q-bindings">
+ <content>[
+ [selectButton,"enabled",false],
+ [playingLabel,"text",model.currentSongTitle],
+ [midLabel,"text",model.currentSongArtist],
+ [posSlider,"minimum",0],
+ [posSlider,"maximum",engine.totalTime],
+ [stackedWidget,"currentIndex",1]
+ ]</content>
+ </invoke>
+ <onentry>
+ <script>view.showPlayer ();</script>
+ </onentry>
+ <transition event="q-signal:model.songChanged()">
+ <script>
+ midLabel.text = model.currentSongArtist + ' / ' + model.currentSongAlbum;
+ playingLabel.text = model.currentSongTitle;
+ </script>
+ </transition>
+ </state>
+ </state>
+ </state>
+ <state id="engine" initial="idle">
+ <onentry><script>
+ volumeSlider.value = engine.volume;
+ </script></onentry>
+
+ <transition event="q-signal:model.songChanged()">
+ <script>engine.setTrack(model.currentSong);</script>
+ <raise event="playIntent" />
+ <raise event="songChanged" />
+ </transition>
+ <state id="idle">
+ <transition event="playIntent" target="playing" />
+ <transition event="q-signal:playButton.clicked()">
+ <raise event="playIntent" />
+ </transition>
+ <invoke type="q-bindings">
+ <content>[[stopButton,"enabled",false]]</content>
+ </invoke>
+ </state>
+ <state id="active" initial="playing">
+ <invoke type="q-bindings">
+ <content>[[stopButton,"enabled",true]]</content>
+ </invoke>
+ <transition event="q-signal:stopButton.clicked()"
+ target="idle">
+ <script>engine.stop ();</script>
+ </transition>
+ <state id="playing">
+ <invoke type="q-bindings">
+ <content>[[playButton,"text","Pause"]]</content>
+ </invoke>
+ <onentry>
+ <script>engine.play ();</script>
+ </onentry>
+ <transition event="q-signal:playButton.clicked()"
+ target="paused" />
+ <transition event="songChanged">
+ <script>engine.play();</script>
+ </transition>
+ <transition event="q-signal:engine.tick(qint64)">
+ <script>view.setCurrentTime(_event.data[0]);</script>
+ </transition>
+ <transition event="q-signal:engine.totalTimeChanged(qint64)">
+ <script>view.setTotalTime(_event.data[0]);</script>
+ </transition>
+ </state>
+ <state id="paused">
+ <onentry>
+ <script>engine.pause();</script>
+ </onentry>
+ <transition event="q-signal:playButton.clicked()"
+ target="playing" />
+ </state>
+ <transition event="q-signal:model.endOfList()"
+ target="idle">
+ <script>engine.stop (); model.reset ();</script>
+ </transition>
+ </state>
+ <transition event="q-signal:engine.aboutToFinish()">
+ <raise event="nextSong" />
+ </transition>
+ <transition event="nextSong">
+ <script>
+ model.next();
+ engine.enqueue(model.currentSong);
+ </script>
+ </transition>
+ <transition event="q-signal:nextButton.clicked()">
+ <script>model.gotoNext();</script>
+ </transition>
+ <transition event="q-signal:prevButton.clicked()">
+ <script>model.gotoPrev();</script>
+ </transition>
+ <transition event="q-signal:posSlider.sliderMoved(int)">
+ <script>engine.seek(_event.data[0]);</script>
+ </transition>
+ <transition event="q-signal:volumeSlider.sliderMoved(int)">
+ <script>engine.volume = _event.data[0];</script>
+ </transition>
+ <transition event="q-signal:engine.volumeChanged(int)">
+ <script>volumeSlider.value = _event.data[0];</script>
+ </transition>
+ </state>
+ <state id="selection_state" initial="no_song_selected">
+ <state id="no_song_selected">
+ <transition event="songSelected" target="song_selected" />
+ <invoke type="q-bindings">
+ <content>[[stopButton,"enabled",false],
+ [playButton,"enabled",false],
+ [prevButton,"enabled",false],
+ [nextButton,"enabled",false]]</content>
+ </invoke>
+ </state>
+ <state id="song_selected">
+ <transition event="endOfList" target="no_song_selected" />
+ </state>
+ </state>
+ </parallel>
+</scxml>
diff --git a/examples/mediaplayer/mediaplayer.ui b/examples/mediaplayer/mediaplayer.ui
new file mode 100644
index 0000000..dbbc6fd
--- /dev/null
+++ b/examples/mediaplayer/mediaplayer.ui
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>mediaPlayerWidget</class>
+ <widget class="QWidget" name="mediaPlayerWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>394</width>
+ <height>287</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>SCXML Media Player</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="page">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QListView" name="listView"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="playerPage">
+ <widget class="QLabel" name="playingLabel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>50</y>
+ <width>291</width>
+ <height>61</height>
+ </rect>
+ </property>
+ <property name="styleSheet">
+ <string>QLabel {font-size: 24px; color: #336699}</string>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="midLabel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>120</y>
+ <width>291</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ <widget class="QSlider" name="posSlider">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>180</y>
+ <width>301</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget1" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QToolButton" name="homeButton">
+ <property name="text">
+ <string>Home</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="playButton">
+ <property name="text">
+ <string>Play</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="prevButton">
+ <property name="text">
+ <string>Prev</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="nextButton">
+ <property name="text">
+ <string>Next</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="stopButton">
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="volumeSlider">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="selectButton">
+ <property name="text">
+ <string>Select</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="backButton">
+ <property name="text">
+ <string>Back</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/mediaplayer/songdata.h b/examples/mediaplayer/songdata.h
new file mode 100644
index 0000000..0c31f03
--- /dev/null
+++ b/examples/mediaplayer/songdata.h
@@ -0,0 +1,15 @@
+#ifndef SONGDATA_H
+#define SONGDATA_H
+
+#include <QStringList>
+
+struct SongData
+{
+ QString url;
+ QString title;
+ QString album;
+ QString artist;
+ QStringList genres;
+ int trackNumber;
+};
+#endif
diff --git a/examples/mediaplayer/spengine.cpp b/examples/mediaplayer/spengine.cpp
new file mode 100644
index 0000000..9d8132f
--- /dev/null
+++ b/examples/mediaplayer/spengine.cpp
@@ -0,0 +1,84 @@
+#include "spengine.h"
+#include <phonon>
+
+using namespace Phonon;
+
+class SPEnginePvt
+{
+ public:
+ MediaObject* mediaObject;
+ AudioOutput* audioOutput;
+};
+void SPEngine::clearQueue()
+{
+ pvt->mediaObject->clearQueue();
+}
+
+int SPEngine::currentTime() const
+{
+ return pvt->mediaObject->currentTime ();
+}
+int SPEngine::totalTime() const
+{
+ return pvt->mediaObject->totalTime();
+}
+
+void SPEngine::enqueue (const QUrl & u)
+{
+ pvt->mediaObject->enqueue(MediaSource(u));
+}
+void SPEngine::setTrack(const QUrl & u)
+{
+ pvt->mediaObject->setCurrentSource(MediaSource(u));
+}
+void SPEngine::play()
+{
+ pvt->mediaObject->play ();
+}
+
+void SPEngine::pause()
+{
+ pvt->mediaObject->pause ();
+}
+
+void SPEngine::stop()
+{
+ pvt->mediaObject->stop();
+}
+
+void SPEngine::seek(qint64 pos)
+{
+ pvt->mediaObject->seek(pos);
+}
+
+void SPEngine::setVolume(int v)
+{
+ pvt->audioOutput->setVolume((qreal)v/100);
+}
+
+void SPEngine::onVolumeChanged(qreal r)
+{
+ emit volumeChanged(r*100);
+}
+int SPEngine::volume() const
+{
+ return pvt->audioOutput->volume()*100;
+}
+
+SPEngine::SPEngine(QObject* p) : QObject(p)
+{
+ pvt = new SPEnginePvt;
+ pvt->mediaObject = new Phonon::MediaObject(this);
+ pvt->audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
+ createPath (pvt->mediaObject, pvt->audioOutput);
+ pvt->mediaObject->setTickInterval(500);
+ connect (pvt->mediaObject, SIGNAL(aboutToFinish()), this, SIGNAL(aboutToFinish()));
+ connect (pvt->mediaObject, SIGNAL(tick(qint64)), this, SIGNAL(tick(qint64)));
+ connect (pvt->mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SIGNAL(totalTimeChanged(qint64)));
+ connect (pvt->audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(onVolumeChanged(qreal)));
+}
+
+SPEngine::~SPEngine ()
+{
+ delete pvt;
+}
diff --git a/examples/mediaplayer/spengine.h b/examples/mediaplayer/spengine.h
new file mode 100644
index 0000000..3134b75
--- /dev/null
+++ b/examples/mediaplayer/spengine.h
@@ -0,0 +1,41 @@
+#ifndef SPENGINE_H
+#define SPENGINE_H
+#include <QObject>
+#include <QUrl>
+class SPEngine : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int totalTime READ totalTime)
+ Q_PROPERTY(int currentTime READ currentTime)
+ Q_PROPERTY(int volume READ volume WRITE setVolume)
+
+ Q_SIGNALS:
+ void aboutToFinish();
+ void tick(qint64);
+ void totalTimeChanged(qint64);
+ void volumeChanged(int);
+
+ public Q_SLOTS:
+ void clearQueue();
+ void enqueue (const QUrl &);
+ void setTrack(const QUrl &);
+ void play();
+ void pause();
+ void seek(qint64);
+ void stop ();
+
+ protected Q_SLOTS:
+ void onVolumeChanged(qreal);
+
+ public:
+ SPEngine(QObject*);
+ virtual ~SPEngine ();
+ int currentTime () const;
+ int totalTime () const;
+ void setVolume(int);
+ int volume () const;
+
+ private:
+ class SPEnginePvt* pvt;
+};
+#endif
diff --git a/examples/mediaplayer/spharvester.cpp b/examples/mediaplayer/spharvester.cpp
new file mode 100644
index 0000000..ee6294f
--- /dev/null
+++ b/examples/mediaplayer/spharvester.cpp
@@ -0,0 +1,68 @@
+#include "spharvester.h"
+#include <QDir>
+#include <phonon>
+#include <QQueue>
+#include <QFile>
+#include <QUrl>
+
+using namespace Phonon;
+
+struct SPHarvesterPvt
+{
+ MediaObject* mediaObject;
+ QQueue<QString> pathQueue;
+};
+
+SPHarvester::SPHarvester(QObject* o) : QObject(o)
+{
+ pvt = new SPHarvesterPvt;
+ pvt->mediaObject = new MediaObject(this);
+ connect (pvt->mediaObject, SIGNAL(metaDataChanged()), this, SLOT(readMetaData ()));
+}
+
+SPHarvester::~SPHarvester()
+{
+ delete pvt;
+}
+
+void SPHarvester::harvest (const QString & directory, bool recurse)
+{
+ QDir d (directory);
+ QFileInfoList l = d.entryInfoList(QStringList() << "*.mp3",recurse ? QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files : QDir::Files);
+ foreach (QFileInfo fi, l) {
+ if (fi.isDir()) {
+ harvest (fi.absoluteFilePath(), recurse);
+ } else {
+ pvt->pathQueue.enqueue(fi.absoluteFilePath());
+ }
+ }
+ harvestNext ();
+}
+
+void SPHarvester::harvestNext ()
+{
+ if (pvt->pathQueue.empty())
+ emit done();
+ else {
+ QString s = pvt->pathQueue.dequeue();
+ pvt->mediaObject->setCurrentSource(MediaSource(s));
+ }
+}
+
+void SPHarvester::readMetaData ()
+{
+ QStringList albums = pvt->mediaObject->metaData("ALBUM");
+ QStringList titles = pvt->mediaObject->metaData("TITLE");
+ QStringList artists = pvt->mediaObject->metaData("ARTIST");
+ QStringList trackNums = pvt->mediaObject->metaData("TRACKNUMBER");
+ SongData sd;
+ sd.url = pvt->mediaObject->currentSource().url().toString();
+ sd.album = albums.count() ? albums[0] : "Unknown Album";
+ sd.artist = artists.count() ? artists[0] : "Unknown Artist";
+ sd.trackNumber = trackNums.count() ? trackNums[0].toInt() : 0;
+ sd.genres = pvt->mediaObject->metaData("GENRE");
+ sd.title = titles.count() ? titles[0] : QFileInfo(sd.url).baseName();
+
+ emit foundTrack(sd);
+ harvestNext ();
+}
diff --git a/examples/mediaplayer/spharvester.h b/examples/mediaplayer/spharvester.h
new file mode 100644
index 0000000..8fbe583
--- /dev/null
+++ b/examples/mediaplayer/spharvester.h
@@ -0,0 +1,25 @@
+#ifndef SPHARVESTER_H
+#define SPHARVESTER_H
+
+#include "songdata.h"
+class SPHarvester : public QObject
+{
+ Q_OBJECT
+public:
+ SPHarvester(QObject* o = NULL);
+ virtual ~SPHarvester ();
+public slots:
+ void harvest (const QString & directory, bool recurse = true);
+
+private slots:
+ void harvestNext ();
+ void readMetaData();
+signals:
+ void foundTrack (const SongData & d);
+ void done ();
+
+private:
+ class SPHarvesterPvt* pvt;
+};
+
+#endif // _H
diff --git a/examples/mediaplayer/spmodel.cpp b/examples/mediaplayer/spmodel.cpp
new file mode 100644
index 0000000..46e614d
--- /dev/null
+++ b/examples/mediaplayer/spmodel.cpp
@@ -0,0 +1,274 @@
+#include "spmodel.h"
+#include <QSqlQueryModel>
+#include <QtSql>
+#include <QStandardItemModel>
+#include <QMessageBox>
+// an SQL query model that always as column (0) as uid and column (1) as display
+class SPSqlQueryModel : public QSqlQueryModel
+{
+ Q_OBJECT
+ public:
+ SPSqlQueryModel (QObject* o = NULL): QSqlQueryModel (o) {}
+ virtual QVariant data(const QModelIndex & index, int role) const
+ {
+ QModelIndex idx(index);
+ if (role == Qt::DisplayRole && query().record().count() > 1) {
+ idx = idx.sibling(idx.row(),1);
+ } else if (role == Qt::UserRole)
+ role = Qt::DisplayRole;
+ return QSqlQueryModel::data(idx,role);
+ }
+};
+
+
+class SPModelPvt
+{
+ public:
+ SPSqlQueryModel
+ artistModel,
+ albumModel,
+ songModel,
+ playlistModel, genreModel;
+
+ QSqlQuery artistQuery, albumQuery, songQuery, playlistQuery, genreQuery, playingQuery;
+
+};
+
+SPModel::SPModel(QObject* o)
+ :QObject(o)
+{
+ QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
+ db.setDatabaseName("sqlite.db");
+ if (!db.open()) {
+ QMessageBox::critical(0, qApp->tr("Cannot open database"),
+ qApp->tr("Unable to establish a database connection.\n"
+ "This example needs SQLite support. Please read "
+ "the Qt SQL driver documentation for information how "
+ "to build it.\n\n"
+ "Click Cancel to exit."), QMessageBox::Cancel);
+ }
+
+ db.exec("CREATE TABLE IF NOT EXISTS songs (song_url VARCHAR(1024) PRIMARY KEY, song_title VARCHAR(1024), song_artist VARCHAR(1024), song_album VARCHAR(1024), song_track_index SMALLINT)");
+ db.exec("DROP TABLE genres");
+ db.exec("CREATE TABLE IF NOT EXISTS genres (genre_title VARCHAR(64), genre_song_url VARCHAR(1024))");
+// db.exec("CREATE TABLE IF NOT EXISTS playlists (playlist_id VARACHAR(256) PRIMARY KEY, playlist_title VARCHAR(1024))");
+// db.exec("CREATE TABLE IF NOT EXISTS playlist_songs (playlist_song_id BIGINT PRIMARY KEY, playlist_song_playlist
+
+ pvt = new SPModelPvt;
+ pvt->artistQuery = QSqlQuery("SELECT DISTINCT song_artist FROM songs");
+ pvt->albumQuery = QSqlQuery("SELECT DISTINCT song_album FROM songs");
+ pvt->playlistQuery = QSqlQuery("SELECT playlist_url, playlist_title FROM playlists");
+ pvt->songQuery = QSqlQuery("SELECT song_url, song_title from songs");
+ pvt->genreQuery = QSqlQuery("SELECT DISTINCT genre_title from genres");
+ QSqlQuery q;
+// q.exec("SELECT DISTINCT song_url,song_title FROM songs, genres WHERE genre_song_url=song_url AND genre_title='All'");
+ q.exec("SELECT * from genres");
+ while (q.next()) {
+ qDebug () << q.value(0);
+ }
+}
+
+void SPModel::addSong ( const SongData & data)
+{
+
+ QSqlQuery q;
+ q.prepare("SELECT count(*) FROM songs WHERE song_url=:url");
+ q.bindValue(":url",data.url);
+ bool inserting = true;
+ if (q.exec()) {
+ q.next();
+ inserting = q.value(0).toInt() == 0;
+ }
+ if (inserting) {
+ q.prepare ("INSERT INTO songs (song_url, song_title, song_artist, song_album, song_track_index) VALUES (:url, :title, :artist, :album, :track)");
+ } else {
+ q.prepare("UPDATE songs SET song_title=:title, song_album=:album, song_track_index=:track WHERE song_url=:url ");
+ }
+ q.bindValue(":url",data.url);
+ q.bindValue(":title",data.title);
+ q.bindValue(":artist",data.artist);
+ q.bindValue(":album",data.album);
+ q.bindValue(":track",data.trackNumber);
+ q.exec();
+
+ q.prepare ("DELETE FROM genres WHERE genre_song_url=:url");
+ q.bindValue(":url",data.url);
+ q.exec();
+
+ q.prepare ("INSERT INTO genres (genre_song_url, genre_title) VALUES(:url, :genre)");
+ q.bindValue(":url",data.url);
+ QStringList gn = data.genres;
+ gn << "All";
+ foreach (QString g, gn) {
+ q.bindValue(":genre",g);
+ q.exec ();
+ }
+
+ if (inserting) {
+ emit albumChanged(data.album);
+ emit songListChanged();
+ emit artistChanged(data.artist);
+ foreach (QString g, data.genres) {
+ emit genreChanged(g);
+ }
+ }
+
+
+}
+
+
+SPModel::~SPModel()
+{
+ delete pvt;
+}
+
+int SPModel::albumCount() const
+{
+ return pvt->albumModel.rowCount();
+}
+
+void SPModel::clearAlbumFilter ()
+{
+ pvt->albumQuery = QSqlQuery ("SELECT DISTINCT song_album FROM songs ");
+}
+void SPModel::clearSongFilter ()
+{
+ pvt->songQuery = QSqlQuery ("SELECT song_url, song_title FROM songs");
+}
+void SPModel::loadArtists ()
+{
+ pvt->artistQuery.exec ();
+ pvt->artistModel.setQuery(pvt->artistQuery);
+}
+void SPModel::filterAlbumsByArtist(const QString & artist)
+{
+ pvt->albumQuery.prepare("SELECT DISTINCT song_album FROM songs WHERE song_artist=:artist");
+ pvt->albumQuery.bindValue(":artist",artist);
+}
+void SPModel::filterSongsByAlbum(const QString & album)
+{
+ pvt->albumQuery.prepare("SELECT song_url,song_title, song_track_index FROM songs WHERE song_album=:album ORDER BY song_track_index");
+ pvt->albumQuery.bindValue(":album",album);
+}
+void SPModel::loadGenres ()
+{
+ pvt->genreQuery.exec();
+ pvt->genreModel.setQuery(pvt->genreQuery);
+}
+
+void SPModel::filterSongsByGenre(const QString & genre)
+{
+ pvt->songQuery.prepare ("SELECT DISTINCT song_url,song_title FROM songs, genres WHERE genre_song_url=song_url AND genre_title=:genre");
+ pvt->songQuery.bindValue(":genre",genre);
+}
+void SPModel::loadPlaylists()
+{
+ pvt->playlistQuery.exec ();
+ pvt->playlistModel.setQuery(pvt->playlistQuery);
+}
+void SPModel::loadAlbums()
+{
+ pvt->albumQuery.exec ();
+ pvt->albumModel.setQuery(pvt->albumQuery);
+}
+void SPModel::filterSongsByPlaylist(const QString & uid)
+{
+ pvt->songQuery.prepare("SELECT DISTINCT song_url, song_title, playlist_song_index FROM playlist_songs INNER JOIN songs ON playlist_song_url=song_url WHERE playlist_id=:playlist ORDER BY playlist_song_index");
+ pvt->songQuery.bindValue(":playlist",uid);
+}
+void SPModel::loadSongs ()
+{
+ pvt->songQuery.exec ();
+ pvt->songModel.setQuery(pvt->songQuery);
+}
+QUrl SPModel::currentSong()
+{
+ if (pvt->playingQuery.isValid())
+ return QUrl(pvt->playingQuery.value(0).toString());
+ else
+ return QUrl();
+}
+QString SPModel::currentSongTitle()
+{
+ if (pvt->playingQuery.isValid())
+ return pvt->playingQuery.value(1).toString();
+ else
+ return QString();
+}
+QString SPModel::currentSongArtist()
+{
+ QSqlQuery q;
+ q.prepare("SELECT song_artist FROM songs WHERE song_url=:url");
+ q.bindValue(":url",currentSong().toString());
+ q.exec();
+ q.next();
+ return q.value(0).toString();
+}
+QString SPModel::currentSongAlbum()
+{
+ QSqlQuery q;
+ q.prepare("SELECT song_album FROM songs WHERE song_url=:url");
+ q.bindValue(":url",currentSong().toString());
+ q.exec();
+ q.next();
+ return q.value(0).toString();
+}
+
+void SPModel::selectSong (const QString & s)
+{
+ pvt->playingQuery = QSqlQuery(pvt->songQuery.executedQuery ());
+
+ while (pvt->playingQuery.next()) {
+ if (pvt->playingQuery.value(0).toString() == s) {
+ emit songChanged ();
+ return;
+ }
+ }
+ emit endOfList ();
+}
+
+void SPModel::reset ()
+{
+ pvt->playingQuery = QSqlQuery(pvt->songQuery.executedQuery ());
+ pvt->playingQuery.exec();
+}
+
+void SPModel::gotoNext()
+{
+ if (pvt->playingQuery.next()) {
+ emit songChanged ();
+ }else
+ emit endOfList ();
+}
+void SPModel::gotoPrev()
+{
+ if (pvt->playingQuery.previous())
+ emit songChanged ();
+ else
+ emit endOfList ();
+}
+
+
+
+QAbstractItemModel* SPModel::albumsItemModel() const
+{
+ return &pvt->albumModel;
+}
+QAbstractItemModel* SPModel::genresItemModel() const
+{
+ return &pvt->genreModel;
+}
+QAbstractItemModel* SPModel::songsItemModel() const
+{
+ return &pvt->songModel;
+}
+QAbstractItemModel* SPModel::playlistsItemModel() const
+{
+ return &pvt->playlistModel;
+}
+QAbstractItemModel* SPModel::artistsItemModel() const
+{
+ return &pvt->artistModel;
+}
+
+#include <spmodel.moc>
diff --git a/examples/mediaplayer/spmodel.h b/examples/mediaplayer/spmodel.h
new file mode 100644
index 0000000..af8254b
--- /dev/null
+++ b/examples/mediaplayer/spmodel.h
@@ -0,0 +1,64 @@
+#ifndef SPMODEL_H
+#define SPMODEL_H
+#include <QObject>
+#include <QUrl>
+#include <QAbstractItemModel>
+#include "songdata.h"
+
+class SPModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QUrl currentSong READ currentSong)
+ Q_PROPERTY(QString currentSongTitle READ currentSongTitle)
+ Q_PROPERTY(QString currentSongArtist READ currentSongArtist)
+ Q_PROPERTY(QString currentSongAlbum READ currentSongAlbum)
+ Q_PROPERTY(int albumCount READ albumCount)
+
+
+ public slots:
+ void clearAlbumFilter ();
+ void clearSongFilter ();
+ void loadArtists ();
+ void filterAlbumsByArtist(const QString & name);
+ void loadGenres ();
+ void filterSongsByGenre(const QString & genre);
+ void loadPlaylists();
+ void loadAlbums();
+ void filterSongsByPlaylist(const QString & uid);
+ void filterSongsByAlbum(const QString & name);
+ void loadSongs ();
+ void selectSong (const QString &);
+ void gotoNext();
+ void gotoPrev();
+ void addSong (const SongData &);
+ void reset ();
+
+ signals:
+ void albumChanged(const QString &);
+ void artistChanged(const QString &);
+ void genreChanged(const QString &);
+ void songListChanged();
+ void songChanged ();
+ void endOfList ();
+
+ public:
+ SPModel(QObject*);
+ virtual ~SPModel ();
+
+ QUrl currentSong();
+ QString currentSongTitle ();
+ QString currentSongArtist();
+ QString currentSongAlbum();
+ QAbstractItemModel* albumsItemModel() const;
+ QAbstractItemModel* genresItemModel() const;
+ QAbstractItemModel* songsItemModel() const;
+ QAbstractItemModel* playlistsItemModel() const;
+ QAbstractItemModel* artistsItemModel() const;
+ int albumCount() const;
+
+ private:
+ class SPModelPvt* pvt;
+};
+
+#endif
diff --git a/examples/mediaplayer/spview.cpp b/examples/mediaplayer/spview.cpp
new file mode 100644
index 0000000..1d52053
--- /dev/null
+++ b/examples/mediaplayer/spview.cpp
@@ -0,0 +1,97 @@
+#include "spview.h"
+#include "spmodel.h"
+#include <QDebug>
+#include <QItemDelegate>
+
+class SPViewPvt
+{
+ public:
+ SPModel* model;
+};
+
+
+class SPItemDelegate : public QItemDelegate
+{
+ Q_OBJECT
+
+public:
+ SPItemDelegate(QObject* o) : QItemDelegate(o) {}
+
+ virtual void paint (QPainter* p, QStyleOptionViewItem & option, const QModelIndex & index) const
+ {
+ QString disp = index.data(Qt::DisplayRole).toString();
+ drawBackground(p,option,index);
+ drawDisplay(p,option,option.rect,disp);
+ }
+};
+
+void SPView::setModel (SPModel* m)
+{
+ pvt->model = m;
+}
+
+void SPView::showAlbums ()
+{
+ listView->setModel (pvt->model->albumsItemModel());
+}
+
+void SPView::showArtists ()
+{
+ listView->setModel (pvt->model->artistsItemModel());
+}
+
+void SPView::showGenres ()
+{
+ QAbstractItemModel* model = pvt->model->genresItemModel();
+ listView->setModel (model);
+}
+
+void SPView::showSongs ()
+{
+ listView->setModel (pvt->model->songsItemModel());
+}
+
+void SPView::showPlaylists()
+{
+ listView->setModel (pvt->model->playlistsItemModel());
+}
+
+
+SPView::SPView(QWidget* w) : QWidget (w)
+{
+ pvt = new SPViewPvt;
+ setupUi(this);
+ listView->setItemDelegate(new SPItemDelegate(this));
+}
+
+QString SPView::currentItem() const
+{
+ QVariant v = listView->model()->data(listView->currentIndex(),Qt::UserRole);
+ if (v.isNull())
+ v = listView->currentIndex().data(Qt::DisplayRole);
+ return v.toString ();
+}
+
+int SPView::itemCount () const
+{
+ return listView->model()->rowCount ();
+}
+int SPView::currentIndex() const
+{
+ return listView->currentIndex().row();
+}
+
+void SPView::setTotalTime (int t)
+{
+ posSlider->setMaximum(t);
+}
+void SPView::setCurrentTime (int t)
+{
+ posSlider->setValue (t);
+}
+
+SPView::~SPView ()
+{
+ delete pvt;
+}
+#include "spview.moc"
diff --git a/examples/mediaplayer/spview.h b/examples/mediaplayer/spview.h
new file mode 100644
index 0000000..53b8c09
--- /dev/null
+++ b/examples/mediaplayer/spview.h
@@ -0,0 +1,36 @@
+#ifndef SPVIEW_H
+#define SPVIEW_H
+#include <QObject>
+#include <QUrl>
+#include "spmodel.h"
+#include "ui_mediaplayer.h"
+
+class SPView : public QWidget, public virtual Ui::mediaPlayerWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString currentItem READ currentItem)
+ Q_PROPERTY(int itemCount READ itemCount)
+ Q_PROPERTY(int currentIndex READ currentIndex)
+ public slots:
+ void setModel (SPModel*);
+ void showAlbums ();
+ void showArtists ();
+ void showGenres ();
+ void showSongs ();
+ void showPlaylists();
+ void setTotalTime (int);
+ void setCurrentTime (int);
+
+
+ public:
+ SPView(QWidget*);
+ virtual ~SPView ();
+ QString currentItem () const;
+ int itemCount () const;
+ int currentIndex() const;
+
+ private:
+ class SPViewPvt* pvt;
+};
+
+#endif