/* * scribiu -- read notebooks and voice memos from Livescribe pens * Copyright (C) 2015 Javier S. Pedro * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "stftxtexport.h" #include "notebookview.h" #define VIEW_MARGIN 2 #define PAGE_SEPARATION 100 NotebookView::NotebookView(QWidget *parent) : QGraphicsView(parent), _nb(new AfdNotebook(this)), _replay(new PaperReplay(this)), _numColumns(1), _zoom(100), _curPage(0) { setScene(new QGraphicsScene(this)); setTransformationAnchor(AnchorUnderMouse); setDragMode(ScrollHandDrag); setRenderHints(QPainter::Antialiasing); } void NotebookView::setNotebook(const QString &path) { removePages(); _nbPath = path; if (!path.isEmpty()) { if (createPages()) { emit pageNumbersChanged(); if (!_pages.isEmpty()) { _curPage = _pages.begin().key(); centerOn(_pages[_curPage]); emit curPageChanged(); } } else { qWarning() << "Could not open notebook:" << _nbPath; } } } QString NotebookView::notebook() const { return _nbPath; } void NotebookView::setPaperReplay(const QString &path) { _replayPath = path; // TODO reload; for now, please set this before the notebook } QString NotebookView::paperReplay() const { return _replayPath; } void NotebookView::setPenUserTime(quint64 userTime) { _penUserTime = userTime; } quint64 NotebookView::penUserTime() const { return _penUserTime; } QList NotebookView::pageNumbers() const { return _pages.keys(); } int NotebookView::curPage() const { return _curPage; } void NotebookView::setCurPage(int page) { if (page != _curPage) { _curPage = page; if (_zoom > 100) { setZoom(100); } if (_pages.contains(_curPage)) { centerOn(_pages[_curPage]); } emit curPageChanged(); } } int NotebookView::zoom() const { return _zoom; } void NotebookView::setZoom(int zoom) { if (zoom != _zoom) { _zoom = zoom; int oldNumColumns = _numColumns; calculateScale(); if (_numColumns != oldNumColumns) { layoutPages(); } emit zoomChanged(_zoom); } } QSize NotebookView::getCurPageSize() const { return _nb->getPageSize(_curPage); } QRect NotebookView::getCurPageTrim() const { return _nb->getPageTrim(_curPage); } QImage NotebookView::exportPageAsImage(int pageNum) const { const QRect pageTrim = _nb->getPageTrim(pageNum); QImage image(pageTrim.width() / 4, pageTrim.height() / 4, QImage::Format_RGB32); QPainter painter(&image); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); renderPage(&painter, pageNum, QRectF(), pageTrim); return image; } void NotebookView::renderPage(QPainter *painter, int pageNum, const QRectF &target, const QRectF &source) const { QGraphicsScene scene; PageItem *item = new PageItem(_nb, 0, pageNum); scene.addItem(item); scene.render(painter, target, source, Qt::KeepAspectRatio); } void NotebookView::exportPageAsTXYP(QIODevice *device, int pageNum, bool relativeTime) { StfTxtExport writer(_nb); writer.exportToTXYP(device, pageNum, relativeTime); } void NotebookView::requestPaperReplay(const QString &file, qint64 time) { emit paperReplayRequested(file, time); } void NotebookView::clear() { removePages(); emit pageNumbersChanged(); } void NotebookView::prevPage() { QMap::iterator it = _pages.lowerBound(_curPage); if (it != _pages.end() && it != _pages.begin()) { --it; setCurPage(it.key()); } } void NotebookView::nextPage() { QMap::iterator it = _pages.upperBound(_curPage); if (it != _pages.end()) { setCurPage(it.key()); } } void NotebookView::focusOnPage(int pageNum) { if (_pages.contains(pageNum)) { if (_zoom < 100) { setZoom(100); } setCurPage(pageNum); } } void NotebookView::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); calculateScale(); } void NotebookView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); QGraphicsItem *item = itemAt(size().width() / 2, size().height() / 2); while (item && item->type() != PageItem::Type) { item = item->parentItem(); } if (item && item->type() == PageItem::Type) { PageItem * page = static_cast(item); int centerPage = page->pageNum(); if (centerPage != _curPage) { _curPage = centerPage; emit curPageChanged(); } } } void NotebookView::removePages() { _pages.clear(); scene()->clear(); scene()->setSceneRect(QRectF()); _nb->close(); _replay->close(); _maxPageSize.setWidth(0); _maxPageSize.setHeight(0); if (_zoom > 100) { _zoom = 100; emit zoomChanged(_zoom); } resetTransform(); } bool NotebookView::createPages() { if (!_nb->open(_nbPath)) return false; QStringList pens = _nb->penSerials(); if (pens.isEmpty()) return false; // Failure to open paperreplay data is not fatal bool haveReplay = _replay->open(_replayPath, _nb->guid(), _penUserTime); QList pagesWithStrokes = _nb->pagesWithStrokes(pens.first()); Q_ASSERT(_pages.isEmpty()); _maxPageSize.setWidth(0); _maxPageSize.setHeight(0); foreach (int pageNum, pagesWithStrokes) { PageItem *page = new PageItem(_nb, haveReplay ? _replay : 0, pageNum); QRectF box = page->boundingRect(); if (box.width() > _maxPageSize.width()) { _maxPageSize.setWidth(box.width()); } if (box.height() > _maxPageSize.height()) { _maxPageSize.setHeight(box.height()); } _pages.insert(pageNum, page); scene()->addItem(page); } calculateScale(); layoutPages(); return true; } void NotebookView::calculateScale() { if (_pages.isEmpty() || _maxPageSize.isEmpty()) return; const int margin = VIEW_MARGIN; QRectF viewRect = viewport()->rect().adjusted(margin, margin, -margin, margin); qreal baseScale = qMin(viewRect.width() / _maxPageSize.width(), viewRect.height() / _maxPageSize.height()); resetTransform(); _numColumns = 1; scale(baseScale, baseScale); if (_zoom < 100) { qreal s = 0.25 + ((_zoom / 100.0) * 0.75); int potentialColumns = viewRect.width() / (_maxPageSize.width() * baseScale * s); if (potentialColumns >= 2) { _numColumns = std::min(potentialColumns, _pages.size()); } scale(s, s); } else if (_zoom > 100) { qreal s = 1.0 + (_zoom - 100) * 0.015; scale(s, s); } } void NotebookView::layoutPages() { const int numRows = (_pages.size() + 1) / _numColumns; const QSizeF pageSpace( _maxPageSize.width() + PAGE_SEPARATION, _maxPageSize.height() + PAGE_SEPARATION); int pageIndex = 0; foreach (PageItem *page, _pages) { QRectF box = page->boundingRect(); qreal curX = (pageIndex % _numColumns) * pageSpace.width(); qreal curY = (pageIndex / _numColumns) * pageSpace.height(); page->setPos(curX + (_maxPageSize.width() - box.width()) / 2.0, curY + (_maxPageSize.height() - box.height()) / 2.0); pageIndex++; } scene()->setSceneRect(0, 0, pageSpace.width() * _numColumns, pageSpace.height() * numRows); }