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