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 }