← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~mrqtros/ubuntu-docviewer-app/reboot-qsg-impress-support into lp:ubuntu-docviewer-app/reboot

 

Stefano Verzegnassi has proposed merging lp:~mrqtros/ubuntu-docviewer-app/reboot-qsg-impress-support into lp:ubuntu-docviewer-app/reboot with lp:~verzegnassi-stefano/ubuntu-docviewer-app/reboot-qsg-impress-support as a prerequisite.

Commit message:
RenderEngine - impress support.

Requested reviews:
  Ubuntu Document Viewer Developers (ubuntu-docviewer-dev)
  Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot): continuous-integration

For more details, see:
https://code.launchpad.net/~mrqtros/ubuntu-docviewer-app/reboot-qsg-impress-support/+merge/273984

RenderEngine - impress support.
-- 
Your team Ubuntu Document Viewer Developers is requested to review the proposed merge of lp:~mrqtros/ubuntu-docviewer-app/reboot-qsg-impress-support into lp:ubuntu-docviewer-app/reboot.
=== modified file 'po/com.ubuntu.docviewer.pot'
--- po/com.ubuntu.docviewer.pot	2015-10-09 14:08:34 +0000
+++ po/com.ubuntu.docviewer.pot	2015-10-09 14:08:35 +0000
@@ -11,7 +11,7 @@
 <<<<<<< TREE
 "POT-Creation-Date: 2015-10-01 01:55+0200\n"
 =======
-"POT-Creation-Date: 2015-09-23 18:40+0200\n"
+"POT-Creation-Date: 2015-10-08 23:48+0300\n"
 >>>>>>> MERGE-SOURCE
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
@@ -26,7 +26,7 @@
 <<<<<<< TREE
 #: /tmp/build-reboot-lok-goto-dialog-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
 =======
-#: /home/stefano/build-reboot-qsg-impress-support-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
+#: /home/qtros/dev/build-reboot-qsg-impress-support-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
 >>>>>>> MERGE-SOURCE
 msgid "Document Viewer"
 msgstr ""
@@ -434,7 +434,7 @@
 msgid "Zoom out"
 msgstr ""
 
-#: ../src/app/qml/loView/SlideControllerPanel.qml:52
+#: ../src/app/qml/loView/SlideControllerPanel.qml:62
 #, qt-format
 msgid "Slide %1 of %2"
 msgstr ""
@@ -519,7 +519,7 @@
 <<<<<<< TREE
 #: /tmp/build-reboot-lok-goto-dialog-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
 =======
-#: /home/stefano/build-reboot-qsg-impress-support-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
+#: /home/qtros/dev/build-reboot-qsg-impress-support-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
 >>>>>>> MERGE-SOURCE
 msgid "documents;viewer;pdf;reader;"
 msgstr ""

=== modified file 'src/app/qml/loView/LOViewPage.qml'
--- src/app/qml/loView/LOViewPage.qml	2015-10-09 14:08:34 +0000
+++ src/app/qml/loView/LOViewPage.qml	2015-10-09 14:08:35 +0000
@@ -99,8 +99,8 @@
                                     left: parent.left
                                 }
 
-                                model: LO.PartsModel { document: loPageContent.loDocument }
-                                visible: model && loDocument.documentType == LO.Document.PresentationDocument
+                                model: loView.partsModel //LO.PartsModel { document: loPageContent.loDocument }
+                                visible: /*loView.partsModel &&*/ loDocument.documentType == LO.Document.PresentationDocument
                                 width: visible ? units.gu(40) : 0
                             }
 
@@ -198,7 +198,7 @@
                     PartsView {
                         property bool belongsToNestedPage: true
                         anchors.fill: parent
-                        model: LO.PartsModel { document: loPageContent.loDocument }
+                        model: loView.partsModel //LO.PartsModel { document: loPageContent.loDocument }
                     }
                 }
             }

=== modified file 'src/app/qml/loView/PartsView.qml'
--- src/app/qml/loView/PartsView.qml	2015-10-09 14:08:34 +0000
+++ src/app/qml/loView/PartsView.qml	2015-10-09 14:08:35 +0000
@@ -28,7 +28,7 @@
 
     property bool expanded: true
 
-    currentIndex: view.model ? view.model.document.currentPart : -1
+    currentIndex: view.model ? loView.document.currentPart : -1
     highlightMoveDuration: UbuntuAnimation.SnapDuration
 
     delegate: ListItemWithActions {
@@ -37,7 +37,7 @@
         width: parent.width
         height: units.gu(16)
 
-        color: (view.model.document.currentPart === model.index) ? Theme.palette.selected.background
+        color: (loView.document.currentPart === model.index) ? Theme.palette.selected.background
                                                                  : "transparent"
 
         AbstractButton {
@@ -45,7 +45,7 @@
             anchors.fill: parent
 
             onClicked: {
-                view.model.document.currentPart = model.index
+                loView.document.currentPart = model.index
 
                 // Check if the view has been included in a nested page (e.g.
                 // bottomEdge). If so, close that page and return to the
@@ -65,22 +65,21 @@
                 fillMode: Image.PreserveAspectFit
                 // Do not store a cache of the thumbnail, so that we don't show
                 // thumbnails of a previously loaded document.
-                cache: false
-
-                source: "image://lok/part/" + model.index
+                cache: true // TODO PLAY WITH IT
+                source: model.thumbnail
             }
 
             Label {
                 Layout.fillWidth: true
                 wrapMode: Text.WordWrap
                 text: model.name
-                color: (view.model.document.currentPart === model.index) ? UbuntuColors.orange
+                color: (loView.document.currentPart === model.index) ? UbuntuColors.orange
                                                                          : Theme.palette.selected.backgroundText
             }
 
             Label {
                 text: model.index + 1
-                color: (view.model.document.currentPart === model.index) ? UbuntuColors.orange
+                color: (loView.document.currentPart === model.index) ? UbuntuColors.orange
                                                                          : Theme.palette.selected.backgroundText
             }
         }

=== modified file 'src/app/qml/ubuntu-docviewer-app.qml'
--- src/app/qml/ubuntu-docviewer-app.qml	2015-09-11 14:48:57 +0000
+++ src/app/qml/ubuntu-docviewer-app.qml	2015-10-09 14:08:35 +0000
@@ -37,7 +37,7 @@
     useDeprecatedToolbar: false   
     automaticOrientation: true
 
-    width: units.gu(50)
+    width: units.gu(150)
     height: units.gu(75)
 
     function openDocument(path)  {

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp	2015-10-09 14:08:35 +0000
@@ -110,6 +110,17 @@
     return m_docType;
 }
 
+int LODocument::documentPart() const
+{
+    return m_document->getPart();
+}
+
+void LODocument::setDocumentPart(int p)
+{
+    if (documentPart() != p)
+        m_document->setPart(p);
+}
+
 // Return the size of the document, in TWIPs
 QSize LODocument::documentSize() const
 {
@@ -130,8 +141,8 @@
     if (!m_document)
         return QImage();
 
-    if (m_currentPart != m_document->getPart())
-        m_document->setPart(m_currentPart);
+//    if (m_currentPart != m_document->getPart())
+//        m_document->setPart(m_currentPart);
 
     QImage result = QImage(canvasSize.width(), canvasSize.height(),  QImage::Format_RGB32);
 
@@ -154,7 +165,7 @@
     return result.rgbSwapped();
 }
 
-QImage LODocument::paintThumbnail(int part, qreal size)
+QImage LODocument::paintThumbnail(qreal size)
 {
     if (!m_document)
         return QImage();
@@ -164,14 +175,6 @@
     renderTimer.start();
 #endif
 
-    // This is used by LOPartsImageProvider to temporarily change the current part,
-    // in order to generate thumbnails.
-
-    // FIXME: Sometimes docviewer crashes at m_document->getPart() when a
-    // document is being loaded.
-    if (m_document->getPart() != part)
-        m_document->setPart(part);
-
     qreal tWidth = this->documentSize().width();
     qreal tHeight = this->documentSize().height();
 
@@ -189,10 +192,6 @@
     m_document->paintTile(result.bits(), resultSize.width(), resultSize.height(),
                           0, 0, tWidth, tHeight);
 
-    // Restore the active part used for tile rendering.
-    if (m_currentPart != part)
-        m_document->setPart(m_currentPart);
-
 #ifdef DEBUG_TILE_BENCHMARK
     qDebug() << "Time to render the thumbnail:" << renderTimer.elapsed() << "ms";
 #endif
@@ -213,7 +212,7 @@
     if (!m_document)
         return QString();
  
-    return QString::fromLatin1(m_document->getPartName(index));
+    return QString::fromUtf8(m_document->getPartName(index));
 }
  
 /* Export the file in a given format:

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.h'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.h	2015-10-09 14:08:35 +0000
@@ -30,11 +30,12 @@
     Q_OBJECT
     Q_DISABLE_COPY(LODocument)
 
-    Q_PROPERTY(QString      path         READ path         WRITE setPath        NOTIFY pathChanged)
-    Q_PROPERTY(int          currentPart  READ currentPart  WRITE setCurrentPart NOTIFY currentPartChanged)
+    Q_PROPERTY(QString      path         READ path         WRITE setPath         NOTIFY pathChanged)
+    Q_PROPERTY(int          currentPart  READ currentPart  WRITE setCurrentPart  NOTIFY currentPartChanged)
     // Declare partsCount as constant at the moment, since LOK-plugin is just a viewer for now.
     Q_PROPERTY(int          partsCount   READ partsCount                                                    CONSTANT)
-    Q_PROPERTY(DocumentType documentType READ documentType                      NOTIFY documentTypeChanged)
+    Q_PROPERTY(int          documentPart READ documentPart WRITE setDocumentPart NOTIFY documentPartChanged)
+    Q_PROPERTY(DocumentType documentType READ documentType                       NOTIFY documentTypeChanged)
     Q_ENUMS(DocumentType)
 
 public:
@@ -57,10 +58,13 @@
 
     DocumentType documentType() const;
 
+    int documentPart() const;
+    void setDocumentPart(int p);
+
     QSize documentSize() const;
 
     QImage paintTile(const QSize& canvasSize, const QRect& tileSize, const qreal& zoom = 1.0);
-    QImage paintThumbnail(int part, qreal size);
+    QImage paintThumbnail(qreal size);
 
     int partsCount();
     QString getPartName(int index) const;
@@ -72,6 +76,7 @@
     void pathChanged();
     void currentPartChanged();
     void documentTypeChanged();
+    void documentPartChanged();
 
 private:
     QString m_path;

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.cpp	2015-10-09 14:08:35 +0000
@@ -18,9 +18,9 @@
 #include "lodocument.h"
 #include "renderengine.h"
 
-LOPartsImageProvider::LOPartsImageProvider(LODocument *document)
-    : QQuickImageProvider(QQuickImageProvider::Image, QQuickImageProvider::ForceAsynchronousImageLoading)
-    , m_document(document)
+LOPartsImageProvider::LOPartsImageProvider(const QSharedPointer<LODocument>& d)
+    : QQuickImageProvider(QQuickImageProvider::Image),
+      m_document(d)
 { }
 
 QImage LOPartsImageProvider::requestImage(const QString & id, QSize * size, const QSize & requestedSize)
@@ -33,18 +33,22 @@
             m_document->documentType() != LODocument::PresentationDocument)
         return QImage();
 
-    // Wait for any in-progress rendering to be completed
-    while (RenderEngine::instance()->activeTaskCount() != 0) { }
-
-    // Lock the render engine
-    RenderEngine::instance()->setEnabled(false);
-
-    // Render the part to QImage
+    // Get info from "id".
     int partNumber = id.section("/", 1, 1).toInt();
-    QImage result = m_document->paintThumbnail(partNumber, 256.0);
-
-    // Unlock the render engine
-    RenderEngine::instance()->setEnabled(true);
-
-    return result;
+    int itemId = id.section("/", 2, 2).toInt();
+
+    // Once rendered images can be found in hash.
+    if (m_images.contains(itemId))
+        return m_images[itemId];
+
+    const int defaultSize = 256;
+
+    RenderEngine::instance()->enqueueTask(m_document, partNumber, defaultSize, itemId);
+
+    // Return default image (empty).
+    static QImage defaultImage;
+    if (defaultImage.isNull())
+        defaultImage = QImage(defaultSize, defaultSize, QImage::Format_ARGB32);
+
+    return defaultImage;
 }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.h'
--- src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lopartsimageprovider.h	2015-10-09 14:08:35 +0000
@@ -19,17 +19,22 @@
 #define LOPARTSIMAGEPROVIDER_H
 
 #include <QQuickImageProvider>
+#include <QSharedPointer>
+#include <QHash>
+#include <QDebug>
 
 class LODocument;
 
 class LOPartsImageProvider : public QQuickImageProvider
 {
 public:
-    LOPartsImageProvider(LODocument *document);
+    LOPartsImageProvider(const QSharedPointer<LODocument>& d);
     QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize);
 
+    QHash<int, QImage> m_images;
+
 private:
-    LODocument *m_document;
+    QSharedPointer<LODocument> m_document;
 };
 
 #endif // LOPARTSIMAGEPROVIDER_H

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.cpp	2015-10-09 14:08:35 +0000
@@ -23,28 +23,13 @@
 #include <QQmlEngine>
 #include <QDebug>
 
-LOPartsModel::LOPartsModel(QAbstractListModel *parent):
+
+
+LOPartsModel::LOPartsModel(const QSharedPointer<LODocument>& document, QAbstractListModel *parent):
     QAbstractListModel(parent)
 {   
-    connect(this, SIGNAL(documentChanged()), this, SLOT(fillModel()));
-}
-
-void LOPartsModel::setDocument(LODocument *document)
-{
-    if (m_document == document)
-        return;
-
     m_document = document;
-    Q_EMIT documentChanged();
-
-    QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
-    QString imageProviderName = "lok";
-
-    if (engine->imageProvider(imageProviderName))
-        engine->removeImageProvider(imageProviderName);
-
-    engine->addImageProvider(imageProviderName, new LOPartsImageProvider(m_document));
-
+    fillModel();
 }
 
 QHash<int, QByteArray> LOPartsModel::roleNames() const
@@ -52,6 +37,8 @@
     QHash<int, QByteArray> roles;
     roles[IndexRole] = "index";
     roles[NameRole] = "name";
+    roles[IdRole] = "id";
+    roles[ThumbnailRole] = "thumbnail";
 
     return roles;
 }
@@ -74,6 +61,10 @@
         return part.index;
     case NameRole:
         return part.name;
+    case IdRole:
+        return part.id;
+    case ThumbnailRole:
+        return part.thumbnail;
 
     default:
         return 0;
@@ -92,34 +83,48 @@
     QVariantMap map;
     map["name"] = part.name;
     map["index"] = part.index;
+    map["id"] = part.id;
+    map["thumbnail"] = part.thumbnail;
 
     return map;
 }
 
+void LOPartsModel::notifyAboutChanges(int id)
+{
+    for (int i = 0; i < m_entries.size(); i++)
+        if (m_entries[i].id == id) {
+            m_entries[i].thumbnail += "/cached";
+            Q_EMIT dataChanged(createIndex(i, 0), createIndex(i + 1, 0));
+            break;
+        }
+}
+
 void LOPartsModel::fillModel() {
-    if (m_document) {
-        if (!m_entries.isEmpty()) {
-            beginRemoveRows(QModelIndex(), 0, rowCount());
-            m_entries.clear();
-            endRemoveRows();
-        }
-
-        int partsCount = m_document->partsCount();
-
-        for (int i = 0; i < partsCount; i++) {
-            LOPartEntry part;
-            part.index = i;
-            part.name = m_document->getPartName(i);
-
-            beginRemoveRows(QModelIndex(), rowCount(), rowCount());
-            m_entries.append(part);
-            endRemoveRows();
-        }
-
-        Q_EMIT countChanged();
-    }    
+    if (!m_document)
+        return;
+
+    if (!m_entries.isEmpty()) {
+        beginRemoveRows(QModelIndex(), 0, rowCount());
+        m_entries.clear();
+        endRemoveRows();
+    }
+
+    int partsCount = m_document->partsCount();
+    beginInsertColumns(QModelIndex(), 0, qMax(partsCount - 1, 0));
+    for (int i = 0; i < partsCount; i++) {
+        LOPartEntry part;
+
+        part.index = i;
+        part.name = m_document->getPartName(i);
+        part.id = RenderEngine::getNextId();
+        part.thumbnail = QString("image://lok/part/%1/%2").arg(QString::number(part.index)).arg(QString::number(part.id));
+
+        m_entries.append(part);
+    }
+    endInsertColumns();
+
+    Q_EMIT countChanged();
 }
 
 LOPartsModel::~LOPartsModel()
-{
-}
+{ }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.h'
--- src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lopartsmodel.h	2015-10-09 14:08:35 +0000
@@ -19,34 +19,49 @@
 #define LOPARTSMODEL_H
 
 #include <QAbstractListModel>
+#include <QImage>
+#include <QHash>
+#include <QSharedPointer>
+
+#include "renderengine.h"
 
 class LODocument;
 
 class LOPartEntry
 {
 public:
+    LOPartEntry():
+        index(0)
+    {
+        id = RenderEngine::getNextId();
+    }
+
+    int id;
     QString name;
-    int index = 0;
+    int index;
+    QString thumbnail;
 };
 
 class LOPartsModel : public QAbstractListModel
 {
     Q_OBJECT
     Q_DISABLE_COPY(LOPartsModel)
-    Q_PROPERTY(LODocument* document READ document WRITE setDocument NOTIFY documentChanged)
+    // Q_PROPERTY(LODocument* document READ document WRITE setDocument NOTIFY documentChanged)
     Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
 
 public:
     enum Roles {
-        NameRole,
-        IndexRole
+        NameRole = Qt::UserRole + 1,
+        IndexRole,
+        IdRole,
+        ThumbnailRole
     };
 
-    explicit LOPartsModel(QAbstractListModel *parent = 0);
+    explicit LOPartsModel(const QSharedPointer<LODocument>& document, QAbstractListModel *parent = 0);
     ~LOPartsModel();
 
-    LODocument* document() { return m_document; }
-    void setDocument(LODocument* document);
+    // LODocument* document() { return m_document; }
+    //void setDocument(LODocument* document);
 
     QHash<int, QByteArray> roleNames() const;
 
@@ -55,15 +70,17 @@
 
     Q_INVOKABLE QVariantMap get(int index) const;
 
+    void notifyAboutChanges(int id);
+
 Q_SIGNALS:
-    void documentChanged();
+    // void documentChanged();
     void countChanged();
 
 private slots:
     void fillModel();
 
 private:
-    LODocument* m_document;
+    QSharedPointer<LODocument> m_document;
     QList<LOPartEntry> m_entries;
 };
 

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.cpp	2015-10-09 14:08:35 +0000
@@ -36,6 +36,7 @@
     : QQuickItem(parent)
     , m_parentFlickable(nullptr)
     , m_document(nullptr)
+    , m_partsModel(nullptr)
     , m_zoomFactor(1.0)
     , m_cacheBuffer(TILE_SIZE * 3)
     , m_visibleArea(0, 0, 0, 0)
@@ -48,7 +49,11 @@
     connect(this, SIGNAL(parentFlickableChanged()), this, SLOT(updateVisibleRect()));
     connect(this, SIGNAL(cacheBufferChanged()), this, SLOT(updateVisibleRect()));
     connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateVisibleRect()));
-    connect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)), this, SLOT(renderResultReceived(int,QImage)));
+
+    connect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)),
+            this, SLOT(slotTileRenderFinished(int,QImage)));
+    connect(RenderEngine::instance(), SIGNAL(thumbnailRenderFinished(int,QImage)),
+            this, SLOT(slotThumbnailRenderFinished(int,QImage)));
 }
 
 // Returns the parent QML Flickable
@@ -79,12 +84,25 @@
 
 void LOView::initializeDocument(const QString &path)
 {
-    if (m_document.data())
-        m_document.data()->disconnect(this);
+    if (m_document)
+        m_document->disconnect(this);
 
     m_document = QSharedPointer<LODocument>(new LODocument());
     m_document->setPath(path);
 
+    // TODO MOVE
+    m_partsModel = new LOPartsModel(m_document);
+    Q_EMIT partsModelChanged();
+
+    // --------------------------------------------------
+    QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
+    if (engine->imageProvider("lok"))
+        engine->removeImageProvider("lok");
+
+    m_imageProvider = new LOPartsImageProvider(m_document);
+    engine->addImageProvider("lok", m_imageProvider);
+    // --------------------------------------------------
+
     connect(m_document.data(), SIGNAL(currentPartChanged()), this, SLOT(invalidateAllTiles()));
 
     Q_EMIT documentChanged();
@@ -96,6 +114,11 @@
     return m_document.data();
 }
 
+LOPartsModel *LOView::partsModel() const
+{
+    return m_partsModel;
+}
+
 qreal LOView::zoomFactor() const
 {
     return m_zoomFactor;
@@ -314,9 +337,9 @@
         qDebug() << "Creating tile indexed as" << index;
 #endif
 
-        auto tile = new SGTileItem(rect, m_zoomFactor, this);
+        auto tile = new SGTileItem(rect, m_zoomFactor, RenderEngine::getNextId(), this);
         m_tiles.insert(index, tile);
-        RenderEngine::instance()->enqueueTask(m_document, rect, m_zoomFactor, tile->id());
+        RenderEngine::instance()->enqueueTask(m_document, m_document->currentPart(), rect, m_zoomFactor, tile->id());
     }
 #ifdef DEBUG_VERBOSE
     else {
@@ -331,7 +354,7 @@
         m_updateTimer.start(20);
 }
 
-void LOView::renderResultReceived(int id, QImage img)
+void LOView::slotTileRenderFinished(int id, QImage img)
 {
     for (auto i = m_tiles.begin(); i != m_tiles.end(); ++i) {
         SGTileItem* sgtile = i.value();
@@ -342,6 +365,13 @@
     }
 }
 
+void LOView::slotThumbnailRenderFinished(int id, QImage img)
+{
+    if (!m_imageProvider->m_images.contains(id))
+        m_imageProvider->m_images.insert(id, img);
+    m_partsModel->notifyAboutChanges(id);
+}
+
 void LOView::clearView()
 {
     for (auto i = m_tiles.begin(); i != m_tiles.end(); ++i)
@@ -352,7 +382,12 @@
 
 LOView::~LOView()
 {
-    disconnect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)), this, SLOT(renderResultReceived(int,QImage)));
+    delete m_partsModel;
+
+    disconnect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)),
+               this, SLOT(slotTileRenderFinished(int,QImage)));
+    disconnect(RenderEngine::instance(), SIGNAL(thumbnailRenderFinished(int,QImage)),
+               this, SLOT(slotThumbnailRenderFinished(int,QImage)));
 
     // Remove all tasks from rendering queue.
     for (auto i = m_tiles.begin(); i != m_tiles.end(); ++i)

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.h'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.h	2015-10-09 14:08:35 +0000
@@ -21,8 +21,12 @@
 #include <QQuickItem>
 #include <QTimer>
 #include <QSharedPointer>
+#include <QQmlContext>
+#include <QQmlEngine>
 
 #include "renderengine.h"
+#include "lopartsmodel.h"
+#include "lopartsimageprovider.h"
 
 class LODocument;
 class SGTileItem;
@@ -31,11 +35,12 @@
 {
     Q_OBJECT
     Q_ENUMS(ZoomMode)
-    Q_PROPERTY(QQuickItem* parentFlickable READ parentFlickable WRITE setParentFlickable NOTIFY parentFlickableChanged)
-    Q_PROPERTY(LODocument* document        READ document        /*WRITE setDocument*/    NOTIFY documentChanged)
-    Q_PROPERTY(qreal       zoomFactor      READ zoomFactor      WRITE setZoomFactor      NOTIFY zoomFactorChanged)
-    Q_PROPERTY(ZoomMode    zoomMode        READ zoomMode                                 NOTIFY zoomModeChanged)
-    Q_PROPERTY(int         cacheBuffer     READ cacheBuffer     WRITE setCacheBuffer     NOTIFY cacheBufferChanged)
+    Q_PROPERTY(QQuickItem*   parentFlickable READ parentFlickable WRITE setParentFlickable NOTIFY parentFlickableChanged)
+    Q_PROPERTY(LODocument*   document        READ document        /*WRITE setDocument*/    NOTIFY documentChanged)
+    Q_PROPERTY(LOPartsModel* partsModel      READ partsModel                               NOTIFY partsModelChanged)
+    Q_PROPERTY(qreal         zoomFactor      READ zoomFactor      WRITE setZoomFactor      NOTIFY zoomFactorChanged)
+    Q_PROPERTY(ZoomMode      zoomMode        READ zoomMode                                 NOTIFY zoomModeChanged)
+    Q_PROPERTY(int           cacheBuffer     READ cacheBuffer     WRITE setCacheBuffer     NOTIFY cacheBufferChanged)
 
 public:
     LOView(QQuickItem *parent = 0);
@@ -52,6 +57,7 @@
     Q_INVOKABLE void initializeDocument(const QString& path);
 
     LODocument* document() const;
+    LOPartsModel* partsModel() const;
 
     qreal       zoomFactor() const;
     void        setZoomFactor(const qreal zoom);
@@ -66,6 +72,7 @@
 Q_SIGNALS:
     void parentFlickableChanged();
     void documentChanged();
+    void partsModelChanged();
     void zoomFactorChanged();
     void zoomModeChanged();
     void cacheBufferChanged();
@@ -75,12 +82,16 @@
     void updateVisibleRect();
     void scheduleVisibleRectUpdate();
     void invalidateAllTiles();
-    void renderResultReceived(int id, QImage img);
+
+    void slotTileRenderFinished(int id, QImage img);
+    void slotThumbnailRenderFinished(int id, QImage img);
 
 private:
 
     QQuickItem*                 m_parentFlickable;
     QSharedPointer<LODocument>  m_document;
+    LOPartsModel*               m_partsModel; // TODO MB move to document.
+    LOPartsImageProvider*       m_imageProvider; // The QQmlEngine takes ownership of provider.
 
     qreal                       m_zoomFactor;
     ZoomMode                    m_zoomMode;

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/plugin.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/plugin.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/plugin.cpp	2015-10-09 14:08:35 +0000
@@ -30,7 +30,7 @@
     //@uri DocumentViewer.LibreOffice
     qmlRegisterType<LODocument>(uri, 1, 0, "Document");
     qmlRegisterType<LOView>(uri, 1, 0, "View");
-    qmlRegisterType<LOPartsModel>(uri, 1, 0, "PartsModel");
+    qmlRegisterUncreatableType<LOPartsModel>(uri, 1, 0, "PartsModel", "You shouldn't create LOPartsModel in QML");
 }
 
 void LOPlugin::initializeEngine(QQmlEngine *engine, const char *uri)

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml'
--- src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml	2015-10-09 14:08:35 +0000
@@ -23,8 +23,9 @@
     property alias document:    view.document
     property alias zoomFactor:  view.zoomFactor
     property alias cacheBuffer: view.cacheBuffer
-
+    property alias partsModel: view.partsModel
     property alias zoomMode:    view.zoomMode
+
     property string documentPath: ""
 
     function adjustZoomToWidth()

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp	2015-10-09 14:08:35 +0000
@@ -7,7 +7,8 @@
 RenderEngine::RenderEngine():
     QObject(nullptr),
     m_activeTaskCount(0),
-    m_enabled(true)
+    m_enabled(true),
+    m_lastPart(-1)
 {
     int itc = QThread::idealThreadCount();
     m_idealThreadCount = itc == -1 ? DefaultIdealThreadCount : itc;
@@ -15,11 +16,20 @@
     connect(this, SIGNAL(enabledChanged()), this, SLOT(doNextTask()));
 }
 
-void RenderEngine::enqueueTask(const QSharedPointer<LODocument>& doc, const QRect& area, const qreal &zoom, int id)
-{
-    Q_ASSERT(doc != nullptr);
-
-    m_queue.enqueue(EngineTask(doc, area, zoom, id));
+void RenderEngine::enqueueTask(const QSharedPointer<LODocument>& doc, int part, const QRect& area, const qreal &zoom, int id)
+{
+    Q_ASSERT(doc != nullptr);
+
+    m_queue.enqueue(EngineTask(doc, part, area, zoom, id));
+
+    doNextTask();
+}
+
+void RenderEngine::enqueueTask(const QSharedPointer<LODocument> &doc, int part, qreal size, int id)
+{
+    Q_ASSERT(doc != nullptr);
+
+    m_queue.enqueue(EngineTask(doc, part, size, id));
 
     doNextTask();
 }
@@ -33,10 +43,12 @@
         }
 }
 
-void RenderEngine::internalRenderCallback(int id, QImage img)
+void RenderEngine::internalRenderCallback(int id, QImage img, bool isThumbnail)
 {
     m_activeTaskCount--;
-    Q_EMIT renderFinished(id, img);
+    if (isThumbnail)
+        Q_EMIT thumbnailRenderFinished(id, img);
+    else Q_EMIT renderFinished(id, img);
     doNextTask();
 }
 
@@ -46,14 +58,36 @@
     qDebug() << " ---- doNextTask" << m_activeTaskCount << m_queue.count();
 #endif
 
+    // Check for too much threads or empty queue.
     if (m_activeTaskCount >= m_idealThreadCount || !m_queue.count() || !m_enabled)
         return;
 
+    // We should avoid different part rendering in the same time.
+    if (m_activeTaskCount && m_queue.head().part != m_lastPart)
+        return;
+
     m_activeTaskCount++;
-    auto task = m_queue.dequeue();
+    EngineTask task = m_queue.dequeue();
+
+    // Set correct part.
+    m_lastPart = task.part;
+    task.document->setDocumentPart(m_lastPart);
 
     QtConcurrent::run( [=] {
-        QImage img = task.document->paintTile(task.area.size(), task.area, task.zoom);
-        QMetaObject::invokeMethod(this, "internalRenderCallback", Q_ARG(int, task.id), Q_ARG(QImage, img));
+        if (task.isThumbnail) {
+            QImage img = task.document->paintThumbnail(task.size);
+            QMetaObject::invokeMethod(this, "internalRenderCallback",
+                                      Q_ARG(int, task.id), Q_ARG(QImage, img), Q_ARG(bool, task.isThumbnail));
+        } else {
+            QImage img = task.document->paintTile(task.area.size(), task.area, task.zoom);
+            QMetaObject::invokeMethod(this, "internalRenderCallback",
+                                      Q_ARG(int, task.id), Q_ARG(QImage, img), Q_ARG(bool, task.isThumbnail));
+        }
+//        QImage img = task.isThumbnail ?
+//                    //task.document->paintThumbnail(task.size) :
+//                    task.document->paintTile(task.area.size(), task.area, task.zoom) :
+//                    task.document->paintTile(task.area.size(), task.area, task.zoom);
+//        QMetaObject::invokeMethod(this, "internalRenderCallback",
+//                                  Q_ARG(int, task.id), Q_ARG(QImage, img), Q_ARG(bool, task.isThumbnail));
     });
 }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/renderengine.h'
--- src/plugin/libreofficetoolkit-qml-plugin/renderengine.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/renderengine.h	2015-10-09 14:08:35 +0000
@@ -10,19 +10,42 @@
 
 #include "lodocument.h"
 
+// TODO replace with class.
+
+
+// TODO Need more OOP here.
 struct EngineTask
 {
     int id;
+    int part;
+    QSharedPointer<LODocument> document;
+    // Used in thumbnail rendering.
+    qreal size;
+    // Used in tile rendering.
     QRect area;
     qreal zoom;
-    QSharedPointer<LODocument> document;
-
+    // Internal.
+    bool isThumbnail;
 public:
-    EngineTask(const QSharedPointer<LODocument>& d, const QRect& a, const qreal& z, int i):
-    id(i),
-    area(a),
-    zoom(z),
-    document(d)
+
+    EngineTask(const QSharedPointer<LODocument>& d, int p, const QRect& a, const qreal& z, int i):
+        id(i),
+        part(p),
+        document(d),
+        size(0),
+        area(a),
+        zoom(z),
+        isThumbnail(false)
+    { }
+
+    EngineTask(const QSharedPointer<LODocument>& d, int p, qreal s, int i):
+        id(i),
+        part(p),
+        document(d),
+        size(s),
+        area(),
+        zoom(0),
+        isThumbnail(true)
     { }
 };
 
@@ -37,7 +60,8 @@
     const int DefaultIdealThreadCount = 2;
 
 public:
-    void enqueueTask(const QSharedPointer<LODocument>& doc, const QRect& area, const qreal& zoom, int id);
+    void enqueueTask(const QSharedPointer<LODocument>& doc, int part, const QRect& area, const qreal& zoom, int id);
+    void enqueueTask(const QSharedPointer<LODocument>& doc, int part, qreal size, int id);
     void dequeueTask(int id);
 
     static RenderEngine* instance() {
@@ -46,23 +70,18 @@
         return s_instance;
     }
 
-    int activeTaskCount() { return m_activeTaskCount; }
-
-    bool enabled() { return m_enabled.loadAcquire(); }
-    void setEnabled(bool enabled) {
-        if (m_enabled.loadAcquire() == enabled)
-            return;
-
-        m_enabled.storeRelease(enabled);
-        Q_EMIT enabledChanged();
+    static int getNextId() {
+        static int idCounter = 0xDEAD0000;
+        return idCounter++;
     }
 
 Q_SIGNALS:
     void renderFinished(int id, QImage img);
+    void thumbnailRenderFinished(int id, QImage img);
     void enabledChanged();
 
 private:
-    Q_INVOKABLE void internalRenderCallback(int id, QImage img);
+    Q_INVOKABLE void internalRenderCallback(int id, QImage img, bool isThumbnail);
 
 private slots:
     void doNextTask();
@@ -71,6 +90,7 @@
     QQueue<EngineTask> m_queue;
     int m_activeTaskCount;
     int m_idealThreadCount;
+    int m_lastPart;
 
     QAtomicInt m_enabled;
 };

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp	2015-10-09 14:08:35 +0000
@@ -8,13 +8,13 @@
 #include <QSGFlatColorMaterial>
 #endif
 
-int SGTileItem::s_idCounter = 0xDEAD0000;
+//int SGTileItem::s_idCounter = 0xDEAD0000;
 
-SGTileItem::SGTileItem(const QRect& area, const qreal &zoom, QQuickItem *parent)
+SGTileItem::SGTileItem(const QRect& area, qreal zoom, int id, QQuickItem *parent)
     : QQuickItem(parent)
     , m_area(area)
     , m_zoomFactor(zoom)
-    , m_id (s_idCounter++)
+    , m_id (id)
 {
     setFlag(ItemHasContents, true);
 }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h'
--- src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h	2015-10-09 14:08:34 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h	2015-10-09 14:08:35 +0000
@@ -14,7 +14,7 @@
 {
     Q_OBJECT
 public:
-    SGTileItem(const QRect& area, const qreal &zoom = 1.0, QQuickItem *parent = 0);
+    SGTileItem(const QRect& area, qreal zoom, int id, QQuickItem *parent = 0);
     ~SGTileItem();
 
     inline const QRect& area() { return m_area; }
@@ -44,7 +44,7 @@
     QImage m_data;
     int m_id;
 
-    static int s_idCounter;
+    // static int s_idCounter;
 };
 
 #endif // SGTILEITEM_H


Follow ups