From 59feffc5a253fec33b310f7a0105c8ef42b9019b Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 14 Jun 2015 01:35:25 +0200 Subject: paperreplay working --- mainwindow.cc | 69 ++++++++++++++++++++++++++++++++++- mainwindow.h | 9 +++++ mainwindow.ui | 80 ++++++++++++++++++++++++++++++++++++++-- notebookview.cc | 9 ++++- notebookview.h | 3 ++ pageitem.cc | 14 +++++-- pageitem.h | 4 +- paperreplay.cc | 105 +++++++++++++++++++++++++++++++++++++++++------------ paperreplay.h | 36 +++++++++++++----- scribiu.pro | 8 ++-- stfgraphicsitem.cc | 29 +++++++++++---- stfgraphicsitem.h | 3 +- stfreader.cc | 4 +- stfreader.h | 4 +- stfstrokeitem.cc | 36 ++++++++++++++++++ stfstrokeitem.h | 25 +++++++++++++ 16 files changed, 379 insertions(+), 59 deletions(-) create mode 100644 stfstrokeitem.cc create mode 100644 stfstrokeitem.h diff --git a/mainwindow.cc b/mainwindow.cc index c509d4e..e269c61 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -6,7 +6,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), _notebooks(new NotebookModel(this)), - _manager(new SmartpenManager(this)) + _manager(new SmartpenManager(this)), + _media(new Phonon::MediaObject(this)), + _mediaOutput(new Phonon::AudioOutput(this)) { ui->setupUi(this); ui->notebookTree->setModel(_notebooks); @@ -14,6 +16,11 @@ MainWindow::MainWindow(QWidget *parent) : ui->notebookTree->header()->setResizeMode(1, QHeaderView::Fixed); ui->notebookTree->header()->setResizeMode(2, QHeaderView::Fixed); ui->notebookTree->expandAll(); + Phonon::createPath(_media, _mediaOutput); + ui->replaySlider->setMediaObject(_media); + ui->pauseButton->setVisible(false); + connect(_media, SIGNAL(stateChanged(Phonon::State,Phonon::State)), + this, SLOT(handleMediaStateChange(Phonon::State))); } MainWindow::~MainWindow() @@ -65,3 +72,63 @@ void MainWindow::handleCurPageChanged() { ui->pageEdit->setText(QString::number(ui->notebookView->curPage() + 1)); } + +void MainWindow::handlePaperReplayRequested(const QString &file, qint64 time) +{ + QFileInfo finfo(file); + if (!finfo.exists()) { + qWarning() << "Cannot open paper replay media file:" << finfo.canonicalFilePath(); + } + + QString filePath = finfo.canonicalFilePath(); + + if (_media->currentSource().fileName() != filePath) { + _media->setCurrentSource(filePath); + } + + switch (_media->state()) { + case Phonon::PlayingState: + case Phonon::BufferingState: + case Phonon::PausedState: + _pendingSeek = 0; + _media->seek(time); + break; + default: + _pendingSeek = time; + break; + } + + _media->play(); +} + +void MainWindow::handlePaperReplayPlay() +{ + _media->play(); +} + +void MainWindow::handlePaperReplayPause() +{ + _media->pause(); +} + +void MainWindow::handleMediaStateChange(Phonon::State state) +{ + qDebug() << "Media state change:" << state; + switch (state) { + case Phonon::PlayingState: + ui->playButton->setVisible(false); + ui->pauseButton->setVisible(true); + if (_pendingSeek) { + _media->seek(_pendingSeek); + _pendingSeek = 0; + } + break; + case Phonon::PausedState: + ui->playButton->setVisible(true); + ui->pauseButton->setVisible(false); + break; + default: + ui->playButton->setVisible(true); + ui->pauseButton->setVisible(false); + } +} diff --git a/mainwindow.h b/mainwindow.h index 4072ffa..7a8fab4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -2,6 +2,8 @@ #define MAINWINDOW_H #include +#include +#include #include "notebookmodel.h" #include "smartpenmanager.h" @@ -24,11 +26,18 @@ public slots: private slots: void handleNotebookSelected(const QModelIndex &index); void handleCurPageChanged(); + void handlePaperReplayRequested(const QString &file, qint64 time); + void handlePaperReplayPlay(); + void handlePaperReplayPause(); + void handleMediaStateChange(Phonon::State state); private: Ui::MainWindow *ui; NotebookModel *_notebooks; SmartpenManager *_manager; + Phonon::MediaObject *_media; + Phonon::AudioOutput *_mediaOutput; + qint64 _pendingSeek; QString _curPenName; QString _curNotebookName; diff --git a/mainwindow.ui b/mainwindow.ui index ac3dce3..4a0e1ea 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -206,17 +206,31 @@ ... - + + + - - - Qt::Horizontal + + + + 30 + 30 + + + + ... + + + + + + @@ -271,11 +285,18 @@
notebookview.h
curPageChanged() + paperReplayRequested(QString,qint64) setZoom(int) prevPage() nextPage() + + Phonon::SeekSlider + QWidget +
Phonon/SeekSlider
+ 1 +
@@ -375,10 +396,61 @@ + + notebookView + paperReplayRequested(QString,qint64) + MainWindow + handlePaperReplayRequested(QString,qint64) + + + 521 + 193 + + + 358 + 192 + + + + + playButton + clicked() + MainWindow + handlePaperReplayPlay() + + + 341 + 343 + + + 358 + 192 + + + + + pauseButton + clicked() + MainWindow + handlePaperReplayPause() + + + 377 + 343 + + + 358 + 192 + + + handleNotebookSelected(QModelIndex) handleZoomChanged(int) handleCurPageChanged() + handlePaperReplayRequested(QString,qint64) + handlePaperReplayPlay() + handlePaperReplayPause() diff --git a/notebookview.cc b/notebookview.cc index aa37ac9..9553451 100644 --- a/notebookview.cc +++ b/notebookview.cc @@ -88,6 +88,11 @@ void NotebookView::setZoom(int zoom) } } +void NotebookView::requestPaperReplay(const QString &file, qint64 time) +{ + emit paperReplayRequested(file, time); +} + void NotebookView::clear() { removePages(); @@ -158,7 +163,7 @@ bool NotebookView::createPages() if (pens.isEmpty()) return false; // Failure to open paperreplay data is not fatal - _replay->open(_replayPath, pens[0], _nb->guid()); + bool haveReplay = _replay->open(_replayPath, pens[0], _nb->guid()); QList pagesWithStrokes = _nb->pagesWithStrokes(pens.first()); Q_ASSERT(_pages.isEmpty()); @@ -166,7 +171,7 @@ bool NotebookView::createPages() _maxPageSize.setWidth(0); _maxPageSize.setHeight(0); foreach (int pageNum, pagesWithStrokes) { - PageItem *page = new PageItem(_nb, pageNum); + PageItem *page = new PageItem(_nb, haveReplay ? _replay : 0, pageNum); QRectF box = page->boundingRect(); if (box.width() > _maxPageSize.width()) { _maxPageSize.setWidth(box.width()); diff --git a/notebookview.h b/notebookview.h index 567d5cf..1b3eb46 100644 --- a/notebookview.h +++ b/notebookview.h @@ -32,10 +32,13 @@ public: int zoom() const; + void requestPaperReplay(const QString &file, qint64 time); + signals: void pageNumbersChanged(); void curPageChanged(); void zoomChanged(); + void paperReplayRequested(const QString &file, qint64 time); public slots: void clear(); diff --git a/pageitem.cc b/pageitem.cc index f938eeb..3bd5b7f 100644 --- a/pageitem.cc +++ b/pageitem.cc @@ -3,8 +3,8 @@ #include "pageitem.h" #include "stfgraphicsitem.h" -PageItem::PageItem(AfdNotebook *nb, int pageNum, QGraphicsItem *parent) : - QGraphicsItem(parent), _nb(nb), _pageNum(pageNum), _strokesLoaded(false) +PageItem::PageItem(AfdNotebook *nb, PaperReplay *replay, int pageNum, QGraphicsItem *parent) : + QGraphicsItem(parent), _nb(nb), _replay(replay), _pageNum(pageNum), _strokesLoaded(false) { _pageSize = _nb->getPageSize(pageNum); _pageTrim = _nb->getPageTrim(pageNum); @@ -51,6 +51,13 @@ void PageItem::createStrokes() { QStringList pens = _nb->penSerials(); if (pens.isEmpty()) return; + + // Load paper replay data if available + AfdPageAddress pageAddr = _nb->getPageAddress(_pageNum); + PaperReplay::SessionList replays = _replay ? _replay->sessions(pageAddr.toUInt64()) : PaperReplay::SessionList(); + + // Now create a new StfGraphicsItem for every stroke file + // Which will in turn create a stroke item for every stroke QStringList strokeFiles = _nb->strokeFiles(pens.first(), _pageNum); foreach (const QString &strokeFile, strokeFiles) { QFile f(strokeFile); @@ -58,7 +65,8 @@ void PageItem::createStrokes() qWarning() << "Could not open stroke file:" << strokeFile; continue; } - new StfGraphicsItem(&f, this); + new StfGraphicsItem(&f, replays, this); } + qDebug() << "strokes loaded for page" << _pageNum; } diff --git a/pageitem.h b/pageitem.h index 2e6059a..10959d9 100644 --- a/pageitem.h +++ b/pageitem.h @@ -4,11 +4,12 @@ #include #include "afdnotebook.h" +#include "paperreplay.h" class PageItem : public QGraphicsItem { public: - explicit PageItem(AfdNotebook *nb, int pageNum, QGraphicsItem *parent = 0); + explicit PageItem(AfdNotebook *nb, PaperReplay *replay, int pageNum, QGraphicsItem *parent = 0); enum { Type = UserType + 'p' }; @@ -23,6 +24,7 @@ private: private: AfdNotebook *_nb; + PaperReplay *_replay; int _pageNum; QSize _pageSize; QRect _pageTrim; diff --git a/paperreplay.cc b/paperreplay.cc index 579ea3c..e0e654e 100644 --- a/paperreplay.cc +++ b/paperreplay.cc @@ -24,14 +24,72 @@ PaperReplay::PaperReplay(QObject *parent) : { } -PaperReplay::Session::Session() : d(new SessionData) +PaperReplay::Session::Session() : d() { } +PaperReplay::Session::Session(quint64 id) : d(new SessionData) +{ + d->id = id; +} + PaperReplay::Session::~Session() { } +bool PaperReplay::Session::isValid() const +{ + return d; +} + +quint64 PaperReplay::Session::id() const +{ + return d ? d->id : 0; +} + +QString PaperReplay::Session::fileName() const +{ + return d->file; +} + +qint64 PaperReplay::Session::startTime() const +{ + return d->start; +} + +qint64 PaperReplay::Session::endTime() const +{ + return d->end; +} + +PaperReplay::SessionList::SessionList() +{ +} + +PaperReplay::SessionList::SessionList(const QMap &byTime) + : _m(byTime) +{ +} + +QList PaperReplay::SessionList::sessionsDuringTime(qint64 time) const +{ + QList sessions; + if (_m.isEmpty()) return sessions; + QMap::const_iterator it = _m.lowerBound(time); + + if (it == _m.end()) --it; + + while (it->d->start <= time && time <= it->d->end) { + sessions.append(*it); + if (it == _m.begin()) { + break; + } else { + --it; + } + } + return sessions; +} + bool PaperReplay::open(const QString &path, const QString &penSerial, quint64 notebookGuid) { _dir.setPath(path + QString("/userdata/%1/Paper Replay/99/%2/sessions") @@ -44,44 +102,43 @@ bool PaperReplay::open(const QString &path, const QString &penSerial, quint64 no QDirIterator iter(_dir.path(), QStringList("PRS-*"), QDir::Dirs | QDir::NoDotAndDotDot); while (iter.hasNext()) { - QDir sessionDir(iter.next()); bool ok; + QDir sessionDir(iter.next()); quint64 sessionId = sessionDir.dirName().mid(4).toULongLong(&ok, 16); + if (!ok) { qWarning() << "Invalid session identifier:" << sessionDir.dirName(); + continue; } - Session &session = _sessions[sessionId]; + + Session session(sessionId); if (!parseSessionInfo(session.d, sessionDir.filePath("session.info"))) { qWarning() << "Could not parse:" << sessionDir.absoluteFilePath("session.info"); } if (!parseSessionPages(session.d, sessionDir.filePath("session.pages"))) { qWarning() << "Could not parse:" << sessionDir.absoluteFilePath("session.pages"); } - } + if (!session.d->file.isEmpty()) { + session.d->file = sessionDir.filePath(session.d->file); + } + + foreach (quint64 page, session.d->pages) { + _byPageTime[page].insert(session.d->start, session); + } + } return true; } void PaperReplay::close() { - _sessions.clear(); - _byPage.clear(); + _byPageTime.clear(); _dir.setPath(QString()); } -QList PaperReplay::sessions() const +PaperReplay::SessionList PaperReplay::sessions(quint64 pageAddress) const { - return _sessions.values(); -} - -QList PaperReplay::sessions(quint64 pageAddress) const -{ - QList sessions; - QMultiMap::const_iterator it = _byPage.constFind(pageAddress); - while (it != _byPage.end() && it.key() == pageAddress) { - sessions.append(_sessions[it.value()]); - } - return sessions; + return SessionList(_byPageTime[pageAddress]); } bool PaperReplay::parseSessionInfo(SessionData *session, const QString &path) @@ -123,7 +180,7 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev) QDataStream s(dev); if (s.skipRawData(5) != 5) return false; - quint64 startTime, endTime, creationTime; + qint64 startTime, endTime, creationTime; QString name; s >> startTime >> endTime >> creationTime; @@ -132,10 +189,10 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev) } session->name = name; - session->start = Smartpen::fromPenTime(startTime); - session->end = Smartpen::fromPenTime(endTime); + session->start = startTime; + session->end = endTime; - qDebug() << "Session:" << name << session->start << session->end; + qDebug() << "Session:" << name << Smartpen::fromPenTime(session->start) << Smartpen::fromPenTime(session->end); quint16 num_clips; s >> num_clips; @@ -156,6 +213,7 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev) quint16 num_strokes; s >> num_strokes; + // TODO: for (uint i = 0; i < num_strokes; ++i) { quint64 a, b, c; quint32 d; @@ -216,7 +274,8 @@ bool PaperReplay::parseSessionPagesV1(SessionData *session, QIODevice *dev) session->pages.reserve(session->pages.size() + num_pages); for (uint i = 0; i < num_pages; ++i) { - quint64 address, time; + quint64 address; + qint64 time; s >> address >> time; session->pages.append(address); diff --git a/paperreplay.h b/paperreplay.h index e771584..c9eb6f2 100644 --- a/paperreplay.h +++ b/paperreplay.h @@ -1,10 +1,9 @@ #ifndef PAPERREPLAY_H #define PAPERREPLAY_H -#include #include #include -#include +#include #include #define PAPER_REPLAY "Paper Replay" @@ -14,8 +13,9 @@ class PaperReplay : public QObject Q_OBJECT struct SessionData : public QSharedData { + quint64 id; QString name; - QDateTime start, end; + qint64 start, end; QVector pages; QString file; }; @@ -28,26 +28,45 @@ public: Session(); ~Session(); + bool isValid() const; + + quint64 id() const; + QString name() const; - QDateTime startTime() const; - QDateTime endTime() const; + qint64 startTime() const; + qint64 endTime() const; QVector pages() const; QString fileName() const; private: + Session(quint64 id); QSharedDataPointer d; + friend class PaperReplay; + friend class SessionList; + }; + + class SessionList { + public: + SessionList(); + + QList sessionsDuringTime(qint64 time) const; + + private: + explicit SessionList(const QMap& byTime); + + QMap _m; + friend class PaperReplay; }; bool open(const QString &path, const QString &penSerial, quint64 notebookGuid); void close(); - QList sessions() const; - QList sessions(quint64 pageAddress) const; + SessionList sessions(quint64 pageAddress) const; private: static bool parseSessionInfo(SessionData *session, const QString &path); @@ -60,8 +79,7 @@ private: private: QDir _dir; - QHash _sessions; - QMultiMap _byPage; + QMap > _byPageTime; }; #endif // PAPERREPLAY_H diff --git a/scribiu.pro b/scribiu.pro index 861ceff..a49ef42 100644 --- a/scribiu.pro +++ b/scribiu.pro @@ -1,4 +1,4 @@ -QT += core gui +QT += core gui phonon greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -21,7 +21,8 @@ SOURCES += main.cc\ pageitem.cc \ stfgraphicsitem.cc \ paperreplay.cc \ - afdpageaddress.cc + afdpageaddress.cc \ + stfstrokeitem.cc HEADERS += mainwindow.h \ smartpenmanager.h \ @@ -34,6 +35,7 @@ HEADERS += mainwindow.h \ pageitem.h \ stfgraphicsitem.h \ paperreplay.h \ - afdpageaddress.h + afdpageaddress.h \ + stfstrokeitem.h FORMS += mainwindow.ui diff --git a/stfgraphicsitem.cc b/stfgraphicsitem.cc index 414cf6c..3c3a48b 100644 --- a/stfgraphicsitem.cc +++ b/stfgraphicsitem.cc @@ -2,38 +2,51 @@ #include #include "stfreader.h" +#include "smartpen.h" +#include "stfstrokeitem.h" #include "stfgraphicsitem.h" class StfToGraphicsPathItems : public StfReader::StrokeHandler { QGraphicsItem *parent; + PaperReplay::SessionList replays; + PaperReplay::Session replay; + qint64 startTime, lastTime; QPainterPath path; QRectF bound; public: - StfToGraphicsPathItems(QGraphicsItem* parent) - : parent(parent), path(), bound(0.0, 0.0, 1.0, 1.0) { + StfToGraphicsPathItems(const PaperReplay::SessionList &replays, QGraphicsItem* parent) + : parent(parent), replays(replays), path(), bound(0.0, 0.0, 1.0, 1.0) { } ~StfToGraphicsPathItems() { } - bool startStroke(const QPoint& p, int force, quint64 time) { + bool startStroke(const QPoint& p, int force, qint64 time) { Q_UNUSED(force); - Q_UNUSED(time); + QList sessions = replays.sessionsDuringTime(time); + if (!sessions.isEmpty()) { + replay = sessions.first(); + } else { + replay = PaperReplay::Session(); + } + startTime = time; + lastTime = time; path = QPainterPath(QPointF(p)); return true; } - bool strokePoint(const QPoint& p, int force, quint64 time) { + bool strokePoint(const QPoint& p, int force, qint64 time) { Q_UNUSED(force); Q_UNUSED(time); + lastTime = time; path.lineTo(QPointF(p)); return true; } bool endStroke() { bound |= path.boundingRect(); - new QGraphicsPathItem(path, parent); + new StfStrokeItem(path, replay, startTime, lastTime, parent); /* Parent will take the child down with him when deleted. */ return true; } @@ -43,12 +56,12 @@ public: } }; -StfGraphicsItem::StfGraphicsItem(QIODevice *dev, QGraphicsItem *parent) : +StfGraphicsItem::StfGraphicsItem(QIODevice *dev, const PaperReplay::SessionList &replays, QGraphicsItem *parent) : QGraphicsItem(parent) { setFlags(ItemHasNoContents); - StfToGraphicsPathItems h(this); + StfToGraphicsPathItems h(replays, this); StfReader r; r.setStrokeHandler(&h); if (r.parse(dev)) { diff --git a/stfgraphicsitem.h b/stfgraphicsitem.h index ed62ede..3d3178e 100644 --- a/stfgraphicsitem.h +++ b/stfgraphicsitem.h @@ -2,13 +2,14 @@ #define STFGRAPHICSITEM_H #include +#include "paperreplay.h" class StfGraphicsItem : public QGraphicsItem { QRectF bbox; public: - explicit StfGraphicsItem(QIODevice *stf, QGraphicsItem *parent = 0); + explicit StfGraphicsItem(QIODevice *stf, const PaperReplay::SessionList &replays, QGraphicsItem *parent = 0); enum { Type = UserType + 's' }; diff --git a/stfreader.cc b/stfreader.cc index c5edff0..fc62603 100644 --- a/stfreader.cc +++ b/stfreader.cc @@ -23,12 +23,12 @@ StfReader::StrokeHandler::~StrokeHandler() bool StfReader::parseV1(BitReader& br) { - quint64 cur_time = 0; + qint64 cur_time = 0; while (!br.atEnd()) { syncV1(br); quint8 header = br.readBits(8); - quint64 time; + qint64 time; QPoint p0, pa; int f0; diff --git a/stfreader.h b/stfreader.h index 06013e1..447e737 100644 --- a/stfreader.h +++ b/stfreader.h @@ -11,8 +11,8 @@ public: { public: virtual ~StrokeHandler(); - virtual bool startStroke(const QPoint& p, int force, quint64 time) = 0; - virtual bool strokePoint(const QPoint& p, int force, quint64 time) = 0; + virtual bool startStroke(const QPoint& p, int force, qint64 time) = 0; + virtual bool strokePoint(const QPoint& p, int force, qint64 time) = 0; virtual bool endStroke() = 0; }; diff --git a/stfstrokeitem.cc b/stfstrokeitem.cc new file mode 100644 index 0000000..caf1ae5 --- /dev/null +++ b/stfstrokeitem.cc @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include "notebookview.h" +#include "stfstrokeitem.h" + +StfStrokeItem::StfStrokeItem(const QPainterPath &stroke, const PaperReplay::Session &session, qint64 startTime, qint64 endTime, QGraphicsItem *parent) + : QGraphicsPathItem(stroke, parent), _session(session), + _startTime(startTime), _endTime(endTime) +{ + if (_session.isValid()) { + setPen(QPen(Qt::green)); + setCursor(Qt::PointingHandCursor); + } +} + +int StfStrokeItem::type() const +{ + return Type; +} + +void StfStrokeItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (_session.isValid() && !_session.fileName().isEmpty()) { + QGraphicsView *view = scene()->views().first(); + if (NotebookView *nbview = qobject_cast(view)) { + event->accept(); + qint64 time = _startTime - _session.startTime(); + if (time < 10) time = 0; + + nbview->requestPaperReplay(_session.fileName(), time); + } + } +} diff --git a/stfstrokeitem.h b/stfstrokeitem.h new file mode 100644 index 0000000..1e780af --- /dev/null +++ b/stfstrokeitem.h @@ -0,0 +1,25 @@ +#ifndef STFSTROKEITEM_H +#define STFSTROKEITEM_H + +#include +#include "paperreplay.h" + +class StfStrokeItem : public QGraphicsPathItem +{ +public: + StfStrokeItem(const QPainterPath &stroke, const PaperReplay::Session &session, qint64 startTime, qint64 endTime, QGraphicsItem *parent = 0); + + enum { Type = UserType + 't' }; + + int type() const; + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + +private: + PaperReplay::Session _session; + qint64 _startTime; + qint64 _endTime; +}; + +#endif // STFSTROKEITEM_H -- cgit v1.2.3