1 /* This file is part of the KDE project
2 * Copyright (C) 2006, 2009 Thomas Zander <zander@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "KWViewModeNormal.h"
21 #include "KWPageManager.h"
22 #include "KWPage.h"
23 #include "KWView.h"
24 #include <KoViewConverter.h>
25
26 #include <WordsDebug.h>
27
28 #define GAP 20
29
KWViewModeNormal()30 KWViewModeNormal::KWViewModeNormal()
31 : m_pageSpreadMode(false)
32 {
33 }
34
mapExposedRects(const QRectF & viewRect,KoViewConverter * viewConverter) const35 QVector<KWViewMode::ViewMap> KWViewModeNormal::mapExposedRects(const QRectF &viewRect, KoViewConverter *viewConverter) const
36 {
37 QVector<ViewMap> answer;
38 if (!viewConverter) return answer;
39
40 #if 1
41 if (m_pageTops.isEmpty())
42 return answer;
43 KWPage page = m_pageManager->begin();
44 const int pageOffset = page.pageNumber();
45
46 // Perform a binary search for page-index using our m_pageTops cache.
47 int begin = 0;
48 int end = m_pageTops.count() - 1;
49 int index = 0;
50 const qreal value = viewConverter->viewToDocument(viewRect.topLeft()).y();
51 if (m_pageTops.value(end) <= value) { // check extremes. Only end is needed since begin is zero.
52 begin = end;
53 index = end;
54 }
55 while (end - begin > 1) {
56 index = begin + (end - begin) / 2;
57 qreal diff = m_pageTops.value(index) - value;
58 if (diff < 0)
59 begin = index;
60 else if (diff > 0)
61 end = index;
62 else
63 break;
64 }
65 // index is now the number of the first possible page that can
66 // contain the viewRect. The next step is to find the
67 // corresponding Page. Since we have no way to get to the Nth
68 // page directly we have to enumerate them from the beginning.
69 //
70 // We use 1 since we might hit a pagespread in the binary search,
71 // so start one page early.
72 while (index > 1) {
73 page = page.next();
74 --index;
75 }
76
77 // From here we loop through some more pages after the found one
78 // and see if they intersect with the page in question. When we
79 // have two pages in row that don't intersect we break the loop.
80 qreal offsetX = 0.0;
81 int emptyPages = 0;
82 for(; page.isValid(); page = page.next()) {
83 Q_ASSERT_X(page.pageNumber()-pageOffset < m_pageTops.count(), __FUNCTION__,
84 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!")
85 .arg(page.pageNumber()-pageOffset).arg(m_pageTops.count())
86 .arg(pageOffset).arg(page.pageNumber()).arg(m_pageManager->pageCount()).toLocal8Bit());
87
88
89 // Some invariants
90 const QRectF pageRect = page.rect();
91 const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top();
92
93 bool pageIntersects = false;
94
95 // 1. First handle the page itself.
96 const QRectF zoomedPage = viewConverter->documentToView(pageRect);
97 ViewMap vm;
98 vm.page = page;
99 //kDebug(32003) <<"page" << page.pageNumber();
100 vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
101
102 const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(),
103 zoomedPage.width(), zoomedPage.height());
104 QRectF intersection = targetPage.intersected(viewRect);
105 if (! intersection.isEmpty()) {
106 intersection.moveTopLeft(intersection.topLeft() - vm.distance);
107 vm.clipRect = intersection.toRect();
108 answer.append(vm);
109 pageIntersects = true;
110 }
111
112 // 2. Then handle the annotation area if annotations are active.
113 //
114 // The reason we don't do them together with the page
115 // itself is because the pages have a gap between them, but
116 // the annotation area should be unbroken.
117 //
118 // NOTE: 'annotation' below means the annotation area.
119 //
120 // FIXME: We should only do this if the annotation area is
121 // actually shown. How can we inside the KWViewMode
122 // know if annotations are active?
123 if (1 /* annotations are shown */) {
124 const QRectF annotationRect = pageRect.adjusted(page.width(), 0,
125 KWCanvasBase::AnnotationAreaWidth, GAP);
126 const QRectF zoomedAnnotation = viewConverter->documentToView(annotationRect);
127 ViewMap vm2;
128 vm2.page = page;
129 vm2.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
130
131 const QRectF targetAnnotation(zoomedAnnotation.x() + vm2.distance.x(),
132 zoomedAnnotation.y() + vm2.distance.y(),
133 zoomedAnnotation.width(), zoomedAnnotation.height());
134 intersection = targetAnnotation.intersected(viewRect);
135 if (! intersection.isEmpty()) {
136 intersection.moveTopLeft(intersection.topLeft() - vm2.distance);
137 vm2.clipRect = intersection.toRect();
138 answer.append(vm2);
139 pageIntersects = true;
140 }
141 }
142
143 // Record whether this page had an intersection with the view rect.
144 if (pageIntersects) {
145 emptyPages = 0;
146 } else {
147 ++emptyPages;
148 }
149 if (emptyPages > 2) // Since we show at max 2 pages side by side this is an easy rule
150 break;
151
152 if (m_pageSpreadMode) {
153 if (page.pageSide() == KWPage::Left)
154 offsetX = page.width() + GAP;
155 else
156 offsetX = 0.0;
157 }
158 }
159 #else
160 KWPage page = m_pageManager->begin();
161 Q_ASSERT(page.isValid());
162 qreal offsetX = 0.0;
163 const int pageOffset = page.pageNumber();
164 for(; page.isValid(); page = page.next()) {
165 const QRectF pageRect = page.rect();
166 const QRectF zoomedPage = viewConverter->documentToView(pageRect);
167 ViewMap vm;
168 vm.page = page;
169
170 const qreal offsetY = m_pageTops[page.pageNumber() - pageOffset] - pageRect.top();
171 vm.distance = viewConverter->documentToView(QPointF(offsetX, offsetY));
172 #if 0
173 const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height());
174 QRectF intersection = targetPage.intersected(viewRect);
175 if (! intersection.isEmpty()) {
176 intersection.moveTopLeft(intersection.topLeft() - vm.distance);
177 vm.clipRect = intersection.toRect();
178 answer.append(vm);
179 }
180 #else
181 const QRectF targetPage(zoomedPage.x() + vm.distance.x(), zoomedPage.y() + vm.distance.y(), zoomedPage.width() , zoomedPage.height());
182 vm.clipRect = targetPage.toRect();
183 answer.append(vm);
184 #endif
185 }
186 #endif
187 return answer;
188 }
189
updatePageCache()190 void KWViewModeNormal::updatePageCache()
191 {
192 if (!m_pageManager) {
193 warnWords << "Error detected while running KWViewModeNormal::updatePageCache: PageManager not set";
194 return;
195 }
196
197 m_pageSpreadMode = false;
198 foreach (const KWPage &page, m_pageManager->pages()) {
199 Q_UNUSED(page);
200 }
201 m_pageTops.clear();
202 qreal width = 0.0, bottom = 0.0;
203 if (m_pageSpreadMode) { // two pages next to each other per row
204 qreal top = 0.0, last = 0.0, halfWidth = 0.0;
205 foreach (const KWPage &page, m_pageManager->pages()) {
206 switch (page.pageSide()) {
207 case KWPage::Left:
208 m_pageTops.append(top);
209 last = page.height();
210 halfWidth = page.width() + GAP;
211 width = qMax(width, halfWidth);
212 bottom = top + last;
213 break;
214 case KWPage::Right:
215 m_pageTops.append(top);
216 top += qMax(page.height(), last);
217 last = 0.0;
218 width = qMax(width, halfWidth + page.width());
219 halfWidth = 0.0;
220 bottom = top;
221 top += GAP;
222 break;
223 default:
224 Q_ASSERT(false);
225 break;
226 }
227 }
228 } else { // each page on a row
229 qreal top = 0.0;
230 foreach (const KWPage &page, m_pageManager->pages()) {
231 m_pageTops.append(top);
232 top += page.height() + GAP;
233 width = qMax(width, page.width());
234 }
235 bottom = top;
236 }
237 if (bottom > GAP)
238 bottom -= GAP; // remove one too many added
239 m_contents = QSizeF(width, bottom);
240 }
241
documentToView(const QPointF & point,KoViewConverter * viewConverter) const242 QPointF KWViewModeNormal::documentToView(const QPointF & point, KoViewConverter *viewConverter) const
243 {
244 Q_ASSERT(viewConverter);
245
246 KWPage page = m_pageManager->page(point);
247 if (! page.isValid())
248 page = m_pageManager->last();
249 if (! page.isValid())
250 return QPointF();
251 int pageIndex = page.pageNumber() - m_pageManager->begin().pageNumber();
252 qreal x = 0;
253 if (m_pageSpreadMode && page.pageSide() == KWPage::Right) {
254 KWPage prevPage = m_pageManager->page(page.pageNumber() - 1);
255 if (prevPage.isValid())
256 x = prevPage.width();
257 }
258
259 QPointF offsetInPage(point.x(), + point.y() - page.offsetInDocument());
260 Q_ASSERT(pageIndex >= 0);
261 Q_ASSERT(pageIndex < m_pageTops.count());
262 QPointF translated(x, m_pageTops[pageIndex]);
263 return viewConverter->documentToView(translated + offsetInPage);
264 }
265
viewToDocument(const QPointF & point,KoViewConverter * viewConverter) const266 QPointF KWViewModeNormal::viewToDocument(const QPointF & point, KoViewConverter *viewConverter) const
267 {
268 Q_ASSERT(viewConverter);
269
270 QPointF clippedPoint(qMax(qreal(0.0), point.x()), qMax(qreal(0.0), point.y()));
271 QPointF translated = viewConverter->viewToDocument(clippedPoint);
272 int pageNumber = 0;
273 foreach (qreal top, m_pageTops) {
274 if (translated.y() < top)
275 break;
276 pageNumber++;
277 }
278 translated = viewConverter->viewToDocument(point);
279 KWPage page = m_pageManager->page(pageNumber - 1 + m_pageManager->begin().pageNumber());
280 qreal xOffset = translated.x();
281
282 if (page.isValid() && m_pageSpreadMode && page.pageSide() == KWPage::Right && page != m_pageManager->begin()) {
283 // there is a page displayed left of this one.
284 KWPage prevPage = page.previous();
285 if (xOffset <= prevPage.width()) // was left page instead of right :)
286 page = prevPage;
287 else
288 xOffset -= prevPage.width();
289 }
290
291 if (! page.isValid()) // below doc or right of last page
292 return QPointF(m_contents.width(), m_contents.height());
293
294 qreal yOffset = translated.y();
295 if (pageNumber >= 0)
296 yOffset -= m_pageTops[pageNumber -1];
297
298 return QPointF(xOffset, page.offsetInDocument() + yOffset);
299 }
300