diff --git a/components/Document.cpp b/components/Document.cpp index 6546a0a8076..21acb66afea 100644 --- a/components/Document.cpp +++ b/components/Document.cpp @@ -1,273 +1,273 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "Document.h" #include #include #include #include #include #include #include #include #include #include #include "impl/TextDocumentImpl.h" #include "impl/SpreadsheetImpl.h" #include "impl/PresentationImpl.h" using namespace Calligra::Components; class Document::Private { public: Private(Document* qq) : q{qq}, impl{nullptr}, status{DocumentStatus::Unloaded}, textEditor{nullptr} { } void updateImpl(); Document* q; QUrl source; DocumentImpl* impl; DocumentStatus::Status status; KoTextEditor* textEditor; }; Document::Document(QObject* parent) : QObject{parent}, d{new Private{this}} { } Document::~Document() { delete d; } QUrl Document::source() const { return d->source; } void Document::setSource(const QUrl& value) { if(value != d->source) { d->source = value; emit sourceChanged(); d->status = DocumentStatus::Loading; emit statusChanged(); d->updateImpl(); emit documentTypeChanged(); if(d->impl) { if(d->impl->load(d->source)) { d->status = DocumentStatus::Loaded; connect(d->impl->canvasController()->canvas()->shapeManager(), SIGNAL(selectionChanged()), SIGNAL(textEditorChanged())); } else { d->status = DocumentStatus::Failed; } } else { d->status = DocumentStatus::Unloaded; } emit indexCountChanged(); emit statusChanged(); } } DocumentType::Type Document::documentType() const { if(d->impl) { return d->impl->documentType(); } return DocumentType::Unknown; } DocumentStatus::Status Document::status() const { return d->status; } QSize Document::documentSize() const { if(d->impl) { return d->impl->documentSize(); } return QSize{}; } int Document::currentIndex() const { if(d->impl) { return d->impl->currentIndex(); } return -1; } void Document::setCurrentIndex(int newValue) { if(d->impl) { d->impl->setCurrentIndex(newValue); } } int Document::indexCount() const { if(d->impl) { return d->impl->indexCount(); } return 0; } KoFindBase* Document::finder() const { if(d->impl) { return d->impl->finder(); } return nullptr; } QGraphicsWidget* Document::canvas() const { if(d->impl) { return d->impl->canvas(); } return nullptr; } KoCanvasController* Document::canvasController() const { if(d->impl) { return d->impl->canvasController(); } return nullptr; } KoZoomController* Document::zoomController() const { if(d->impl) { return d->impl->zoomController(); } return nullptr; } QObject* Document::part() const { - return nullptr; + return d->impl->part(); } QObject* Document::document() const { return koDocument(); } KoDocument* Document::koDocument() const { if(d->impl) { return d->impl->koDocument(); } return nullptr; } QUrl Document::urlAtPoint(const QPoint& point) { if(d->impl) return d->impl->urlAtPoint(point); return QUrl(); } QObject * Document::textEditor() { if (d->impl && d->impl->canvasController()) { if (d->textEditor) { disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged())); } d->textEditor = KoTextEditor::getTextEditorFromCanvas(d->impl->canvasController()->canvas()); if (d->textEditor) { disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged())); } // emit selectionChanged(); return d->textEditor; } return 0; } void Document::deselectEverything() { KoTextEditor* editor = KoTextEditor::getTextEditorFromCanvas(d->impl->canvasController()->canvas()); if (editor) { editor->clearSelection(); } d->impl->canvasController()->canvas()->shapeManager()->selection()->deselectAll(); emit requestViewUpdate(); } void Document::Private::updateImpl() { if(impl) { delete impl; } if(!source.isEmpty()) { auto type = Global::documentType(source); switch(type) { case DocumentType::TextDocument: impl = new TextDocumentImpl{q}; break; case DocumentType::Spreadsheet: impl = new SpreadsheetImpl{q}; break; case DocumentType::Presentation: impl = new PresentationImpl{q}; break; default: impl = nullptr; break; } } else { impl = nullptr; } if(impl) { connect(impl, &DocumentImpl::documentSizeChanged, q, &Document::documentSizeChanged); connect(impl, &DocumentImpl::currentIndexChanged, q, &Document::currentIndexChanged); connect(impl, &DocumentImpl::requestViewUpdate, q, &Document::requestViewUpdate); } emit q->documentChanged(); } #include "moc_Document.cpp" diff --git a/components/impl/DocumentImpl.h b/components/impl/DocumentImpl.h index 007ff65852b..cddc31c07c5 100644 --- a/components/impl/DocumentImpl.h +++ b/components/impl/DocumentImpl.h @@ -1,91 +1,92 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef CALLIGRA_COMPONENTS_DOCUMENTIMPL_H #define CALLIGRA_COMPONENTS_DOCUMENTIMPL_H #include #include "Global.h" class KoDocument; class QGraphicsWidget; class KoFindBase; class KoCanvasController; class KoZoomController; class KoDocument; class KoCanvasBase; namespace Calligra { namespace Components { /** * \brief Defines an interface for handling specific documents in Document. * */ class DocumentImpl : public QObject { Q_OBJECT public: explicit DocumentImpl(QObject* parent = 0); virtual ~DocumentImpl(); virtual bool load(const QUrl& url) = 0; virtual int currentIndex() = 0; virtual void setCurrentIndex(int newValue) = 0; virtual int indexCount() const = 0; virtual QUrl urlAtPoint(QPoint point) = 0; DocumentType::Type documentType() const; KoFindBase* finder() const; QGraphicsWidget* canvas() const; KoCanvasController* canvasController() const; KoZoomController* zoomController() const; QSize documentSize() const; KoDocument* koDocument() const; + virtual QObject* part() const = 0; Q_SIGNALS: void documentSizeChanged(); void currentIndexChanged(); void requestViewUpdate(); protected: void setDocumentType(DocumentType::Type type); void setKoDocument(KoDocument* document); void setCanvas(QGraphicsWidget* newCanvas); void setFinder(KoFindBase* newFinder); void createAndSetCanvasController(KoCanvasBase* canvas); void createAndSetZoomController(KoCanvasBase* canvas); protected Q_SLOTS: void setDocumentSize(const QSize& size); private: class Private; Private* const d; }; } // Namespace Components } // Namespace Calligra #endif // CALLIGRA_COMPONENTS_DOCUMENTIMPL_H diff --git a/components/impl/SpreadsheetImpl.cpp b/components/impl/SpreadsheetImpl.cpp index c3163243b41..fe6373b8f73 100644 --- a/components/impl/SpreadsheetImpl.cpp +++ b/components/impl/SpreadsheetImpl.cpp @@ -1,240 +1,245 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "SpreadsheetImpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Calligra::Components; class SpreadsheetImpl::Private { public: Private() : part{nullptr}, document{nullptr} { } Calligra::Sheets::Part* part; Calligra::Sheets::Doc* document; Calligra::Sheets::CanvasItem* canvas; int currentSheet; QList< QPair< QRectF, QUrl > > links; QList deepShapeFind(QList shapes) { QList allShapes; foreach(KoShape* shape, shapes) { allShapes.append(shape); KoShapeContainer *container = dynamic_cast(shape); if(container) { allShapes.append(deepShapeFind(container->shapes())); } } return allShapes; } void updateLinkTargets() { links.clear(); if(!canvas) return; foreach(const KoShape* shape, canvas->activeSheet()->shapes()) { if(!shape->hyperLink().isEmpty()) { QRectF rect = shape->boundingRect(); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } links.append(QPair(rect, QUrl(shape->hyperLink()))); } } QList texts; KoFindText::findTextInShapes(canvas->activeSheet()->shapes(), texts); QList allShapes = deepShapeFind(canvas->activeSheet()->shapes()); foreach(QTextDocument* text, texts) { QTextBlock block = text->rootFrame()->firstCursorPosition().block(); for (; block.isValid(); block = block.next()) { block.begin(); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QTextCharFormat format = fragment.charFormat(); if(format.isAnchor()) { // This is an anchor, store target and position... QRectF rect = getFragmentPosition(block, fragment); foreach(KoShape* shape, allShapes) { KoTextShapeData *shapeData = dynamic_cast(shape->userData()); if (!shapeData) continue; if(shapeData->document() == text) { rect.translate(shape->position()); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } break; } } links.append(QPair(canvas->viewConverter()->documentToView(rect), QUrl(format.anchorHref()))); } } } } } } QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment) { // TODO this only produces a position for the first part, if the link spans more than one line... // Need to sort that somehow, unfortunately probably by slapping this code into the above function. // For now leave it like this, more important things are needed. QTextLayout* layout = block.layout(); QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); if(!line.isValid()) { // fragment has no valid position and consequently no line... return QRectF(); } qreal top = line.position().y() + (line.height() / 2); qreal bottom = top + line.height(); qreal left = line.cursorToX(fragment.position() - block.position()); qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); QRectF fragmentPosition(QPointF(left, top), QPointF(right, bottom)); return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); } static const float wiggleFactor; }; const float Calligra::Components::SpreadsheetImpl::Private::wiggleFactor{ 4.f }; SpreadsheetImpl::SpreadsheetImpl(QObject* parent) : DocumentImpl{parent}, d{new Private} { setDocumentType(DocumentType::Spreadsheet); } SpreadsheetImpl::~SpreadsheetImpl() { delete d; } bool SpreadsheetImpl::load(const QUrl& url) { if(d->part) { delete d->part; delete d->document; } d->part = new Calligra::Sheets::Part{this}; d->document = new Calligra::Sheets::Doc{d->part}; setKoDocument(d->document); d->part->setDocument(d->document); bool retval = d->document->openUrl(url); d->canvas = static_cast(d->part->canvasItem(d->document)); createAndSetCanvasController(d->canvas); createAndSetZoomController(d->canvas); connect(d->canvas, &Calligra::Sheets::CanvasItem::documentSizeChanged, this, &SpreadsheetImpl::updateDocumentSize); auto sheet = d->document->map()->sheet(0); if(sheet) { updateDocumentSize(sheet->documentSize().toSize()); } setCanvas(d->canvas); d->updateLinkTargets(); return retval; } int SpreadsheetImpl::currentIndex() { if (d->document && d->document->map() && d->canvas->activeSheet()) { return d->document->map()->indexOf(d->canvas->activeSheet()); } else { return -1; } } void SpreadsheetImpl::setCurrentIndex(int newValue) { if(newValue != currentIndex()) { d->canvas->setActiveSheet(d->document->map()->sheet(newValue)); d->updateLinkTargets(); emit currentIndexChanged(); } } void SpreadsheetImpl::updateDocumentSize(const QSize& size) { QRectF activeRect = d->canvas->viewConverter()->documentToView(d->canvas->activeSheet()->cellCoordinatesToDocument(d->canvas->activeSheet()->usedArea(true))); zoomController()->setDocumentSize(activeRect.size(), false); setDocumentSize(activeRect.size().toSize()); } int SpreadsheetImpl::indexCount() const { return d->document->map()->count(); } QUrl SpreadsheetImpl::urlAtPoint(QPoint point) { for( const QPair< QRectF, QUrl >& link : d->links ) { QRectF hitTarget{ link.first.x() - Private::wiggleFactor, link.first.y() - Private::wiggleFactor, link.first.width() + Private::wiggleFactor * 2, link.first.height() + Private::wiggleFactor * 2 }; if( hitTarget.contains( point ) ) { return link.second; } } return QUrl(); } + +QObject* SpreadsheetImpl::part() const +{ + return d->part; +} diff --git a/components/impl/SpreadsheetImpl.h b/components/impl/SpreadsheetImpl.h index 92827408779..f15f10231e0 100644 --- a/components/impl/SpreadsheetImpl.h +++ b/components/impl/SpreadsheetImpl.h @@ -1,55 +1,56 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef CALLIGRA_COMPONENTS_SPREADSHEETIMPL_H #define CALLIGRA_COMPONENTS_SPREADSHEETIMPL_H #include "DocumentImpl.h" namespace Calligra { namespace Components { class SpreadsheetImpl : public DocumentImpl { Q_OBJECT public: explicit SpreadsheetImpl(QObject* parent = 0); ~SpreadsheetImpl(); virtual bool load(const QUrl& url); virtual int currentIndex(); virtual void setCurrentIndex(int newValue); virtual int indexCount() const; virtual QUrl urlAtPoint(QPoint point); + virtual QObject* part() const; private Q_SLOTS: void updateDocumentSize(const QSize& size); private: class Private; Private* const d; }; } // Namespace Components } // Namespace Calligra #endif // CALLIGRA_COMPONENTS_SPREADSHEETIMPL_H diff --git a/components/impl/TextDocumentImpl.cpp b/components/impl/TextDocumentImpl.cpp index 646edcfb2f3..3d097292456 100644 --- a/components/impl/TextDocumentImpl.cpp +++ b/components/impl/TextDocumentImpl.cpp @@ -1,247 +1,249 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "TextDocumentImpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "ComponentsKoCanvasController.h" #include using namespace Calligra::Components; class TextDocumentImpl::Private { public: Private() : part{nullptr}, document{nullptr} { } KWPart* part; KWDocument* document; KWCanvasItem* canvas; QTimer indexChangedDelay; QList< QPair< QRectF, QUrl > > links; QList deepShapeFind(QList shapes) { QList allShapes; foreach(KoShape* shape, shapes) { allShapes.append(shape); KoShapeContainer *container = dynamic_cast(shape); if(container) { allShapes.append(deepShapeFind(container->shapes())); } } return allShapes; } void updateLinkTargets() { links.clear(); if(!canvas) return; foreach(const KoShape* shape, canvas->shapeManager()->shapes()) { if(!shape->hyperLink().isEmpty()) { QRectF rect = shape->boundingRect(); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } links.append(QPair(rect, QUrl(shape->hyperLink()))); } } QList texts; KoFindText::findTextInShapes(canvas->shapeManager()->shapes(), texts); QList allShapes = deepShapeFind(canvas->shapeManager()->shapes()); foreach(QTextDocument* text, texts) { QTextBlock block = text->rootFrame()->firstCursorPosition().block(); for (; block.isValid(); block = block.next()) { block.begin(); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QTextCharFormat format = fragment.charFormat(); if(format.isAnchor()) { // This is an anchor, store target and position... QRectF rect = getFragmentPosition(block, fragment); foreach(KoShape* shape, allShapes) { KoTextShapeData *shapeData = dynamic_cast(shape->userData()); if (!shapeData) continue; if(shapeData->document() == text) { rect.translate(shape->position()); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } break; } } KWPage page = document->pageManager()->page(rect.top()); //rect.translate(page.rightMargin(), page.topMargin()); //rect = canvas->viewConverter()->documentToView(rect); //rect.translate(0, page.pageNumber() * (page.topMargin() + page.bottomMargin()) + 20); rect.translate(0, (page.pageNumber() - 1) * (page.topMargin() + 20)); links.append(QPair(canvas->viewConverter()->documentToView(rect), QUrl(format.anchorHref()))); } } } } } qDebug() << links; } QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment) { // TODO this only produces a position for the first part, if the link spans more than one line... // Need to sort that somehow, unfortunately probably by slapping this code into the above function. // For now leave it like this, more important things are needed. QTextLayout* layout = block.layout(); QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); if(!line.isValid()) { // fragment has no valid position and consequently no line... return QRectF(); } qreal top = line.position().y() + (line.height() / 2); qreal bottom = top + line.height(); qreal left = line.cursorToX(fragment.position() - block.position()); qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); QRectF fragmentPosition(QPointF(left, top), QPointF(right, bottom)); return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); } static const float wiggleFactor; }; const float Calligra::Components::TextDocumentImpl::Private::wiggleFactor{ 4.f }; TextDocumentImpl::TextDocumentImpl(QObject* parent) : DocumentImpl{parent}, d{new Private} { setDocumentType(DocumentType::TextDocument); d->indexChangedDelay.setInterval(0); connect(&d->indexChangedDelay, SIGNAL(timeout()), this, SIGNAL(currentIndexChanged())); } TextDocumentImpl::~TextDocumentImpl() { delete d; } bool TextDocumentImpl::load(const QUrl& url) { if(d->part) { delete d->part; delete d->document; } d->part = new KWPart{this}; d->document = new KWDocument{d->part}; setKoDocument(d->document); d->part->setDocument(d->document); d->document->setAutoSave(0); d->document->setCheckAutoSaveFile(false); bool retval = d->document->openUrl(url); + qDebug() << "Attempting to open" << url << "and our success was" << retval; d->canvas = static_cast(d->part->canvasItem(d->document)); createAndSetCanvasController(d->canvas); createAndSetZoomController(d->canvas); zoomController()->setPageSize(d->document->pageManager()->begin().rect().size()); connect(d->canvas, SIGNAL(documentSize(QSizeF)), zoomController(), SLOT(setDocumentSize(QSizeF))); d->canvas->updateSize(); setCanvas(d->canvas); connect(canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), &d->indexChangedDelay, SLOT(start())); d->updateLinkTargets(); return retval; } int TextDocumentImpl::currentIndex() { QPointF newPoint = d->canvas->viewConverter()->viewToDocument(canvasController()->documentOffset()); KWPage page = d->document->pageManager()->page(newPoint.y()); return page.pageNumber(); } void TextDocumentImpl::setCurrentIndex(int newValue) { KWPage newPage = d->document->pageManager()->page(newValue + 1); QRectF newRect = d->canvas->viewConverter()->documentToView(newPage.rect()); canvasController()->setScrollBarValue(newRect.topLeft().toPoint()); emit requestViewUpdate(); emit currentIndexChanged(); } int TextDocumentImpl::indexCount() const { return d->document->pageCount(); } QUrl TextDocumentImpl::urlAtPoint(QPoint point) { qDebug() << Q_FUNC_INFO << point + (d->canvas->documentOffset() / zoomController()->zoomAction()->effectiveZoom()); for( const QPair< QRectF, QUrl >& link : d->links ) { QRectF hitTarget{ link.first.x() - Private::wiggleFactor, link.first.y() - Private::wiggleFactor, link.first.width() + Private::wiggleFactor * 2, link.first.height() + Private::wiggleFactor * 2 }; if( hitTarget.contains( point + (d->canvas->documentOffset() / zoomController()->zoomAction()->effectiveZoom()) ) ) return link.second; } return QUrl(); } QObject* TextDocumentImpl::part() const { return d->part; } diff --git a/components/models/TextContentsModelImpl.cpp b/components/models/TextContentsModelImpl.cpp index 0f3fd3accfb..96de0a285eb 100644 --- a/components/models/TextContentsModelImpl.cpp +++ b/components/models/TextContentsModelImpl.cpp @@ -1,194 +1,196 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "TextContentsModelImpl.h" #include #include #include #include #include #include #include using namespace Calligra::Components; struct ContentsEntry { ContentsEntry() : level{0}, pageNumber{0}, page{nullptr} {} QString title; int level; int pageNumber; KWPage* page; }; class TextContentsModelImpl::Private { public: Private() : useToC{false}, document{nullptr}, textDocument{nullptr}, layout{nullptr}, canvas{nullptr} { } bool useToC; KWDocument* document; QTextDocument* textDocument; KoTextDocumentLayout* layout; KoCanvasBase* canvas; QHash thumbnails; QSize thumbnailSize; QList entries; }; TextContentsModelImpl::TextContentsModelImpl(KoDocument* document, KoCanvasBase* canvas) : d{new Private} { d->document = qobject_cast(document); Q_ASSERT(d->document); - d->textDocument = d->document->mainFrameSet()->document(); - d->layout = qobject_cast(d->textDocument->documentLayout()); - connect(d->layout, &KoTextDocumentLayout::finishedLayout, this, &TextContentsModelImpl::documentLayoutFinished); - d->layout->scheduleLayout(); + if(d->document->mainFrameSet() && d->document->mainFrameSet()->document()) { + d->textDocument = d->document->mainFrameSet()->document(); + d->layout = qobject_cast(d->textDocument->documentLayout()); + connect(d->layout, &KoTextDocumentLayout::finishedLayout, this, &TextContentsModelImpl::documentLayoutFinished); + d->layout->scheduleLayout(); + } d->canvas = canvas; } TextContentsModelImpl::~TextContentsModelImpl() { delete d; } int TextContentsModelImpl::rowCount() const { if(d->useToC && d->entries.count() > 0) { return d->entries.count(); } return d->document->pageCount(); } QVariant TextContentsModelImpl::data(int index, Calligra::Components::ContentsModel::Role role) const { if(d->useToC && d->entries.count() > 0) { auto entry = d->entries.at(index); switch(role) { case ContentsModel::TitleRole: return entry.title; case ContentsModel::LevelRole: return entry.level; case ContentsModel::ThumbnailRole: { if(d->thumbnails.contains(entry.pageNumber)) { return d->thumbnails.value(entry.pageNumber); } if(d->thumbnailSize.isNull()) { return QImage{}; } QImage thumb = entry.page->thumbnail(d->thumbnailSize, d->canvas->shapeManager()); d->thumbnails.insert(entry.pageNumber, thumb); return thumb; } case ContentsModel::ContentIndexRole: { return entry.pageNumber - 1; } default: return QVariant(); } } //Fallback behaviour when we don't have a ToC KWPage page = d->document->pageManager()->page(index + 1); if(!page.isValid()) return QVariant(); switch(role) { case ContentsModel::TitleRole: return i18n("Page %1", page.pageNumber()); case ContentsModel::LevelRole: return 0; case ContentsModel::ThumbnailRole: { if(d->thumbnails.contains(index)) { return d->thumbnails.value(index); } if(d->thumbnailSize.isNull()) { return QImage{}; } QImage thumb = page.thumbnail(d->thumbnailSize, d->canvas->shapeManager()); d->thumbnails.insert(index, thumb); return thumb; } case ContentsModel::ContentIndexRole: { return index; } default: return QVariant(); } } void TextContentsModelImpl::setThumbnailSize(const QSize& size) { d->thumbnailSize = size; d->thumbnails.clear(); } QImage TextContentsModelImpl::thumbnail(int index, int width) const { KWPage page = d->document->pageManager()->page( index + 1 ); return page.thumbnail(QSize{ width, int((page.height() / page.width()) * width)}, d->canvas->shapeManager()); } void TextContentsModelImpl::setUseToC(bool newValue) { d->useToC = newValue; } void TextContentsModelImpl::documentLayoutFinished() { QTextBlock block = d->textDocument->firstBlock(); d->entries.clear(); while (block.isValid()) { QTextBlockFormat format = block.blockFormat(); if (format.hasProperty(KoParagraphStyle::OutlineLevel)) { ContentsEntry entry; entry.title = block.text(); entry.level = format.intProperty(KoParagraphStyle::OutlineLevel); auto rootArea = d->layout->rootAreaForPosition(block.position()); if (rootArea) { if (rootArea->page()) { entry.pageNumber = rootArea->page()->visiblePageNumber(); entry.page = static_cast(rootArea->page()); } } d->entries.append(entry); } block = block.next(); } emit listContentsCompleted(); }