File indexing completed on 2024-05-12 16:37:15

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2006, 2009 Thomas Zander <zander@kde.org>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "KWViewModeNormal.h"
0021 #include "KWPageManager.h"
0022 #include "KWPage.h"
0023 #include "KWView.h"
0024 #include <KoViewConverter.h>
0025 
0026 #include <WordsDebug.h>
0027 
0028 #define GAP 20
0029 
0030 KWViewModeNormal::KWViewModeNormal()
0031     : m_pageSpreadMode(false)
0032 {
0033 }
0034 
0035 QVector<KWViewMode::ViewMap> KWViewModeNormal::mapExposedRects(const QRectF &viewRect, KoViewConverter *viewConverter) const
0036 {
0037     QVector<ViewMap> answer;
0038     if (!viewConverter) return answer;
0039 
0040 #if 1
0041     if (m_pageTops.isEmpty())
0042         return answer;
0043     KWPage page  = m_pageManager->begin();
0044     const int pageOffset = page.pageNumber();
0045 
0046     // Perform a binary search for page-index using our m_pageTops cache.
0047     int begin = 0;
0048     int end = m_pageTops.count() - 1;
0049     int index = 0;
0050     const qreal value = viewConverter->viewToDocument(viewRect.topLeft()).y();
0051     if (m_pageTops.value(end) <= value) { // check extremes. Only end is needed since begin is zero.
0052         begin = end;
0053         index = end;
0054     }
0055     while (end - begin > 1) {
0056         index = begin + (end - begin) / 2;
0057         qreal diff = m_pageTops.value(index) - value;
0058         if (diff < 0)
0059             begin = index;
0060         else if (diff > 0)
0061             end = index;
0062         else
0063             break;
0064     }
0065     // index is now the number of the first possible page that can
0066     // contain the viewRect.  The next step is to find the
0067     // corresponding Page.  Since we have no way to get to the Nth
0068     // page directly we have to enumerate them from the beginning.
0069     //
0070     // We use 1 since we might hit a pagespread in the binary search,
0071     // so start one page early.
0072     while (index > 1) { 
0073         page = page.next();
0074         --index;
0075     }
0076 
0077     // From here we loop through some more pages after the found one
0078     // and see if they intersect with the page in question.  When we
0079     // have two pages in row that don't intersect we break the loop.
0080     qreal offsetX = 0.0;
0081     int emptyPages = 0;
0082     for(; page.isValid(); page = page.next()) {
0083         Q_ASSERT_X(page.pageNumber()-pageOffset < m_pageTops.count(), __FUNCTION__,
0084                    QString("Pagemanager has more pages than viewmode (%1>%2 with pageOffset=%3 and pageNumber=%4 and pageCount=%5). Make sure you add pages via the document!")
0085                    .arg(page.pageNumber()-pageOffset).arg(m_pageTops.count())
0086                    .arg(pageOffset).arg(page.pageNumber()).arg(m_pageManager->pageCount()).toLocal8Bit());
0087 
0088 
0089         // Some invariants
0090         const QRectF pageRect = page.rect();
0091         const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top();
0092 
0093         bool pageIntersects = false;
0094 
0095         // 1. First handle the page itself.
0096         const QRectF zoomedPage = viewConverter->documentToView(pageRect);
0097         ViewMap vm;
0098         vm.page = page;
0099         //kDebug(32003) <<"page" << page.pageNumber();
0100         vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
0101 
0102         const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(),
0103                                 zoomedPage.width(), zoomedPage.height());
0104         QRectF intersection = targetPage.intersected(viewRect);
0105         if (! intersection.isEmpty()) {
0106             intersection.moveTopLeft(intersection.topLeft() - vm.distance);
0107             vm.clipRect = intersection.toRect();
0108             answer.append(vm);
0109             pageIntersects = true;
0110         }
0111 
0112         // 2. Then handle the annotation area if annotations are active.
0113         //
0114         //    The reason we don't do them together with the page
0115         //    itself is because the pages have a gap between them, but
0116         //    the annotation area should be unbroken.
0117         //
0118         // NOTE: 'annotation' below means the annotation area.
0119         //
0120         // FIXME: We should only do this if the annotation area is
0121         //        actually shown. How can we inside the KWViewMode
0122         //        know if annotations are active?
0123         if (1 /* annotations are shown */) {
0124             const QRectF annotationRect = pageRect.adjusted(page.width(), 0,
0125                                                             KWCanvasBase::AnnotationAreaWidth, GAP);
0126             const QRectF zoomedAnnotation = viewConverter->documentToView(annotationRect);
0127             ViewMap vm2;
0128             vm2.page = page;
0129             vm2.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
0130 
0131             const QRectF targetAnnotation(zoomedAnnotation.x() + vm2.distance.x(),
0132                                           zoomedAnnotation.y() + vm2.distance.y(),
0133                                           zoomedAnnotation.width(), zoomedAnnotation.height());
0134             intersection = targetAnnotation.intersected(viewRect);
0135             if (! intersection.isEmpty()) {
0136                 intersection.moveTopLeft(intersection.topLeft() - vm2.distance);
0137                 vm2.clipRect = intersection.toRect();
0138                 answer.append(vm2);
0139                 pageIntersects = true;
0140             }
0141         }
0142 
0143         // Record whether this page had an intersection with the view rect.
0144         if (pageIntersects) {
0145             emptyPages = 0;
0146         } else {
0147             ++emptyPages;
0148         }
0149         if (emptyPages > 2) // Since we show at max 2 pages side by side this is an easy rule
0150             break;
0151 
0152         if (m_pageSpreadMode) {
0153             if (page.pageSide() == KWPage::Left)
0154                 offsetX = page.width() + GAP;
0155             else
0156                 offsetX = 0.0;
0157         }
0158     }
0159 #else
0160     KWPage page  = m_pageManager->begin();
0161     Q_ASSERT(page.isValid());
0162     qreal offsetX = 0.0;
0163     const int pageOffset = page.pageNumber();
0164     for(; page.isValid(); page = page.next()) {
0165         const QRectF pageRect = page.rect();
0166         const QRectF zoomedPage = viewConverter->documentToView(pageRect);
0167         ViewMap vm;
0168         vm.page = page;
0169 
0170         const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top();
0171         vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
0172 #if 0
0173         const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height());
0174         QRectF intersection = targetPage.intersected(viewRect);
0175         if (! intersection.isEmpty()) {
0176             intersection.moveTopLeft(intersection.topLeft() - vm.distance);
0177             vm.clipRect = intersection.toRect();
0178             answer.append(vm);
0179         }
0180 #else
0181         const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height());
0182         vm.clipRect = targetPage.toRect();
0183         answer.append(vm);
0184 #endif
0185     }
0186 #endif
0187     return answer;
0188 }
0189 
0190 void KWViewModeNormal::updatePageCache()
0191 {
0192     if (!m_pageManager) {
0193         warnWords << "Error detected while running KWViewModeNormal::updatePageCache: PageManager not set";
0194         return;
0195     }
0196 
0197     m_pageSpreadMode = false;
0198     foreach (const KWPage &page, m_pageManager->pages()) {
0199         Q_UNUSED(page);
0200     }
0201     m_pageTops.clear();
0202     qreal width = 0.0, bottom = 0.0;
0203     if (m_pageSpreadMode) { // two pages next to each other per row
0204         qreal top = 0.0, last = 0.0, halfWidth = 0.0;
0205         foreach (const KWPage &page, m_pageManager->pages()) {
0206             switch (page.pageSide()) {
0207             case KWPage::Left:
0208                 m_pageTops.append(top);
0209                 last = page.height();
0210                 halfWidth = page.width() + GAP;
0211                 width = qMax(width, halfWidth);
0212                 bottom = top + last;
0213                 break;
0214             case KWPage::Right:
0215                 m_pageTops.append(top);
0216                 top += qMax(page.height(), last);
0217                 last = 0.0;
0218                 width = qMax(width, halfWidth + page.width());
0219                 halfWidth = 0.0;
0220                 bottom = top;
0221                 top += GAP;
0222                 break;
0223             default:
0224                 Q_ASSERT(false);
0225                 break;
0226             }
0227         }
0228     } else { // each page on a row
0229         qreal top = 0.0;
0230         foreach (const KWPage &page, m_pageManager->pages()) {
0231             m_pageTops.append(top);
0232             top += page.height() + GAP;
0233             width = qMax(width, page.width());
0234         }
0235         bottom = top;
0236     }
0237     if (bottom > GAP)
0238         bottom -= GAP; // remove one too many added
0239     m_contents = QSizeF(width, bottom);
0240 }
0241 
0242 QPointF KWViewModeNormal::documentToView(const QPointF & point, KoViewConverter *viewConverter) const
0243 {
0244     Q_ASSERT(viewConverter);
0245 
0246     KWPage page = m_pageManager->page(point);
0247     if (! page.isValid())
0248         page = m_pageManager->last();
0249     if (! page.isValid())
0250         return QPointF();
0251     int pageIndex = page.pageNumber() - m_pageManager->begin().pageNumber();
0252     qreal x = 0;
0253     if (m_pageSpreadMode && page.pageSide() == KWPage::Right) {
0254         KWPage prevPage = m_pageManager->page(page.pageNumber() - 1);
0255         if (prevPage.isValid())
0256             x = prevPage.width();
0257     }
0258 
0259     QPointF offsetInPage(point.x(),  + point.y() - page.offsetInDocument());
0260     Q_ASSERT(pageIndex >= 0);
0261     Q_ASSERT(pageIndex < m_pageTops.count());
0262     QPointF translated(x, m_pageTops[pageIndex]);
0263     return viewConverter->documentToView(translated + offsetInPage);
0264 }
0265 
0266 QPointF KWViewModeNormal::viewToDocument(const QPointF & point, KoViewConverter *viewConverter) const
0267 {
0268     Q_ASSERT(viewConverter);
0269 
0270     QPointF clippedPoint(qMax(qreal(0.0), point.x()), qMax(qreal(0.0), point.y()));
0271     QPointF translated = viewConverter->viewToDocument(clippedPoint);
0272     int pageNumber = 0;
0273     foreach (qreal top, m_pageTops) {
0274         if (translated.y() < top)
0275             break;
0276         pageNumber++;
0277     }
0278     translated = viewConverter->viewToDocument(point);
0279     KWPage page = m_pageManager->page(pageNumber - 1 + m_pageManager->begin().pageNumber());
0280     qreal xOffset = translated.x();
0281 
0282     if (page.isValid() && m_pageSpreadMode && page.pageSide() == KWPage::Right && page != m_pageManager->begin()) {
0283         // there is a page displayed left of this one.
0284         KWPage prevPage = page.previous();
0285         if (xOffset <= prevPage.width()) // was left page instead of right :)
0286             page = prevPage;
0287         else
0288             xOffset -= prevPage.width();
0289     }
0290 
0291     if (! page.isValid()) // below doc or right of last page
0292         return QPointF(m_contents.width(), m_contents.height());
0293 
0294     qreal yOffset = translated.y();
0295     if (pageNumber >= 0)
0296         yOffset -= m_pageTops[pageNumber -1];
0297 
0298     return QPointF(xOffset, page.offsetInDocument() + yOffset);
0299 }