1 /* This file is part of the KDE project
2 * Copyright (C) 2002-2006 David Faure <faure@kde.org>
3 * Copyright (C) 2005-2006 Thomas Zander <zander@kde.org>
4 * Copyright (C) 2009 Inge Wallin <inge@lysator.liu.se>
5 * Copyright (C) 2010-2011 Boudewijn Rempt <boud@kogmbh.com>
6 * Copyright (C) 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23 #include "KWCanvasBase.h"
24
25 // words includes
26 #include "KWCanvas.h"
27 #include "KWGui.h"
28 #include "KWViewMode.h"
29 #include "KWPage.h"
30 #include "KWPageCacheManager.h"
31 #include "frames/KWFrameLayout.h"
32 #include "WordsDebug.h"
33
34 // calligra libs includes
35 #include <KoShapeManager.h>
36 #include <KoPointerEvent.h>
37 #include <KoCanvasController.h>
38 #include <KoToolProxy.h>
39 #include <KoGridData.h>
40 #include <KoGuidesData.h>
41 #include <KoShape.h>
42 #include <KoViewConverter.h>
43 #include <KoUnit.h>
44
45 #include <KoAnnotationLayoutManager.h>
46
47 // Qt
48 #include <QBrush>
49 #include <QPainter>
50 #include <QPainterPath>
51 #include <QThread>
52
53 #include <sys/time.h>
54
55 //#define DEBUG_REPAINT
56
57
KWCanvasBase(KWDocument * document,QObject * parent)58 KWCanvasBase::KWCanvasBase(KWDocument *document, QObject *parent)
59 : KoCanvasBase(document),
60 m_document(document),
61 m_shapeManager(0),
62 m_toolProxy(0),
63 m_viewMode(0),
64 m_viewConverter(0),
65 m_showAnnotations(false),
66 m_cacheEnabled(false),
67 m_currentZoom(0.0),
68 m_maxZoom(2.0),
69 m_pageCacheManager(0)
70 {
71 m_shapeManager = new KoShapeManager(this);
72 m_toolProxy = new KoToolProxy(this, parent);
73 //setCacheEnabled(true);
74 }
75
~KWCanvasBase()76 KWCanvasBase::~KWCanvasBase()
77 {
78 delete m_shapeManager;
79 delete m_viewMode;
80 delete m_pageCacheManager;
81 }
82
gridSize(qreal * horizontal,qreal * vertical) const83 void KWCanvasBase::gridSize(qreal *horizontal, qreal *vertical) const
84 {
85 *horizontal = m_document->gridData().gridX();
86 *vertical = m_document->gridData().gridY();
87 }
88
addCommand(KUndo2Command * command)89 void KWCanvasBase::addCommand(KUndo2Command *command)
90 {
91 m_document->addCommand(command);
92 }
93
shapeManager() const94 KoShapeManager *KWCanvasBase::shapeManager() const
95 {
96 return m_shapeManager;
97 }
98
unit() const99 KoUnit KWCanvasBase::unit() const
100 {
101 return m_document->unit();
102 }
103
toolProxy() const104 KoToolProxy *KWCanvasBase::toolProxy() const
105 {
106 return m_toolProxy;
107 }
108
clipToDocument(const KoShape * shape,QPointF & move) const109 void KWCanvasBase::clipToDocument(const KoShape *shape, QPointF &move) const
110 {
111 Q_ASSERT(shape);
112 const QPointF absPos = shape->absolutePosition();
113 const QPointF destination = absPos + move;
114 qreal bottomOfPage = 0.0;
115 KWPage page;
116 foreach (const KWPage &p, m_document->pageManager()->pages()) {
117 bottomOfPage += p.height();
118 if (bottomOfPage >= absPos.y())
119 page = p;
120 if (bottomOfPage >= destination.y()) {
121 page = p;
122 break;
123 }
124 }
125 if (!page.isValid()) { // shape was not in any page to begin with, can't propose anything sane...
126 move.setX(0);
127 move.setY(0);
128 return;
129 }
130 QRectF pageRect(page.rect().adjusted(5, 5, -5, -5));
131 QPainterPath path(shape->absoluteTransformation(0).map(shape->outline()));
132 QRectF shapeBounds = path.boundingRect();
133 shapeBounds.moveTopLeft(shapeBounds.topLeft() + move);
134 if (!shapeBounds.intersects(pageRect)) {
135 if (shapeBounds.left() > pageRect.right()) // need to move to the left some
136 move.setX(move.x() + (pageRect.right() - shapeBounds.left()));
137 else if (shapeBounds.right() < pageRect.left()) // need to move to the right some
138 move.setX(move.x() + pageRect.left() - shapeBounds.right());
139
140 if (shapeBounds.top() > pageRect.bottom()) // need to move up some
141 move.setY(move.y() + (pageRect.bottom() - shapeBounds.top()));
142 else if (shapeBounds.bottom() < pageRect.top()) // need to move down some
143 move.setY(move.y() + pageRect.top() - shapeBounds.bottom());
144 }
145
146 // Also make sure any anchoring restrictions are adhered to
147 KWFrameLayout::proposeShapeMove(shape, move, page);
148 }
149
guidesData()150 KoGuidesData *KWCanvasBase::guidesData()
151 {
152 return &m_document->guidesData();
153 }
154
document() const155 KWDocument *KWCanvasBase::document() const
156 {
157 return m_document;
158 }
159
viewMode() const160 KWViewMode *KWCanvasBase::viewMode() const
161 {
162 return m_viewMode;
163 }
164
ensureVisible(const QRectF & rect)165 void KWCanvasBase::ensureVisible(const QRectF &rect)
166 {
167 QRectF viewRect = m_viewMode->documentToView(rect, m_viewConverter);
168 canvasController()->ensureVisible(viewRect);
169 }
170
showAnnotations() const171 bool KWCanvasBase::showAnnotations() const
172 {
173 return m_showAnnotations;
174 }
175
setShowAnnotations(bool doShow)176 void KWCanvasBase::setShowAnnotations(bool doShow)
177 {
178 m_showAnnotations = doShow;
179 }
180
paintBackgrounds(QPainter & painter,KWViewMode::ViewMap & viewMap)181 void KWCanvasBase::paintBackgrounds(QPainter &painter, KWViewMode::ViewMap &viewMap)
182 {
183 // Paint the page.
184
185 QColor color = Qt::white;
186 #ifdef DEBUG_REPAINT
187 color = QColor(random() % 255, random() % 255, random() % 255);
188 #endif
189 painter.fillRect(viewMap.clipRect, QBrush(color));
190
191 // Paint the annotation area if that is turned on.
192 if (m_showAnnotations) {
193 color = Qt::cyan;
194 QRect annotationRect(m_viewMode->contentsSize().width(), 0,
195 AnnotationAreaWidth, m_viewMode->contentsSize().height());
196 QRectF viewRect(m_viewMode->documentToView(annotationRect, m_viewConverter));
197 painter.fillRect(viewRect, QBrush(color));
198
199
200 if (m_document->annotationLayoutManager())
201 m_document->annotationLayoutManager()->paintConnections(painter);
202 }
203 }
204
paintPageDecorations(QPainter & painter,KWViewMode::ViewMap & viewMap)205 void KWCanvasBase::paintPageDecorations(QPainter &painter, KWViewMode::ViewMap &viewMap)
206 {
207 // We have no page shadows yet.
208 Q_UNUSED(painter);
209 Q_UNUSED(viewMap);
210 }
211
paintBorder(QPainter & painter,KWViewMode::ViewMap & viewMap)212 void KWCanvasBase::paintBorder(QPainter &painter, KWViewMode::ViewMap &viewMap)
213 {
214 painter.save();
215
216 const QRectF pageRect = viewMap.page.rect();
217 const KoPageLayout pageLayout = viewMap.page.pageStyle().pageLayout();
218
219 qreal zoomX, zoomY;
220 viewConverter()->zoom(&zoomX, &zoomY);
221 painter.scale(zoomX, zoomY);
222
223 QPointF topLeftCorner = QPointF(pageRect.topLeft() + QPointF(pageLayout.leftMargin,
224 pageLayout.topMargin));
225 QPointF bottomRightCorner = QPointF(pageRect.bottomRight() + QPointF(-pageLayout.rightMargin,
226 -pageLayout.bottomMargin));
227 QRectF borderRect = QRectF(topLeftCorner, bottomRightCorner);
228 pageLayout.border.paint(painter, borderRect);
229
230 painter.restore();
231 }
232
paintGrid(QPainter & painter,KWViewMode::ViewMap & vm)233 void KWCanvasBase::paintGrid(QPainter &painter, KWViewMode::ViewMap &vm)
234 {
235 painter.save();
236 painter.translate(-vm.distance.x(), -vm.distance.y());
237 painter.setRenderHint(QPainter::Antialiasing, false);
238 const QRectF clipRect = viewConverter()->viewToDocument(vm.clipRect);
239 document()->gridData().paintGrid(painter, *(viewConverter()), clipRect);
240 document()->guidesData().paintGuides(painter, *(viewConverter()), clipRect);
241 painter.restore();
242 }
243
paint(QPainter & painter,const QRectF & paintRect)244 void KWCanvasBase::paint(QPainter &painter, const QRectF &paintRect)
245 {
246 painter.translate(-m_documentOffset);
247
248 static int iteration = 0;
249 iteration++;
250
251 if (m_viewMode->hasPages()) {
252
253 int pageContentArea = 0;
254 if (!m_cacheEnabled || !m_pageCacheManager) { // no caching, simple case
255
256 QVector<KWViewMode::ViewMap> map =
257 m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
258 viewConverter());
259 foreach (KWViewMode::ViewMap vm, map) {
260 painter.save();
261
262 // Set up the painter to clip the part of the canvas that contains the rect.
263 // FIXME: The viewmap must also take into account the annotation area
264 painter.translate(vm.distance.x(), vm.distance.y());
265 vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
266 painter.setClipRect(vm.clipRect);
267
268 // Paint the background of the page. This includes
269 // the annotation area if that should be shown.
270 paintBackgrounds(painter, vm);
271
272 // Paint the contents of the page (shapes border).
273 painter.setRenderHint(QPainter::Antialiasing);
274 m_shapeManager->paint(painter, *(viewConverter()), false); // Paint all shapes
275 paintBorder(painter, vm);
276
277 // Paint the page decorations: shadow, etc.
278 // FIXME: This will fail because the painter is clipped to the page.
279 paintPageDecorations(painter, vm);
280
281 // Paint the grid
282 paintGrid(painter, vm);
283
284 // Paint whatever the tool wants to paint
285 m_toolProxy->paint(painter, *(viewConverter()));
286 painter.restore();
287
288 int contentArea = vm.clipRect.width() * vm.clipRect.height();
289 if (contentArea > pageContentArea) {
290 pageContentArea = contentArea;
291 }
292 }
293 }
294 else {
295
296 #if 0
297 // at the moment we're always caching at the actual zoomlevel anyway, but if we want to
298 // re-enable this distinction, the massive code duplication between these two code paths
299 // should first be removed
300 if (viewConverter()->zoom() <= m_maxZoom) { // we cache at the actual zoom level
301 #endif
302 QVector<KWViewMode::ViewMap> map =
303 m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
304 viewConverter());
305
306 foreach (KWViewMode::ViewMap vm, map) {
307
308 painter.save();
309
310 // Set up the painter to clip the part of the canvas that contains the rect.
311 painter.translate(vm.distance.x(), vm.distance.y());
312 vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
313 painter.setClipRect(vm.clipRect);
314
315 // Paint the background of the page.
316 QColor color = Qt::white;
317 #ifdef DEBUG_REPAINT
318 color = QColor(random() % 255, random() % 255, random() % 255);
319 #endif
320 painter.fillRect(vm.clipRect, QBrush(color));
321
322 // Paint the contents of the page.
323 painter.setRenderHint(QPainter::Antialiasing);
324
325 // clear the cache if the zoom changed
326 qreal zoom = viewConverter()->zoom();
327 if (m_currentZoom != zoom) {
328 m_pageCacheManager->clear();
329 m_currentZoom = zoom;
330 }
331
332 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
333
334 if (!pageCache) {
335 pageCache = m_pageCacheManager->cache(QSize(viewConverter()->documentToViewX(vm.page.width()),
336 viewConverter()->documentToViewY(vm.page.height())));
337 }
338
339 Q_ASSERT(!pageCache->cache.isEmpty());
340
341 // vm.page is in points, not view units
342 QSizeF pageSizeDocument(vm.page.width(), vm.page.height());
343 QSizeF pageSizeView = viewConverter()->documentToView(pageSizeDocument);
344
345 qreal pageTopDocument = vm.page.offsetInDocument();
346 qreal pageTopView = viewConverter()->documentToViewY(pageTopDocument);
347
348 QRectF pageRectDocument = vm.page.rect();
349 QRectF pageRectView = viewConverter()->documentToView(pageRectDocument);
350
351 // translated from the page topleft to 0,0 for our cache image
352 QRectF clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
353
354 // create exposed rects when the page is to be completely repainted.
355 // we cannot wait for the updateCanvas calls to actually tell us which parts
356 // need painting, because updateCanvas is not called when a page is done
357 // layouting.
358 if (pageCache->allExposed) {
359
360 pageCache->exposed.clear();
361 QRect rc(QPoint(0,0), pageSizeView.toSize());
362
363 const int UPDATE_WIDTH = 900;
364 const int UPDATE_HEIGHT = 128;
365
366 int row = 0;
367 int heightLeft = rc.height();
368 while (heightLeft > 0) {
369 int height = qMin(heightLeft, UPDATE_HEIGHT);
370 int column = 0;
371 int columnLeft = rc.width();
372 while (columnLeft > 0) {
373 int width = qMin(columnLeft, UPDATE_WIDTH);
374 QRect rc2(column, row, width, height);
375 pageCache->exposed << rc2;
376 columnLeft -= width;
377 column += width;
378 }
379 heightLeft -= height;
380 row += height;
381 }
382 pageCache->allExposed = false;
383 }
384
385 // There is stuff to be repainted, so collect all the repaintable
386 // rects that are in view and paint them.
387 if (!pageCache->exposed.isEmpty()) {
388 QRegion paintRegion;
389 QVector<QRect> remainingUnExposed;
390 const QVector<QRect> &exposed = pageCache->exposed;
391 for (int i = 0; i < exposed.size(); ++i) {
392
393 QRect rc = exposed.at(i);
394
395 if (rc.intersects(clipRectOnPage.toRect())) {
396 paintRegion += rc;
397 int tilex = 0, tiley = 0;
398 for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
399 int dx = pageCache->cache[i].width();
400 for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
401 QImage& img = pageCache->cache[i];
402 QRect tile(tilex, tiley, img.width(), img.height());
403 QRect toClear = tile.intersected(rc);
404 if (!toClear.isEmpty()) {
405 QPainter gc(&img);
406 gc.eraseRect(toClear.translated(-tilex, -tiley));
407 gc.end();
408 }
409 tiley += img.height();
410 }
411 tilex += dx;
412 tiley = 0;
413 }
414 }
415 else {
416 remainingUnExposed << rc;
417 }
418 }
419 pageCache->exposed = remainingUnExposed;
420 if (!paintRegion.isEmpty()) {
421 // paint the exposed regions of the page
422
423 QRect r = paintRegion.boundingRect();
424 QImage img(r.size(), QImage::Format_RGB16);
425 img.fill(0xffff);
426
427 // we paint to a small image as it is much faster the painting to the big image
428 QPainter tilePainter(&img);
429 tilePainter.setClipRect(QRect(QPoint(0,0), r.size()));
430 tilePainter.translate(-r.left(), -pageTopView - r.top());
431 tilePainter.setRenderHint(QPainter::Antialiasing);
432 shapeManager()->paint(tilePainter, *viewConverter(), false);
433
434 int tilex = 0, tiley = 0;
435 for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
436 int dx = pageCache->cache[i].width();
437 for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
438 QImage& tileImg = pageCache->cache[i];
439 QRect tile(tilex, tiley, tileImg.width(), tileImg.height());
440 QRect toPaint = tile.intersected(r);
441 if (!toPaint.isEmpty()) {
442 QPainter imagePainter(&tileImg);
443 imagePainter.drawImage(r.topLeft() - QPoint(tilex, tiley), img);
444 }
445 tiley += tileImg.height();
446 }
447 tilex += dx;
448 tiley = 0;
449 }
450 }
451 }
452 // paint from the cached page image on the original painter
453
454 int tilex = 0, tiley = 0;
455 for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
456 int dx = pageCache->cache[i].width();
457 for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
458 const QImage& cacheImage = pageCache->cache[i];
459 QRectF tile(tilex, tiley, cacheImage.width(), cacheImage.height());
460 QRectF toPaint = tile.intersected(clipRectOnPage);
461 QRectF dst = toPaint.translated(pageRectView.topLeft());
462 QRectF src = toPaint.translated(-tilex, -tiley);
463 painter.drawImage(dst, cacheImage, src);
464 tiley += cacheImage.height();
465 }
466 tilex += dx;
467 tiley = 0;
468 }
469
470 // put the cache back
471 m_pageCacheManager->insert(vm.page, pageCache);
472 // Paint the page decorations: border, shadow, etc.
473 paintPageDecorations(painter, vm);
474
475 // Paint the grid
476 paintGrid(painter, vm);
477
478 // paint whatever the tool wants to paint
479 m_toolProxy->paint(painter, *(viewConverter()));
480 painter.restore();
481
482 int contentArea = vm.clipRect.width() * vm.clipRect.height();
483 if (contentArea > pageContentArea) {
484 pageContentArea = contentArea;
485 }
486 }
487 #if 0
488 }
489 else { // we cache at 100%, but paint at the actual zoom level
490
491 KoViewConverter localViewConverter;
492 localViewConverter.setZoom(1.0);
493
494 QVector<KWViewMode::ViewMap> map =
495 m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
496 viewConverter());
497 foreach (KWViewMode::ViewMap vm, map) {
498
499 painter.save();
500
501 // Set up the painter to clip the part of the canvas that contains the rect.
502 painter.translate(vm.distance.x(), vm.distance.y());
503 vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
504 painter.setClipRect(vm.clipRect);
505
506 // Paint the background of the page.
507 QColor color = Qt::white;
508 #ifdef DEBUG_REPAINT
509 color = QColor(random() % 255, random() % 255, random() % 255);
510 #endif
511 painter.fillRect(vm.clipRect, QBrush(color));
512
513 // Paint the contents of the page.
514 painter.setRenderHint(QPainter::Antialiasing);
515
516 // clear the cache if the zoom changed
517 qreal zoom = 1.0;
518 if (m_currentZoom != zoom) {
519 m_pageCacheManager->clear();
520 m_currentZoom = zoom;
521 }
522
523 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
524 if (!pageCache) {
525 pageCache = m_pageCacheManager->cache(QSize(localViewConverter.documentToViewX(vm.page.width()),
526 localViewConverter.documentToViewY(vm.page.height())));
527 }
528 Q_ASSERT(pageCache->cache);
529
530 // vm.page is in points, not view units
531 QSizeF pageSizeDocument(vm.page.width(), vm.page.height());
532 QSizeF pageSizeView = localViewConverter.documentToView(pageSizeDocument);
533
534 qreal pageTopDocument = vm.page.offsetInDocument();
535 qreal pageTopView = localViewConverter.documentToViewY(pageTopDocument);
536
537 QRectF pageRectDocument = vm.page.rect();
538 QRectF pageRectView = localViewConverter.documentToView(pageRectDocument);
539
540 // translated from the page topleft to 0,0 for our cache image
541 QRectF documentClipRect = m_viewMode->viewToDocument(vm.clipRect, viewConverter());
542 QRectF clipRectOnPage = localViewConverter.documentToView(documentClipRect);
543 clipRectOnPage = clipRectOnPage.translated(-pageRectView.x(), -pageTopView);
544
545 // create exposed rects when the page is to be completely repainted.
546 // we cannot wait for the updateCanvas calls to actually tell us which parts
547 // need painting, because updateCanvas is not called when a page is done
548 // layouting.
549 if (pageCache->allExposed) {
550
551 pageCache->exposed.clear();
552 QRect rc(QPoint(0,0), pageSizeView.toSize());
553
554 const int UPDATE_SIZE = 64; //pixels
555
556 if (rc.height() < UPDATE_SIZE) {
557 pageCache->exposed << rc;
558 }
559 else {
560 int row = 0;
561 int hleft = rc.height();
562 int w = rc.width();
563 while (hleft > 0) {
564 QRect rc2(0, row, w, qMin(hleft, UPDATE_SIZE));
565 pageCache->exposed << rc2;
566 hleft -= UPDATE_SIZE;
567 row += UPDATE_SIZE;
568 }
569 }
570 pageCache->allExposed = false;
571 }
572
573 // There is stuff to be repainted, so collect all the repaintable
574 // rects that are in view and paint them.
575 if (!pageCache->exposed.isEmpty()) {
576 QRegion paintRegion;
577 QVector<QRect> remainingUnExposed;
578 const QVector<QRect> &exposed = pageCache->exposed;
579 for (int i = 0; i < exposed.size(); ++i) {
580
581 QRect rc = exposed.at(i);
582
583 if (rc.intersects(clipRectOnPage.toRect())) {
584 paintRegion += rc;
585 QPainter gc(pageCache->cache);
586 gc.eraseRect(rc);
587 gc.end();
588 }
589 else {
590 remainingUnExposed << rc;
591 }
592 }
593
594 pageCache->exposed = remainingUnExposed;
595
596 // paint the exposed regions of the page
597 QPainter gc(pageCache->cache);
598 gc.translate(0, -pageTopView);
599 gc.setClipRegion(paintRegion.translated(0, pageTopView));
600
601 // paint into the cache
602 shapeManager()->paint(gc, localViewConverter, false);
603 }
604 QImage copy = pageCache->cache->copy(clipRectOnPage.toRect());
605
606 // Now calculate where to paint pour stuff
607 pageTopView = viewConverter()->documentToViewY(pageTopDocument);
608 pageRectView = viewConverter()->documentToView(pageRectDocument);
609 clipRectOnPage = viewConverter()->documentToView(documentClipRect);
610 clipRectOnPage = clipRectOnPage.translated(-pageRectView.x(), -pageTopView);
611
612 copy = copy.scaled(clipRectOnPage.width(), clipRectOnPage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
613
614 // paint from the cached page image on the original painter.
615 QRect dst = QRect(pageRectView.x() + clipRectOnPage.x(),
616 pageRectView.y() + clipRectOnPage.y(),
617 clipRectOnPage.width(),
618 clipRectOnPage.height());
619
620 painter.drawImage(dst.x(), dst.y(), copy, 0, 0, copy.width(), copy.height());
621 painter.restore();
622
623 // put the cache back
624 m_pageCacheManager->insert(vm.page, pageCache);
625
626 // Paint the page decorations: border, shadow, etc.
627 paintPageDecorations(painter, vm);
628
629 // Paint the grid
630 paintGrid(painter, vm);
631
632 // paint whatever the tool wants to paint
633 m_toolProxy->paint(painter, *(viewConverter()));
634
635
636 int contentArea = qRound(vm.clipRect.width() * vm.clipRect.height());
637 if (contentArea > pageContentArea) {
638 pageContentArea = contentArea;
639 }
640 }
641 }
642 #endif
643 }
644 } else {
645 // TODO paint the main-text-flake directly
646 warnWordsUI << "Non-page painting not implemented yet!";
647 }
648 }
649
updateCanvas(const QRectF & rc)650 void KWCanvasBase::updateCanvas(const QRectF &rc)
651 {
652 if (!m_cacheEnabled) { // no caching
653 QRectF zoomedRect = m_viewMode->documentToView(rc, viewConverter());
654 QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect,
655 viewConverter());
656 foreach (KWViewMode::ViewMap vm, map) {
657 vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
658 QRect finalClip((int)(vm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
659 (int)(vm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
660 vm.clipRect.width(), vm.clipRect.height());
661 updateCanvasInternal(finalClip);
662 }
663 }
664 else { // Caching at the actual zoom level
665 if (viewConverter()->zoom() <= m_maxZoom) {
666 QRectF zoomedRect = m_viewMode->documentToView(rc, viewConverter());
667 QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect,
668 viewConverter());
669 foreach (KWViewMode::ViewMap vm, map) {
670 vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
671 QRect finalClip((int)(vm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
672 (int)(vm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
673 vm.clipRect.width(), vm.clipRect.height());
674
675 if (!m_pageCacheManager) {
676 // no pageCacheManager, so create one for the current view. This happens only once!
677 // so on zoom change, we don't re-pre-generate weight/zoom images.
678 m_pageCacheManager = new KWPageCacheManager(m_cacheSize);
679 }
680
681 if (m_currentZoom != viewConverter()->zoom()) {
682 m_currentZoom = viewConverter()->zoom();
683 m_pageCacheManager->clear();
684 }
685
686 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
687 if (pageCache) {
688 //if (rc.isNull()) {
689 pageCache->allExposed = true;
690 pageCache->exposed.clear();
691 #if 0
692 }
693 else {
694 qreal pageTopDocument = vm.page.offsetInDocument();
695 qreal pageTopView = viewConverter()->documentToViewY(pageTopDocument);
696 QRectF pageRectDocument = vm.page.rect();
697 QRectF pageRectView = viewConverter()->documentToView(pageRectDocument);
698
699 // translated from the page topleft to 0,0 for our cache image
700 QRect clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
701
702 pageCache->exposed.append(clipRectOnPage);
703 }
704 #endif
705 m_pageCacheManager->insert(vm.page, pageCache);
706 }
707 updateCanvasInternal(finalClip);
708 }
709 }
710 else { // Cache at 100%, but update the canvas at the actual zoom level
711
712 KoViewConverter localViewConverter;
713 localViewConverter.setZoom(1.0);
714
715 // Viewmap scaled to 100% for calculating which parts of the cached page image
716 // are exposed.
717 QRectF zoomedRect = m_viewMode->documentToView(rc, &localViewConverter);
718 QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect, &localViewConverter);
719
720 // Viewmap scaled to the actual size of the canvas, so we know which areas to call
721 // update() for.
722 zoomedRect = m_viewMode->documentToView(rc, viewConverter());
723 QVector<KWViewMode::ViewMap> actualMap = m_viewMode->mapExposedRects(zoomedRect, viewConverter());
724
725 Q_ASSERT(actualMap.size() == map.size());
726
727 for (int index = 0; index < map.size(); ++index) {
728
729 Q_ASSERT(index < map.size());
730 KWViewMode::ViewMap vm = map.at(index);
731 vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
732
733 Q_ASSERT(index < actualMap.size());
734 KWViewMode::ViewMap actualVm = actualMap.at(index);
735 actualVm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
736 QRect finalClip = QRect((int)(actualVm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
737 (int)(actualVm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
738 actualVm.clipRect.width(),
739 actualVm.clipRect.height());
740
741 if (!m_pageCacheManager) {
742 // no pageCacheManager, so create one for the current view. This happens only once!
743 // so on zoom change, we don't re-pre-generate weight/zoom images.
744 m_pageCacheManager = new KWPageCacheManager(m_cacheSize);
745 }
746
747 if (m_currentZoom != 1.0) {
748 m_pageCacheManager->clear();
749 m_currentZoom = 1.0;
750 }
751
752 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
753 if (pageCache) {
754 //if (rc.isNull()) {
755 pageCache->allExposed = true;
756 pageCache->exposed.clear();
757 #if 0
758 }
759 else {
760 qreal pageTopDocument = vm.page.offsetInDocument();
761 qreal pageTopView = localViewConverter.documentToViewY(pageTopDocument);
762 QRectF pageRectDocument = vm.page.rect();
763 QRectF pageRectView = localViewConverter.documentToView(pageRectDocument);
764
765 // translated from the page topleft to 0,0 for our cache image
766 QRect clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
767
768 pageCache->exposed.append(clipRectOnPage);
769 }
770 #endif
771 m_pageCacheManager->insert(vm.page, pageCache);
772 }
773 updateCanvasInternal(finalClip);
774 }
775 }
776 }
777 }
778
779
viewConverter() const780 KoViewConverter *KWCanvasBase::viewConverter() const
781 {
782 return m_viewConverter;
783 }
784
setCacheEnabled(bool enabled,int cacheSize,qreal maxZoom)785 void KWCanvasBase::setCacheEnabled(bool enabled, int cacheSize, qreal maxZoom)
786 {
787 if ((!m_pageCacheManager && enabled) || (m_cacheSize != cacheSize)) {
788 delete m_pageCacheManager;
789 m_pageCacheManager = new KWPageCacheManager(cacheSize);
790 }
791 m_cacheEnabled = enabled;
792 m_cacheSize = cacheSize;
793 m_maxZoom = maxZoom;
794 }
795
documentOffset() const796 QPoint KWCanvasBase::documentOffset() const
797 {
798 return m_documentOffset;
799 }
800
801 const qreal KWCanvasBase::AnnotationAreaWidth = 200.0; // only static const integral data members can be initialized within a class
802