summaryrefslogtreecommitdiffstats
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
parenta6553f68f17c28adca049857686496a69b4c1e7a (diff)
scxml for 4.6
-rw-r--r--examples/blackjack/bj.qrc5
-rw-r--r--examples/blackjack/blackjack.pro14
-rw-r--r--examples/blackjack/blackjack.scxml305
-rw-r--r--examples/blackjack/blackjack.ui174
-rw-r--r--examples/blackjack/main.cpp25
-rw-r--r--examples/calc/calc.cpp35
-rw-r--r--examples/calc/calc.h19
-rw-r--r--examples/calc/calc.pro16
-rw-r--r--examples/calc/calc.qrc5
-rw-r--r--examples/calc/calc.scxml213
-rw-r--r--examples/calc/calc.ui187
-rw-r--r--examples/calc/main.cpp15
-rw-r--r--examples/examples.pro2
-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
-rw-r--r--qscxml.pro14
-rw-r--r--src/qscxml.cpp (renamed from qscxml.cpp)100
-rw-r--r--src/qscxml.h (renamed from qscxml.h)14
-rw-r--r--src/qscxml.pri6
-rw-r--r--src/qscxmlgui.cpp171
-rw-r--r--src/qscxmlgui.h48
-rw-r--r--src/src.pro10
34 files changed, 2491 insertions, 62 deletions
diff --git a/examples/blackjack/bj.qrc b/examples/blackjack/bj.qrc
new file mode 100644
index 0000000..6e39934
--- /dev/null
+++ b/examples/blackjack/bj.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>blackjack.scxml</file>
+ </qresource>
+</RCC>
diff --git a/examples/blackjack/blackjack.pro b/examples/blackjack/blackjack.pro
new file mode 100644
index 0000000..29c7351
--- /dev/null
+++ b/examples/blackjack/blackjack.pro
@@ -0,0 +1,14 @@
+# -------------------------------------------------
+# Project created by QtCreator 2008-12-16T16:32:05
+# -------------------------------------------------
+QT += script
+DEPENDPATH += .
+INCLUDEPATH += .
+TARGET = blackjack
+CONFIG += console
+CONFIG -= app_bundle
+TEMPLATE = app
+SOURCES += main.cpp
+include($$PWD/../../src/qtstatemachine.pri)
+FORMS += blackjack.ui
+RESOURCES += bj.qrc
diff --git a/examples/blackjack/blackjack.scxml b/examples/blackjack/blackjack.scxml
new file mode 100644
index 0000000..6c98eb5
--- /dev/null
+++ b/examples/blackjack/blackjack.scxml
@@ -0,0 +1,305 @@
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ initial="root" profile="ecmascript">
+ <script><![CDATA[
+ var Suits = "CDHS";
+ var Ranks = "-A23456789TJQK";
+
+ function CardToString()
+ {
+ return "" + Ranks[this.rank] + Suits[this.suit];
+ }
+
+ function Card (r,s)
+ {
+ this.rank = r;
+ this.suit = s;
+ this.minValue = Math.min(r,10);
+ this.toString = CardToString;
+ }
+
+ function updateDisplay ()
+ {
+ cardsLabel.text = "My Cards: " + myDeck + " Dealer Cards: " + dealerCards;
+ }
+
+ function randomSort (a,b)
+ {
+ return Math.random() * 3 - 1;
+ }
+
+ function deckReset()
+ {
+ this.clear ();
+ for (var i=1; i <= 13; ++i)
+ for (var j = 0; j < 4; ++j)
+ this.cards.push(new Card(i,j));
+ this.cards.sort(randomSort);
+ }
+
+ function deckDraw ()
+ {
+ return this.cards.pop();
+ }
+
+ function deckDrawFrom (d)
+ {
+ var c = d.draw ();
+ this.cards.push(c);
+ updateDisplay ();
+ }
+
+ function deckClear ()
+ {
+ this.cards = new Array;
+ }
+
+ function deckEvalMin ()
+ {
+ var minVal = 0;
+ var cardCount = this.cards.length;
+ for (c in this.cards) {
+ minVal += this.cards[c].minValue;
+ }
+ if (cardCount > 4 && minVal < 22)
+ minVal = 21;
+ return minVal;
+ }
+
+ function deckEvalBest ()
+ {
+ var bestVal = this.evalMin();
+ if (bestVal > 21)
+ return 0;
+ else if (bestVal == 21)
+ return bestVal;
+
+ for (i in this.cards) {
+ if (this.cards[i].rank == 1)
+ {
+ var v = bestVal + 10;
+ if (v <= 21)
+ bestVal = v;
+ }
+ }
+ return bestVal;
+
+ }
+
+ function deckToString ()
+ {
+ var s = "";
+ for (i in this.cards)
+ s += this.cards[i].toString() + ":";
+
+ return s;
+ }
+
+ function Deck()
+ {
+ this.draw = deckDraw;
+ this.cards = new Array();
+ this.reset = deckReset;
+ this.clear = deckClear;
+ this.evalMin = deckEvalMin;
+ this.evalBest = deckEvalBest;
+ this.toString = deckToString;
+ this.drawFrom = deckDrawFrom;
+ }
+
+
+ function hitMe ()
+ {
+ myDeck.drawFrom (availDeck);
+ }
+ ]]></script>
+ <final id="exit" />
+ <state id="root" initial="newgame">
+ <onentry>
+ <script>
+ var myDeck = new Deck;
+ var dealerCards = new Deck;
+ var availDeck = new Deck;
+ var pot = 0;
+ var points = 1000;
+
+ </script>
+
+ </onentry>
+ <invoke type="q-bindings">
+ <content>
+ [[welcomeLabel,"text","Welcome to Blackjack"]]
+ </content>
+ </invoke>
+ <transition event="q-signal:newGameButton.clicked()" target="newgame" />
+ <state id="newgame">
+ <onentry>
+ <script>
+ points = 1000;
+ pointsLabel.text = points;
+ </script>
+ </onentry>
+ <transition target="newround" />
+ </state>
+ <state id="quitdlg">
+ <invoke type="q-messagebox">
+ <content>
+ {
+ "parent" : gameWidget,
+ "icon" : QMessageBox.Question,
+ "windowTitle" : "Exit Blackjack",
+ "text" : "Are you sure?",
+ "standardButtons" :
+ QMessageBox.Yes|QMessageBox.No
+ }
+ </content>
+ </invoke>
+ <transition event="q-messagebox.finished" target="exit" cond="_event.data[0]==QMessageBox.Yes" />
+ <transition event="q-messagebox.finished" target="gamestate" cond="_event.data[0]==QMessageBox.No" />
+ </state>
+ <state id="game">
+ <history type="deep" id="gamestate" />
+ <transition event="q-signal:exitButton.clicked()" target="quitdlg" />
+ <state id="newround">
+ <onentry>
+ <script>
+ pot = 0;
+ availDeck.reset ();
+ myDeck.clear ();
+ dealerCards.clear();
+ dealerCards.drawFrom(availDeck);
+ hitMe ();
+ hitMe ();
+ </script>
+ </onentry>
+ <transition target="waitForBet" />
+ </state>
+ <state id="waitForBet">
+ <invoke type="q-bindings"><content>
+ [
+ [betEdit,"enabled",true],
+ [betButton,"enabled",true],
+ [surrenderButton,"enabled",true],
+ [welcomeLabel,"text","Please place your bet"]
+ ]
+ </content></invoke>
+ <transition event="q-signal:betButton.clicked()" target="testCards" cond="parseInt(betEdit.text) &lt;= points">
+ <script>
+ pot = betEdit.text;
+ points -= pot;
+ pointsLabel.text = points;
+ </script>
+ </transition>
+ <transition event="q-signal:betButton.clicked()" target="betTooHigh" cond="parseInt(betEdit.text) &gt; points">
+ </transition>
+ <transition event="q-signal:surrenderButton.clicked()" target="newround" />
+ </state>
+ <state id="betTooHigh">
+ <invoke type="q-messagebox">
+ <content>
+ {
+ "parent" : betEdit,
+ "icon" : QMessageBox.Warning,
+ "windowTitle" : "Bet is Too High",
+ "text" : "Please Place Another Bet",
+ "standardButtons" :
+ QMessageBox.Ok
+ }
+ </content>
+ </invoke>
+ <transition event="q-messagebox.finished" target="waitForBet" />
+ <transition event="bth-mb-timeout" target="waitForBet" />
+ <onentry>
+ <send event="'bth-mb-timeout'" delay="'1500ms'" />
+ </onentry>
+ </state>
+ <state id="testCards">
+ <transition target="loss" cond="myDeck.evalBest() == 0" />
+ <transition target="getDealerCards" cond="myDeck.evalBest() == 21" />
+ <transition target="waitForAction" cond="myDeck.evalBest() %21 != 0" />
+ </state>
+
+ <state id="waitForAction">
+ <transition event="q-signal:hitButton.clicked()" target="testCards">
+ <script>hitMe (); </script>
+ </transition>
+ <transition event="q-signal:standButton.clicked()" target="getDealerCards" />
+ <invoke type="q-bindings"><content>
+ [
+ [welcomeLabel,"text","Hit/Stand?"],
+ [hitButton,"enabled",true],
+ [standButton,"enabled",true]
+ ]
+ </content></invoke>
+ </state>
+
+ <state id="getDealerCards">
+ <onentry>
+ <script><![CDATA[
+ while (dealerCards.evalBest() > 0 && dealerCards.evalBest() < 17)
+ {
+ dealerCards.drawFrom(availDeck);
+ }
+ ]]></script>
+ <raise event="doneWithCards" />
+ </onentry>
+ <transition target="checkWinner" />
+ </state>
+
+ <state id="checkWinner">
+ <onentry>
+ <script>
+ _global.diff = myDeck.evalBest() - dealerCards.evalBest();
+ </script>
+ </onentry>
+
+ <transition cond="diff&gt;0" target="win" />
+ <transition cond="diff&lt;0" target="loss" />
+ <transition cond="diff==0" target="draw" />
+ </state>
+ <state id="endGame">
+ <invoke type="q-bindings"><content>[[welcomeLabel,"text","Game Over"]]</content></invoke>
+ <transition event="timeout" target="newgame" />
+ <onentry>
+ <send event="'timeout'" delay="'3s'" />
+ </onentry>
+ </state>
+ <state id="endRound">
+ <invoke type="q-bindings"><content>[[newRoundButton,"enabled",true]]</content></invoke>
+ <transition event="q-signal:newRoundButton.clicked()" target="newround" />
+ <transition event="timeout" target="newround" />
+ <onentry>
+ <send event="'timeout'" delay="'3s'" />
+ </onentry>
+
+ <state id="win">
+ <onentry>
+ <script>
+ points = parseInt(points) + parseInt(pot) * 2;
+ pointsLabel.text = points;
+ </script>
+ </onentry>
+ <invoke type="q-bindings"><content>[[welcomeLabel,"text","You Won!"]]</content></invoke>
+ </state>
+ <state id="loss">
+ <invoke type="q-bindings"><content>[[welcomeLabel,"text","You Lost..."]]</content></invoke>
+ <transition cond="points == 0" target="endGame" />
+ </state>
+ <state id="draw">
+ <invoke type="q-bindings"><content>[[welcomeLabel,"text","You It's a draw."]]</content></invoke>
+ <onentry>
+ <script>
+ points = parseInt(points) + parseInt(pot);
+ pointsLabel.text = points;
+ </script>
+ </onentry>
+ </state>
+ </state>
+ </state>
+
+ </state>
+
+</scxml>
+
+
+
diff --git a/examples/blackjack/blackjack.ui b/examples/blackjack/blackjack.ui
new file mode 100644
index 0000000..187ec62
--- /dev/null
+++ b/examples/blackjack/blackjack.ui
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>gameWidget</class>
+ <widget class="QWidget" name="gameWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>520</width>
+ <height>173</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>SCXML Blackjack Example</string>
+ </property>
+ <property name="styleSheet">
+ <string/>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="welcomeLabel">
+ <property name="font">
+ <font>
+ <pointsize>18</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="cardsLabel">
+ <property name="font">
+ <font>
+ <pointsize>16</pointsize>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string>color:#660033</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Points:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="pointsLabel">
+ <property name="styleSheet">
+ <string>color:#339999</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="betEdit">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxLength">
+ <number>10</number>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="betButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Bet</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="newGameButton">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>New Game</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="newRoundButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>New Round</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="surrenderButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Surrender</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="hitButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Hit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="standButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Stand</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="exitButton">
+ <property name="text">
+ <string>Exit</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/blackjack/main.cpp b/examples/blackjack/main.cpp
new file mode 100644
index 0000000..c2e8105
--- /dev/null
+++ b/examples/blackjack/main.cpp
@@ -0,0 +1,25 @@
+#include <QApplication>
+#include <QFileInfo>
+#include "ui_blackjack.h"
+#include <QDebug>
+#include <QMessageBox>
+#include <QUrl>
+#include <QScriptEngine>
+#include "qscriptedstatemachine.h"
+#include "time.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ srand(clock());
+ QtScriptedStateMachine *sm = QtScriptedStateMachine::load(":/blackjack.scxml");
+ QObject::connect (sm, SIGNAL(finished()), &a, SLOT(quit()));
+ QMessageBox b;
+ QWidget* wdg = new QWidget();
+ Ui::gameWidget gw;
+ gw.setupUi(wdg);
+ sm->registerObject(wdg,"gameWidget",true);
+ wdg->show();
+ sm->start();
+ return a.exec();
+}
diff --git a/examples/calc/calc.cpp b/examples/calc/calc.cpp
new file mode 100644
index 0000000..4b61027
--- /dev/null
+++ b/examples/calc/calc.cpp
@@ -0,0 +1,35 @@
+#include "calc.h"
+#include "ui_calc.h"
+#include <QSignalMapper>
+CalcWidget::CalcWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ setupUi(this);
+ QSignalMapper* mapper = new QSignalMapper(this);
+ connect (mapper, SIGNAL(mapped(QString)), this, SIGNAL(command(QString)));
+ QList<QAbstractButton*> buttons = findChildren<QAbstractButton*>();
+ foreach (QAbstractButton* b, buttons) {
+ connect (b, SIGNAL(clicked()), mapper, SLOT(map()));
+ }
+ mapper->setMapping(button0,"DIGIT.0");
+ mapper->setMapping(button1,"DIGIT.1");
+ mapper->setMapping(button2,"DIGIT.2");
+ mapper->setMapping(button3,"DIGIT.3");
+ mapper->setMapping(button4,"DIGIT.4");
+ mapper->setMapping(button5,"DIGIT.5");
+ mapper->setMapping(button6,"DIGIT.6");
+ mapper->setMapping(button7,"DIGIT.7");
+ mapper->setMapping(button8,"DIGIT.8");
+ mapper->setMapping(button9,"DIGIT.9");
+ mapper->setMapping(buttonEq,"EQUALS");
+ mapper->setMapping(buttonCE,"CE");
+ mapper->setMapping(buttonC,"C");
+ mapper->setMapping(buttonPoint,"POINT");
+ mapper->setMapping(buttonPlus,"OPER.PLUS");
+ mapper->setMapping(buttonStar,"OPER.STAR");
+ mapper->setMapping(buttonMinus,"OPER.MINUS");
+ mapper->setMapping(buttonDiv,"OPER.DIV");
+}
+CalcWidget::~CalcWidget()
+{
+}
diff --git a/examples/calc/calc.h b/examples/calc/calc.h
new file mode 100644
index 0000000..6c7cadc
--- /dev/null
+++ b/examples/calc/calc.h
@@ -0,0 +1,19 @@
+#ifndef CALC_H
+#define CALC_H
+
+#include <QtGui/QWidget>
+#include "ui_calc.h"
+
+class CalcWidget : public QWidget, public Ui::CalcClass
+{
+ Q_OBJECT
+
+public:
+ CalcWidget(QWidget *parent = 0);
+ ~CalcWidget();
+
+Q_SIGNALS:
+ void command(const QString &);
+};
+
+#endif // CALC_H
diff --git a/examples/calc/calc.pro b/examples/calc/calc.pro
new file mode 100644
index 0000000..ff274c2
--- /dev/null
+++ b/examples/calc/calc.pro
@@ -0,0 +1,16 @@
+# -------------------------------------------------
+# Project created by QtCreator 2008-12-25T19:50:44
+# -------------------------------------------------
+
+TARGET = calc
+TEMPLATE = app
+win32: CONFIG += console
+mac:CONFIG -= app_bundle
+QT = core gui script
+include($$PWD/../../src/qtstatemachine.pri)
+
+# Input
+SOURCES += main.cpp calc.cpp
+HEADERS += calc.h
+FORMS += calc.ui
+RESOURCES += calc.qrc
diff --git a/examples/calc/calc.qrc b/examples/calc/calc.qrc
new file mode 100644
index 0000000..04b1be3
--- /dev/null
+++ b/examples/calc/calc.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>calc.scxml</file>
+ </qresource>
+</RCC>
diff --git a/examples/calc/calc.scxml b/examples/calc/calc.scxml
new file mode 100644
index 0000000..35a9b9e
--- /dev/null
+++ b/examples/calc/calc.scxml
@@ -0,0 +1,213 @@
+<!-- http://www.state-machine.com/devzone/Recipe_DesigningHSM.pdf -->
+<!-- events: OPER.PLUS OPER.MINUS OPER.MULTIPLY OPER.DIVIDE DIGIT.0 DIGIT.1_9 EQUALS CE C POINT -
+-->
+<scxml
+ initial="on" profile="ecma" name="calc">
+ <script>
+ function insertDigit ()
+ {
+ insert (_event.name.charAt(_event.name.lastIndexOf('.')+1));
+ }
+ function insert(c)
+ {
+ short_expr += c;
+ updateDisplay();
+ }
+ function negate ()
+ {
+ short_expr = "-";
+ updateDisplay ();
+ }
+
+ function updateDisplay ()
+ {
+ if (short_expr == "")
+ dispLbl.text = _data.res;
+ else
+ dispLbl.text = short_expr;
+ }
+ function subcalc ()
+ {
+ if (short_expr != "")
+ _data.long_expr += "(" + short_expr + ")";
+ _data.res = eval(_data.long_expr);
+ short_expr = "";
+ updateDisplay ();
+ return true;
+ }
+
+ function insertOp ()
+ {
+ var sc = subcalc ();
+ var op = '';
+ if (_event.name == "OPER.PLUS")
+ op = '+';
+ else if (_event.name == "OPER.MINUS")
+ op = '-';
+ else if (_event.name == "OPER.STAR")
+ op = '*';
+ else if (_event.name == "OPER.DIV")
+ op = '/';
+ _data.long_expr += op;
+ return sc;
+ }
+ function reset ()
+ {
+ short_expr = "";
+ }
+ function calc ()
+ {
+ if (subcalc ()) {
+ short_expr = "" + _data.res;
+ _data.long_expr = "";
+ _data.res = 0;
+ return true;
+ } else
+ return false;
+ }
+ </script>
+ <state id="on" initial="ready">
+ <datamodel>
+ <data id="long_expr" />
+ <data id="res" >0</data>
+ </datamodel>
+ <onentry>
+ <script>
+ var short_expr = 0;
+ _data.res = 0;
+ _data.long_expr = "";
+ updateDisplay();
+ </script>
+ </onentry>
+ <state id="ready" initial="begin">
+ <state id="begin">
+ <transition event="OPER.MINUS" target="negated1" />
+ <onentry>
+ <script>
+ updateDisplay ();
+ </script>
+ </onentry>
+ </state>
+ <state id="result">
+ </state>
+ <transition event="OPER" target="opEntered" />
+ <transition event="DIGIT.0" target="zero1">
+ <script>
+ reset ();
+ </script>
+ </transition>
+ <transition event="DIGIT" target="int1">
+ <script>
+ reset ();
+ </script>
+ </transition>
+ <transition event="POINT" target="frac1">
+ <script>
+ reset ();
+ </script>
+ </transition>
+ </state>
+ <state id="negated1">
+ <onentry>
+ <script>
+ negate ();
+ </script>
+ </onentry>
+ <transition event="DIGIT.0" target="zero1" />
+ <transition event="DIGIT" target="int1" />
+ <transition event="POINT" target="frac1" />
+ </state>
+ <state id="operand1">
+ <state id="zero1">
+ <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
+ <transition event="POINT" target="frac1" />
+ </state>
+ <state id="int1">
+ <transition event="POINT" target="frac1" />
+ <transition event="DIGIT">
+ <script>
+ insertDigit ();
+ </script>
+ </transition>
+ <onentry>
+ <script>
+ insertDigit ();
+ </script>
+ </onentry>
+ </state>
+ <state id="frac1">
+ <onentry>
+ <script>
+ insert ('.');
+ </script>
+ </onentry>
+ <transition event="DIGIT">
+ <script>
+ insertDigit ();
+ </script>
+ </transition>
+ </state>
+ <transition event="CE" target="ready" />
+ <transition event="OPER" target="opEntered" />
+ </state>
+ <state id="error" />
+ <state id="opEntered">
+ <transition event="OPER.MINUS" target="negated2" />
+ <transition event="POINT" target="frac2" />
+ <transition event="DIGIT.0" target="zero2" />
+ <transition event="DIGIT" target="int2" />
+ <onentry>
+ <script>
+ insertOp ();
+ </script>
+ </onentry>
+ </state>
+ <state id="negated2">
+ <onentry>
+ <script>
+ negate ();
+ </script>
+ </onentry>
+ <transition event="CE" target="opEntered" />
+ <transition event="DIGIT.0" target="zero2" />
+ <transition event="DIGIT" target="int2" />
+ <transition event="POINT" target="frac2" />
+ </state>
+ <state id="operand2">
+ <state id="zero2">
+ <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
+ <transition event="POINT" target="frac2" />
+ </state>
+ <state id="int2">
+ <transition event="DIGIT">
+ <script>
+ insertDigit ();
+ </script>
+ </transition>
+ <onentry>
+ <script>
+ insertDigit ();
+ </script>
+ </onentry>
+ <transition event="POINT" target="frac2" />
+ </state>
+ <state id="frac2">
+ <onentry>
+ <script>
+ insert ('.');
+ </script>
+ </onentry>
+ <transition event="DIGIT">
+ <script>
+ insertDigit ();
+ </script>
+ </transition>
+ </state>
+ <transition event="OPER" cond="!insertOp()" target="error" />
+ <transition event="OPER" target="opEntered" />
+ <transition event="EQUALS" cond="!calc()" target="error" />
+ <transition event="EQUALS" target="result" />
+ </state>
+ <transition event="C" target="on" />
+ </state>
+</scxml>
diff --git a/examples/calc/calc.ui b/examples/calc/calc.ui
new file mode 100644
index 0000000..0e2a651
--- /dev/null
+++ b/examples/calc/calc.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CalcClass</class>
+ <widget class="QWidget" name="CalcClass">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>419</width>
+ <height>156</height>
+ </rect>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>419</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>SCXML Calculator</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">* {background-color:black;color:white}
+button {border-width:3; }</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="dispLbl">
+ <property name="styleSheet">
+ <string notr="true">
+background-color: #CCCCCC;
+color: black;
+font: 12pt &quot;Courier New&quot;;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="horizontalSpacing">
+ <number>6</number>
+ </property>
+ <item row="3" column="0">
+ <widget class="QPushButton" name="button4">
+ <property name="text">
+ <string>4</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QPushButton" name="button1">
+ <property name="text">
+ <string>1</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QPushButton" name="buttonCE">
+ <property name="styleSheet">
+ <string notr="true">color:red;font-weight:bold</string>
+ </property>
+ <property name="text">
+ <string>CE</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="button9">
+ <property name="text">
+ <string>9</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QPushButton" name="button5">
+ <property name="text">
+ <string>5</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="button7">
+ <property name="text">
+ <string>7</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QPushButton" name="button2">
+ <property name="text">
+ <string>2</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="2">
+ <widget class="QPushButton" name="button0">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="button6">
+ <property name="text">
+ <string>6</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="button3">
+ <property name="text">
+ <string>3</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="buttonPoint">
+ <property name="text">
+ <string>.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QPushButton" name="buttonPlus">
+ <property name="text">
+ <string>+</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="4">
+ <widget class="QPushButton" name="buttonMinus">
+ <property name="text">
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QPushButton" name="buttonStar">
+ <property name="text">
+ <string>*</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="4">
+ <widget class="QPushButton" name="buttonDiv">
+ <property name="text">
+ <string>/</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="3" colspan="2">
+ <widget class="QPushButton" name="buttonEq">
+ <property name="text">
+ <string>=</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="button8">
+ <property name="text">
+ <string>8</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="buttonC">
+ <property name="styleSheet">
+ <string notr="true">color: red; font-weight:bold</string>
+ </property>
+ <property name="text">
+ <string>C</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/calc/main.cpp b/examples/calc/main.cpp
new file mode 100644
index 0000000..9d30c6b
--- /dev/null
+++ b/examples/calc/main.cpp
@@ -0,0 +1,15 @@
+
+#include <QtGui/QApplication>
+#include "calc.h"
+#include "qscriptedstatemachine.h"
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QtScriptedStateMachine *sm = QtScriptedStateMachine::load(":/calc.scxml");
+ CalcWidget w;
+ sm->registerObject(&w,"",true);
+ QObject::connect (&w, SIGNAL(command(QString)), sm, SLOT(postNamedEvent(QString)));
+ w.show();
+ sm->start();
+ return a.exec();
+}
diff --git a/examples/examples.pro b/examples/examples.pro
new file mode 100644
index 0000000..a13e798
--- /dev/null
+++ b/examples/examples.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += blackjack calc mediaplayer \ No newline at end of file
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
diff --git a/qscxml.pro b/qscxml.pro
index 208c38e..67fed9e 100644
--- a/qscxml.pro
+++ b/qscxml.pro
@@ -1,12 +1,2 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2009-05-24T14:33:48
-#
-#-------------------------------------------------
-
-QT += script
-QT -= gui
-TARGET = qscxml
-TEMPLATE = staticlib
-SOURCES += qscxml.cpp
-HEADERS += qscxml.h
+TEMPLATE = subdirs
+SUBDIRS += src examples \ No newline at end of file
diff --git a/qscxml.cpp b/src/qscxml.cpp
index 741dc33..8190056 100644
--- a/qscxml.cpp
+++ b/src/qscxml.cpp
@@ -56,7 +56,6 @@
#include "qscxml.h"
#include <QScriptEngine>
#include <QScriptValueIterator>
-#include "qstatefinishedevent.h"
#include <QDebug>
#include <QTimer>
#include <QSignalMapper>
@@ -67,12 +66,11 @@
#include <QDir>
#include <QSet>
#include <QStack>
-#include "qhistorystate.h"
-#include "qabstracttransition_p.h"
-#include "qfinalstate.h"
-#include "qabstractstate.h"
+#include <QHistoryState>
+#include <QFinalState>
+#include <QState>
#ifdef QT_GUI_LIB
-#include "qscxmlguiinvokers_p.h"
+#include "qscxmlgui.h"
#endif
@@ -443,7 +441,7 @@ QScxmlTransition::QScxmlTransition (QState* state,QScxml* machine)
/*!
\internal
*/
-bool QScxmlTransition::eventTest(QEvent *e) const
+bool QScxmlTransition::eventTest(QEvent *e)
{
QScriptEngine* engine = scxml->scriptEngine();
QString ev;
@@ -451,8 +449,6 @@ bool QScxmlTransition::eventTest(QEvent *e) const
if (e) {
if (e->type() == QScxmlEvent::eventType()) {
ev = ((QScxmlEvent*)e)->eventName();
- } else if (e->type() == QEvent::Type(QEvent::User-2)) {
- ev = QString("done.state.") + ((QStateFinishedEvent*)e)->state()->objectName();
}
if (!(eventPrefix() == "*" || eventPrefix() == ev || ev.startsWith(eventPrefix()+".")))
return false;
@@ -682,10 +678,6 @@ void QScxml::beginSelectTransitions(QEvent* ev)
}
eventObj.setProperty("data",dataObj);
emit eventTriggered(se->eventName());
- } else if (ev->type() == QEvent::Type(QEvent::User-2)) {
- QString n = QString("done.state.")+((QStateFinishedEvent*)ev)->state()->objectName();
- eventObj.setProperty("name",qScriptValueFromValue<QString>(pvt->scriptEng, n));
- emit eventTriggered(n);
}
}
scriptEngine()->globalObject().setProperty("_event",eventObj);
@@ -851,13 +843,13 @@ QScxml::~QScxml()
delete pvt;
}
-QScxmlInvoker::QString id () const
+QString QScxmlInvoker::id () const
{
- return initEvent->invokeID;
+ return initEvent->metaData.invokeID;
}
void QScxmlInvoker::setID(const QString & id)
{
- initEvent->invokeID = id;
+ initEvent->metaData.invokeID = id;
}
QScxmlInvoker::~QScxmlInvoker()
@@ -865,7 +857,7 @@ QScxmlInvoker::~QScxmlInvoker()
if (cancelled)
postParentEvent("CancelResponse");
else
- postParentEvent(QString("done.invoke.%1").arg(metaData.invokeID));
+ postParentEvent(QString("done.invoke.%1").arg(initEvent->metaData.invokeID));
}
/*!
\property QScxml::baseUrl
@@ -914,7 +906,23 @@ struct ScTransitionInfo
QStringList targets;
QString anchor;
QString script;
- ScTransitionInfo() : {}
+ ScTransitionInfo() : transition(NULL) {}
+};
+
+class QScxmlScriptExec : public QObject
+{
+ Q_OBJECT
+ QString script;
+ QScxml* scxml;
+ public:
+ QScxmlScriptExec(const QString & scr, QScxml* scx) : script(scr),scxml(scx)
+ {
+ }
+ public Q_SLOTS:
+ void exec()
+ {
+ scxml->executeScript(script);
+ }
};
struct ScStateInfo
@@ -945,13 +953,13 @@ struct ScExecContext
QScxmlScriptExec* exec = new QScxmlScriptExec(script,sm);
switch(type) {
case StateEntry:
- connect(state,SIGNAL(entered()),exec,SLOT(exec()));
+ QObject::connect(state,SIGNAL(entered()),exec,SLOT(exec()));
break;
case StateExit:
- connect(state,SIGNAL(exited()),exec,SLOT(exec()));
+ QObject::connect(state,SIGNAL(exited()),exec,SLOT(exec()));
break;
case Transition:
- connect(trans,SIGNAL(activated()),exec,SLOT(exec()));
+ QObject::connect(trans,SIGNAL(activated()),exec,SLOT(exec()));
break;
default:
delete exec;
@@ -971,7 +979,8 @@ class QScxmlLoader
QList<ScHistoryInfo> historyInfo;
QHash<QString,QAbstractState*> stateByID;
QSet<QString> signalEvents;
- void loadState (QState* state, QIODevice* dev, const QString & stateID,const QString & filename);
+ QSet<QState*> statesWithFinal;
+ void loadState (QState* state, QIODevice* dev, const QString & stateID,const QString & filename);
QScxml* load (QIODevice* device, QObject* obj = NULL, const QString & filename = "");
QScriptValue evaluateFile (const QString & fn)
@@ -1042,22 +1051,6 @@ class QScxmlAnchorRestore : public QObject
}
};
-class QScxmlScriptExec : public QObject
-{
- Q_OBJECT
- QScxml* scxml;
- QString script;
- public:
- QScxmlScriptExec(const QString & scr, QScxml* sc)
- :scxml(sc),script(scr)
- {
- }
- public Q_SLOTS:
- void execute()
- {
- scxml->executeScript(script);
- }
-};
static QString sanitize (const QString & str)
{
return str;
@@ -1069,7 +1062,7 @@ static QString sanitize (const QStringRef & str)
return sanitize(str.toString());
}
-void QtScStreamLoader::loadState (
+void QScxmlLoader::loadState (
QState* stateParam,
QIODevice *dev,
const QString & stateID,
@@ -1153,16 +1146,21 @@ void QtScStreamLoader::loadState (
} else if (r.name().toString().compare("history",Qt::CaseInsensitive) == 0) {
if (curState) {
QString id = r.attributes().value("id").toString();
- curHistoryState = curState->addHistoryState(r.attributes().value("type") == "shallow" ? QState::ShallowHistory : QState::DeepHistory);
+ curHistoryState = new QHistoryState(r.attributes().value("type") == "shallow" ? QHistoryState::ShallowHistory : QHistoryState::DeepHistory,curState);
curHistoryState->setObjectName(id);
stateByID[id] = curHistoryState;
}
} else if (r.name().toString().compare("final",Qt::CaseInsensitive) == 0) {
if (curState) {
QString id = r.attributes().value("id").toString();
- QtFinalState* f = new QtFinalState(curState);
+ QFinalState* f = new QFinalState(curState);
f->setObjectName(id);
curExecContext.state = f;
+ statesWithFinal.insert(curState);
+ QState* gp = qobject_cast<QState*>(curState->parentState());
+ if (gp->childMode() == QState::ParallelStates) {
+ statesWithFinal.insert(gp);
+ }
stateByID[id] = f;
}
} else if (r.name().toString().compare("script",Qt::CaseInsensitive) == 0) {
@@ -1236,7 +1234,7 @@ void QtScStreamLoader::loadState (
idLocation = r.attributes().value("idlocation").toString();
if (idLocation.isEmpty())
idLocation = r.attributes().value("invokeid").toString();
- connect (curState, SIGNAL(exited()),new QScxmlScriptExec(QString("invoke_%1.cancel();").arg(curState->objectName()),stateMachine),SLOT(exec()));
+ QObject::connect (curState, SIGNAL(exited()),new QScxmlScriptExec(QString("invoke_%1.cancel();").arg(curState->objectName()),stateMachine),SLOT(exec()));
QString type = r.attributes().value("type").toString();
if (type.isEmpty())
@@ -1270,7 +1268,7 @@ void QtScStreamLoader::loadState (
QString anc = r.attributes().value("anchor").toString();
if (!anc.isEmpty()) {
stateMachine->pvt->anchorTransitions.insert(anc,curTransition);
- connect (curTransition, SIGNAL(activated()),new QScxmlAnchorRestore(stateMachine,stateMachine->pvt,anc),SLOT(restore()));
+ QObject::connect (curTransition, SIGNAL(activated()),new QScxmlAnchorRestore(stateMachine,stateMachine->pvt,anc),SLOT(restore()));
}
inf.transition = curTransition;
transitions.append(inf);
@@ -1280,7 +1278,7 @@ void QtScStreamLoader::loadState (
curTransition->setObjectName(QString ("%1 to %2 on %3 if %4 (anchor=%5)").arg(curState->objectName()).arg(inf.targets.join(" ")).arg(curTransition->eventPrefix()).arg(curTransition->conditionExpression()).arg(anc));
}
} else if (r.name().toString().compare("anchor",Qt::CaseInsensitive) == 0) {
- connect(curState,SIGNAL(exited()),new QScxmlAnchorSave(stateMachine,stateMachine->pvt,r.attributes().value("type").toString(),r.attributes().value("snapshot").toString(),curState),SLOT(save()));
+ QObject::connect(curState,SIGNAL(exited()),new QScxmlAnchorSave(stateMachine,stateMachine->pvt,r.attributes().value("type").toString(),r.attributes().value("snapshot").toString(),curState),SLOT(save()));
} else if (r.name().toString().compare("data",Qt::CaseInsensitive) == 0) {
QScriptValue val = qScriptValueFromValue<QString>(stateMachine->scriptEngine(),"") ;
QString id = r.attributes().value("id").toString();
@@ -1319,7 +1317,7 @@ void QtScStreamLoader::loadState (
} else if (r.name().toString().compare("history",Qt::CaseInsensitive) == 0) {
curHistoryState = NULL;
} else if (r.name().toString().compare("final",Qt::CaseInsensitive) == 0) {
- curExecContext.state = curExecContext.state->parent();
+ curExecContext.state = (curExecContext.state->parentState());
} else if (r.name().toString().compare("send",Qt::CaseInsensitive) == 0) {
if (!idLocation.isEmpty())
curExecContext.script += idLocation + " = ";
@@ -1384,6 +1382,10 @@ QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & file
QString scr = QString("%1.connect({e:\"%2\"},_rcvSig);\n").arg(sig).arg(s);
stateMachine->pvt->startScript += scr;
}
+
+ foreach (QState* s, statesWithFinal) {
+ QObject::connect(s,SIGNAL(finished()),stateMachine,SLOT(handleStateFinished()));
+ }
// resolve transitions
@@ -1404,6 +1406,14 @@ QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & file
return stateMachine;
}
+void QScxml::handleStateFinished()
+{
+ QState* state = qobject_cast<QState*>(sender());
+ if (state) {
+ postEvent(new QScxmlEvent("done.state." + state->objectName()));
+ }
+}
+
/*!
Loads a state machine from an scxml file located at \a filename, with parent object \a o.
*/
diff --git a/qscxml.h b/src/qscxml.h
index 896b247..255b33a 100644
--- a/qscxml.h
+++ b/src/qscxml.h
@@ -41,8 +41,10 @@
#ifndef QSCXML_H
#define QSCXML_H
-#include "qstatemachine.h"
-#include "qabstracttransition.h"
+#ifndef QT_NO_STATEMACHINE
+
+#include <QStateMachine>
+#include <QAbstractTransition>
#include <QVariant>
#include <QEvent>
#include <QStringList>
@@ -102,7 +104,7 @@ class QScxmlTransition : public QAbstractTransition
Q_SIGNALS:
void activated ();
protected:
- bool eventTest(QEvent*) const;
+ bool eventTest(QEvent*);
void onTransition (QEvent*) { emit activated(); }
private:
QScxml* scxml;
@@ -115,7 +117,7 @@ class QScxmlInvoker : public QObject
Q_PROPERTY (QString id READ id WRITE setID)
protected:
- QScxmlInvoker(QScxmlEvent* ievent, QStateMachine* p) : QObject(p), initEvent(ievent) {}
+ QScxmlInvoker(QScxmlEvent* ievent, QStateMachine* p) : QObject(p), initEvent(ievent),cancelled(false) {}
public:
virtual ~QScxmlInvoker();
@@ -133,6 +135,7 @@ class QScxmlInvoker : public QObject
QScxml* parentStateMachine() { return (QScxml*)parent(); }
void postParentEvent (QScxmlEvent* ev);
QScxmlEvent* initEvent;
+ bool cancelled;
friend struct QScxmlFunctions;
};
@@ -184,6 +187,7 @@ class QScxml : public QStateMachine
private Q_SLOTS:
void registerSession();
void unregisterSession();
+ void handleStateFinished();
Q_SIGNALS:
void eventTriggered(const QString &);
@@ -193,5 +197,5 @@ class QScxml : public QStateMachine
friend class QScxmlLoader;
friend struct QScxmlFunctions;
};
-
+#endif
#endif // QSCXML_H
diff --git a/src/qscxml.pri b/src/qscxml.pri
new file mode 100644
index 0000000..effa64d
--- /dev/null
+++ b/src/qscxml.pri
@@ -0,0 +1,6 @@
+QT += script
+SOURCES += $$PWD/qscxml.cpp \
+ $$PWD/qscxmlgui.cpp
+
+HEADERS += $$PWD/qscxml.h \
+ $$PWD/qscxmlgui.h
diff --git a/src/qscxmlgui.cpp b/src/qscxmlgui.cpp
new file mode 100644
index 0000000..3fdfabc
--- /dev/null
+++ b/src/qscxmlgui.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+#include "qscxmlgui.h"
+#include <QMenu>
+#include <QDebug>
+#include <QMessageBox>
+#include <QScriptValueIterator>
+#include <QScriptEngine>
+#include <QSignalMapper>
+/*
+
+ { "parent" : parentObject,
+ "trackHovers" : true/false
+ "children": {{"type": "action", "text": "",},
+ {"type": "menu"},
+ {"type": "separator"} },
+ */
+namespace
+{
+ void traverseMenu (QMenu* menu, QScriptValue value, QSignalMapper* clickMap, QSignalMapper* hoverMap, bool trackHover)
+ {
+ QScriptValueIterator it (value);
+ while (it.hasNext()) {
+ it.next();
+ if (it.name() == "trackHover") {
+ trackHover = it.value().toBoolean();
+ } else if (it.name() == "parent") {
+ } else if (it.name() == "children") {
+ QScriptValueIterator cit (it.value());
+ while (cit.hasNext()) {
+ cit.next();
+ QString type = cit.value().property("type").toString();
+ if (type == "action") {
+ QAction* act = menu->addAction("");
+ QScriptValueIterator ait (cit.value());
+ while (ait.hasNext()) {
+ ait.next();
+ if (ait.name() != "type") {
+ act->setProperty(ait.name().toAscii().constData(),ait.value().toVariant());
+ }
+ }
+ QObject::connect(act,SIGNAL(triggered()),clickMap,SLOT(map()));
+ clickMap->setMapping(act,QString("menu.action." + cit.value().property("id").toString()));
+ if (trackHover) {
+ QObject::connect(act,SIGNAL(hovered()),hoverMap,SLOT(map()));
+ hoverMap->setMapping(act,QString("menu.hover." + cit.value().property("id").toString()));
+ }
+ } else if (type == "menu") {
+ traverseMenu(menu->addMenu(""),it.value(),clickMap,hoverMap,trackHover);
+ } else if (type == "separator") {
+ menu->addSeparator();
+ }
+ }
+ } else {
+ menu->setProperty(it.name().toAscii().constData(),it.value().toVariant());
+ }
+ }
+ }
+};
+
+void QScxmlMenuInvoker::activate ()
+{
+ QScxmlEvent* ie = initEvent;
+ QScriptValue v = ie->content();
+ QWidget* parent = qobject_cast<QWidget*>(v.property("parent").toQObject());
+ menu = new QMenu(parent);
+ QSignalMapper* clickMap = new QSignalMapper(this);
+ QSignalMapper* hoverMap = new QSignalMapper(this);
+ connect (clickMap,SIGNAL(mapped(QString)), this, SLOT(postParentEvent(QString)));
+ connect (hoverMap,SIGNAL(mapped(QString)), this, SLOT(postParentEvent(QString)));
+ traverseMenu(menu,v,clickMap,hoverMap,false);
+ menu->setParent(parent,Qt::Widget);
+ menu->move(QPoint(0,0));
+ menu->resize(parent->size());
+ menu->show();
+}
+void QScxmlMenuInvoker::cancel ()
+{
+ if (menu)
+ menu->deleteLater();
+ QScxmlInvoker::cancel();
+}
+
+Q_SCRIPT_DECLARE_QMETAOBJECT(QMenu,QWidget*)
+Q_SCRIPT_DECLARE_QMETAOBJECT(QMessageBox,QWidget*)
+ void QScxmlMenuInvoker::initInvokerFactory(QScxml* sm)
+ {
+ QScriptEngine* se = sm->scriptEngine();
+ se->globalObject().setProperty("QMenu",qScriptValueFromQMetaObject<QMenu>(se));
+ }
+ void QScxmlMessageBoxInvoker::initInvokerFactory(QScxml* sm)
+ {
+ QScriptEngine* se = sm->scriptEngine();
+ se->globalObject().setProperty("QMessageBox",qScriptValueFromQMetaObject<QMessageBox>(se));
+ }
+
+void QScxmlMessageBoxInvoker::onFinished(int n) {
+ postParentEvent(new QScxmlEvent("q-messagebox.finished",QStringList()<<"result",QVariantList()<<QVariant(n)));
+}
+/*
+ { "parent": someWidget, "buttons": ...}
+ */
+void QScxmlMessageBoxInvoker::activate()
+{
+ QScriptValue v = initEvent->content();
+ QWidget* parent = qobject_cast<QWidget*>(v.property("parent").toQObject());
+ messageBox = new QMessageBox(parent);
+ messageBox->setModal(false);
+ QScriptValueIterator it (v);
+ while (it.hasNext()) {
+ it.next();
+ if (it.name() == "standardButtons") {
+ messageBox->setStandardButtons((QMessageBox::StandardButtons)it.value().toInt32());
+ } else if (it.name() == "icon") {
+ messageBox->setIcon((QMessageBox::Icon)it.value().toInt32());
+ } else if (it.name() != "parent") {
+ messageBox->setProperty(it.name().toAscii().constData(),it.value().toVariant());
+ }
+ }
+ connect(messageBox,SIGNAL(finished(int)),this,SLOT(onFinished(int)));
+ messageBox->show ();
+}
+
+void QScxmlMessageBoxInvoker::cancel()
+{
+ messageBox->deleteLater();
+ QScxmlInvoker::cancel();
+}
diff --git a/src/qscxmlgui.h b/src/qscxmlgui.h
new file mode 100644
index 0000000..2560ad7
--- /dev/null
+++ b/src/qscxmlgui.h
@@ -0,0 +1,48 @@
+#ifndef QSSMGUIINVOKERS_P_H
+#define QSSMGUIINVOKERS_P_H
+
+#include "qscxml.h"
+class QMenu;
+class QMessageBox;
+
+class QScxmlMenuInvoker: public QScxmlInvoker
+{
+ Q_OBJECT
+
+ public:
+ QScxmlMenuInvoker(QScxmlEvent* ievent, QScxml* p) : QScxmlInvoker(ievent,p),menu(0)
+ {
+ }
+ static void initInvokerFactory(QScxml*);
+ static bool isTypeSupported (const QString & s) { return s== "q-menu"; }
+ public Q_SLOTS:
+ void activate ();
+ void cancel ();
+
+ private:
+ QMenu* menu;
+};
+
+
+class QScxmlMessageBoxInvoker: public QScxmlInvoker
+{
+ Q_OBJECT
+
+ public:
+ QScxmlMessageBoxInvoker(QScxmlEvent* ievent, QScxml* p) : QScxmlInvoker(ievent,p),messageBox(0)
+ {
+ }
+
+ static void initInvokerFactory(QScxml*);
+ static bool isTypeSupported (const QString & s) { return s== "q-messagebox"; }
+ public Q_SLOTS:
+ void activate ();
+ void cancel ();
+
+ private Q_SLOTS:
+ void onFinished (int);
+ private:
+ QMessageBox* messageBox;
+};
+
+#endif // QSSMGUIINVOKERS_P_H
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..df334f7
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,10 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2009-05-24T14:33:48
+#
+#-------------------------------------------------
+QT += script
+TARGET = qscxml
+TEMPLATE = lib
+CONFIG += staticlib
+include (qscxml.pri) \ No newline at end of file