File indexing completed on 2024-05-12 16:28:24

0001 /*
0002  * This file is part of the KDE project
0003  *
0004  * Copyright (C) 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public License
0017  * along with this library; see the file COPYING.LIB.  If not, write to
0018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  *
0021  */
0022 
0023 #include "TextContentsModelImpl.h"
0024 
0025 #include <KWDocument.h>
0026 #include <KWPage.h>
0027 #include <frames/KWTextFrameSet.h>
0028 #include <KoCanvasBase.h>
0029 #include <KoTextDocumentLayout.h>
0030 #include <KoTextLayoutRootArea.h>
0031 #include <styles/KoParagraphStyle.h>
0032 
0033 using namespace Calligra::Components;
0034 
0035 struct ContentsEntry {
0036     ContentsEntry() : level{0}, pageNumber{0}, page{nullptr}
0037     {}
0038     QString title;
0039     int level;
0040     int pageNumber;
0041     KWPage* page;
0042 };
0043 
0044 class TextContentsModelImpl::Private
0045 {
0046 public:
0047     Private() : useToC{false}, document{nullptr}, textDocument{nullptr}, layout{nullptr}, canvas{nullptr}
0048     { }
0049 
0050     bool useToC;
0051     KWDocument* document;
0052     QTextDocument* textDocument;
0053     KoTextDocumentLayout* layout;
0054     KoCanvasBase* canvas;
0055 
0056     QHash<int, QImage> thumbnails;
0057     QSize thumbnailSize;
0058 
0059     QList<ContentsEntry> entries;
0060 };
0061 
0062 TextContentsModelImpl::TextContentsModelImpl(KoDocument* document, KoCanvasBase* canvas)
0063     : d{new Private}
0064 {
0065     d->document = qobject_cast<KWDocument*>(document);
0066     Q_ASSERT(d->document);
0067     if(d->document->mainFrameSet() && d->document->mainFrameSet()->document()) {
0068         d->textDocument = d->document->mainFrameSet()->document();
0069         d->layout = qobject_cast<KoTextDocumentLayout*>(d->textDocument->documentLayout());
0070         connect(d->layout, &KoTextDocumentLayout::finishedLayout, this, &TextContentsModelImpl::documentLayoutFinished);
0071         d->layout->scheduleLayout();
0072     }
0073     d->canvas = canvas;
0074 }
0075 
0076 TextContentsModelImpl::~TextContentsModelImpl()
0077 {
0078     delete d;
0079 }
0080 
0081 int TextContentsModelImpl::rowCount() const
0082 {
0083     if(d->useToC && d->entries.count() > 0) {
0084         return d->entries.count();
0085     }
0086 
0087     return d->document->pageCount();
0088 }
0089 
0090 QVariant TextContentsModelImpl::data(int index, Calligra::Components::ContentsModel::Role role) const
0091 {
0092     if(d->useToC && d->entries.count() > 0) {
0093         auto entry = d->entries.at(index);
0094         switch(role) {
0095             case ContentsModel::TitleRole:
0096                 return entry.title;
0097             case ContentsModel::LevelRole:
0098                 return entry.level;
0099             case ContentsModel::ThumbnailRole: {
0100                 if(d->thumbnails.contains(entry.pageNumber)) {
0101                     return d->thumbnails.value(entry.pageNumber);
0102                 }
0103 
0104                 if(d->thumbnailSize.isNull()) {
0105                     return QImage{};
0106                 }
0107 
0108                 QImage thumb = entry.page->thumbnail(d->thumbnailSize, d->canvas->shapeManager());
0109                 d->thumbnails.insert(entry.pageNumber, thumb);
0110                 return thumb;
0111             }
0112             case ContentsModel::ContentIndexRole: {
0113                 return entry.pageNumber - 1;
0114             }
0115             default:
0116                 return QVariant();
0117         }
0118     }
0119 
0120     //Fallback behaviour when we don't have a ToC
0121     KWPage page = d->document->pageManager()->page(index + 1);
0122     if(!page.isValid())
0123         return QVariant();
0124 
0125     switch(role) {
0126         case ContentsModel::TitleRole:
0127             return i18n("Page %1", page.pageNumber());
0128         case ContentsModel::LevelRole:
0129             return 0;
0130         case ContentsModel::ThumbnailRole: {
0131             if(d->thumbnails.contains(index)) {
0132                 return d->thumbnails.value(index);
0133             }
0134 
0135             if(d->thumbnailSize.isNull()) {
0136                 return QImage{};
0137             }
0138 
0139             QImage thumb = page.thumbnail(d->thumbnailSize, d->canvas->shapeManager());
0140             d->thumbnails.insert(index, thumb);
0141             return thumb;
0142         }
0143         case ContentsModel::ContentIndexRole: {
0144             return index;
0145         }
0146         default:
0147             return QVariant();
0148     }
0149 }
0150 
0151 void TextContentsModelImpl::setThumbnailSize(const QSize& size)
0152 {
0153     d->thumbnailSize = size;
0154     d->thumbnails.clear();
0155 }
0156 
0157 QImage TextContentsModelImpl::thumbnail(int index, int width) const
0158 {
0159     KWPage page = d->document->pageManager()->page( index + 1 );
0160     return page.thumbnail(QSize{ width, int((page.height() / page.width()) * width)}, d->canvas->shapeManager());
0161 }
0162 
0163 void TextContentsModelImpl::setUseToC(bool newValue)
0164 {
0165     d->useToC = newValue;
0166 }
0167 
0168 void TextContentsModelImpl::documentLayoutFinished()
0169 {
0170     QTextBlock block = d->textDocument->firstBlock();
0171     d->entries.clear();
0172 
0173     while (block.isValid())
0174     {
0175         QTextBlockFormat format = block.blockFormat();
0176         if (format.hasProperty(KoParagraphStyle::OutlineLevel))
0177         {
0178             ContentsEntry entry;
0179             entry.title = block.text();
0180             entry.level = format.intProperty(KoParagraphStyle::OutlineLevel);
0181 
0182             auto rootArea = d->layout->rootAreaForPosition(block.position());
0183             if (rootArea) {
0184                 if (rootArea->page()) {
0185                     entry.pageNumber = rootArea->page()->visiblePageNumber();
0186                     entry.page = static_cast<KWPage*>(rootArea->page());
0187                 }
0188             }
0189 
0190             d->entries.append(entry);
0191         }
0192         block = block.next();
0193     }
0194 
0195     emit listContentsCompleted();
0196 }