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

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2002-2006 David Faure <faure@kde.org>
0003  * Copyright (C) 2005-2006 Thomas Zander <zander@kde.org>
0004  * Copyright (C) 2009 Inge Wallin <inge@lysator.liu.se>
0005  * Copyright (C) 2010-2011 Boudewijn Rempt <boud@kogmbh.com>
0006  * Copyright (C) 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  */
0023 #include "KWCanvasBase.h"
0024 
0025 // words includes
0026 #include "KWCanvas.h"
0027 #include "KWGui.h"
0028 #include "KWViewMode.h"
0029 #include "KWPage.h"
0030 #include "KWPageCacheManager.h"
0031 #include "frames/KWFrameLayout.h"
0032 #include "WordsDebug.h"
0033 
0034 // calligra libs includes
0035 #include <KoShapeManager.h>
0036 #include <KoPointerEvent.h>
0037 #include <KoCanvasController.h>
0038 #include <KoToolProxy.h>
0039 #include <KoGridData.h>
0040 #include <KoGuidesData.h>
0041 #include <KoShape.h>
0042 #include <KoViewConverter.h>
0043 #include <KoUnit.h>
0044 
0045 #include <KoAnnotationLayoutManager.h>
0046 
0047 // Qt
0048 #include <QBrush>
0049 #include <QPainter>
0050 #include <QPainterPath>
0051 #include <QThread>
0052 
0053 #include <sys/time.h>
0054 
0055 //#define DEBUG_REPAINT
0056 
0057 
0058 KWCanvasBase::KWCanvasBase(KWDocument *document, QObject *parent)
0059     : KoCanvasBase(document),
0060       m_document(document),
0061       m_shapeManager(0),
0062       m_toolProxy(0),
0063       m_viewMode(0),
0064       m_viewConverter(0),
0065       m_showAnnotations(false),
0066       m_cacheEnabled(false),
0067       m_currentZoom(0.0),
0068       m_maxZoom(2.0),
0069       m_pageCacheManager(0)
0070 {
0071     m_shapeManager = new KoShapeManager(this);
0072     m_toolProxy = new KoToolProxy(this, parent);
0073     //setCacheEnabled(true);
0074 }
0075 
0076 KWCanvasBase::~KWCanvasBase()
0077 {
0078     delete m_shapeManager;
0079     delete m_viewMode;
0080     delete m_pageCacheManager;
0081 }
0082 
0083 void KWCanvasBase::gridSize(qreal *horizontal, qreal *vertical) const
0084 {
0085     *horizontal = m_document->gridData().gridX();
0086     *vertical = m_document->gridData().gridY();
0087 }
0088 
0089 void KWCanvasBase::addCommand(KUndo2Command *command)
0090 {
0091     m_document->addCommand(command);
0092 }
0093 
0094 KoShapeManager *KWCanvasBase::shapeManager() const
0095 {
0096     return m_shapeManager;
0097 }
0098 
0099 KoUnit KWCanvasBase::unit() const
0100 {
0101     return m_document->unit();
0102 }
0103 
0104 KoToolProxy *KWCanvasBase::toolProxy() const
0105 {
0106     return m_toolProxy;
0107 }
0108 
0109 void KWCanvasBase::clipToDocument(const KoShape *shape, QPointF &move) const
0110 {
0111     Q_ASSERT(shape);
0112     const QPointF absPos = shape->absolutePosition();
0113     const QPointF destination = absPos + move;
0114     qreal bottomOfPage = 0.0;
0115     KWPage page;
0116     foreach (const KWPage &p, m_document->pageManager()->pages()) {
0117         bottomOfPage += p.height();
0118         if (bottomOfPage >= absPos.y())
0119             page = p;
0120         if (bottomOfPage >= destination.y()) {
0121             page = p;
0122             break;
0123         }
0124     }
0125     if (!page.isValid()) { // shape was not in any page to begin with, can't propose anything sane...
0126         move.setX(0);
0127         move.setY(0);
0128         return;
0129     }
0130     QRectF pageRect(page.rect().adjusted(5, 5, -5, -5));
0131     QPainterPath path(shape->absoluteTransformation(0).map(shape->outline()));
0132     QRectF shapeBounds = path.boundingRect();
0133     shapeBounds.moveTopLeft(shapeBounds.topLeft() + move);
0134     if (!shapeBounds.intersects(pageRect)) {
0135         if (shapeBounds.left() > pageRect.right()) // need to move to the left some
0136             move.setX(move.x() + (pageRect.right() - shapeBounds.left()));
0137         else if (shapeBounds.right() < pageRect.left()) // need to move to the right some
0138             move.setX(move.x() + pageRect.left() - shapeBounds.right());
0139 
0140         if (shapeBounds.top() > pageRect.bottom()) // need to move up some
0141             move.setY(move.y() + (pageRect.bottom() - shapeBounds.top()));
0142         else if (shapeBounds.bottom() < pageRect.top()) // need to move down some
0143             move.setY(move.y() + pageRect.top() - shapeBounds.bottom());
0144     }
0145 
0146     // Also make sure any anchoring restrictions are adhered to
0147     KWFrameLayout::proposeShapeMove(shape, move, page);
0148 }
0149 
0150 KoGuidesData *KWCanvasBase::guidesData()
0151 {
0152     return &m_document->guidesData();
0153 }
0154 
0155 KWDocument *KWCanvasBase::document() const
0156 {
0157     return m_document;
0158 }
0159 
0160 KWViewMode *KWCanvasBase::viewMode() const
0161 {
0162     return m_viewMode;
0163 }
0164 
0165 void KWCanvasBase::ensureVisible(const QRectF &rect)
0166 {
0167     QRectF viewRect = m_viewMode->documentToView(rect, m_viewConverter);
0168     canvasController()->ensureVisible(viewRect);
0169 }
0170 
0171 bool KWCanvasBase::showAnnotations() const
0172 {
0173     return m_showAnnotations;
0174 }
0175 
0176 void KWCanvasBase::setShowAnnotations(bool doShow)
0177 {
0178     m_showAnnotations = doShow;
0179 }
0180 
0181 void KWCanvasBase::paintBackgrounds(QPainter &painter, KWViewMode::ViewMap &viewMap)
0182 {
0183     // Paint the page.
0184 
0185     QColor color = Qt::white;
0186 #ifdef DEBUG_REPAINT
0187     color = QColor(random() % 255, random() % 255, random() % 255);
0188 #endif
0189     painter.fillRect(viewMap.clipRect, QBrush(color));
0190 
0191     // Paint the annotation area if that is turned on.
0192     if (m_showAnnotations) {
0193         color = Qt::cyan;
0194         QRect annotationRect(m_viewMode->contentsSize().width(), 0,
0195                              AnnotationAreaWidth, m_viewMode->contentsSize().height());
0196         QRectF viewRect(m_viewMode->documentToView(annotationRect, m_viewConverter));
0197         painter.fillRect(viewRect, QBrush(color));
0198 
0199 
0200         if (m_document->annotationLayoutManager())
0201             m_document->annotationLayoutManager()->paintConnections(painter);
0202     }
0203 }
0204 
0205 void KWCanvasBase::paintPageDecorations(QPainter &painter, KWViewMode::ViewMap &viewMap)
0206 {
0207     // We have no page shadows yet.
0208     Q_UNUSED(painter);
0209     Q_UNUSED(viewMap);
0210 }
0211 
0212 void KWCanvasBase::paintBorder(QPainter &painter, KWViewMode::ViewMap &viewMap)
0213 {
0214     painter.save();
0215 
0216     const QRectF       pageRect = viewMap.page.rect();
0217     const KoPageLayout pageLayout = viewMap.page.pageStyle().pageLayout();
0218 
0219     qreal zoomX, zoomY;
0220     viewConverter()->zoom(&zoomX, &zoomY);
0221     painter.scale(zoomX, zoomY);
0222 
0223     QPointF topLeftCorner = QPointF(pageRect.topLeft() + QPointF(pageLayout.leftMargin,
0224                                                                  pageLayout.topMargin));
0225     QPointF bottomRightCorner = QPointF(pageRect.bottomRight() + QPointF(-pageLayout.rightMargin,
0226                                                                          -pageLayout.bottomMargin));
0227     QRectF borderRect = QRectF(topLeftCorner, bottomRightCorner);
0228     pageLayout.border.paint(painter, borderRect);
0229 
0230     painter.restore();
0231 }
0232 
0233 void KWCanvasBase::paintGrid(QPainter &painter, KWViewMode::ViewMap &vm)
0234 {
0235     painter.save();
0236     painter.translate(-vm.distance.x(), -vm.distance.y());
0237     painter.setRenderHint(QPainter::Antialiasing, false);
0238     const QRectF clipRect = viewConverter()->viewToDocument(vm.clipRect);
0239     document()->gridData().paintGrid(painter, *(viewConverter()), clipRect);
0240     document()->guidesData().paintGuides(painter, *(viewConverter()), clipRect);
0241     painter.restore();
0242 }
0243 
0244 void KWCanvasBase::paint(QPainter &painter, const QRectF &paintRect)
0245 {
0246     painter.translate(-m_documentOffset);
0247 
0248     static int iteration = 0;
0249     iteration++;
0250 
0251     if (m_viewMode->hasPages()) {
0252 
0253         int pageContentArea = 0;
0254         if (!m_cacheEnabled || !m_pageCacheManager) { // no caching, simple case
0255 
0256             QVector<KWViewMode::ViewMap> map =
0257                     m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
0258                                                 viewConverter());
0259             foreach (KWViewMode::ViewMap vm, map) {
0260                 painter.save();
0261 
0262                 // Set up the painter to clip the part of the canvas that contains the rect.
0263                 // FIXME: The viewmap must also take into account the annotation area
0264                 painter.translate(vm.distance.x(), vm.distance.y());
0265                 vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
0266                 painter.setClipRect(vm.clipRect);
0267 
0268                 // Paint the background of the page.  This includes
0269                 // the annotation area if that should be shown.
0270                 paintBackgrounds(painter, vm);
0271 
0272                 // Paint the contents of the page (shapes border).
0273                 painter.setRenderHint(QPainter::Antialiasing);
0274                 m_shapeManager->paint(painter, *(viewConverter()), false); // Paint all shapes
0275                 paintBorder(painter, vm);
0276 
0277                 // Paint the page decorations: shadow, etc.
0278                 // FIXME: This will fail because the painter is clipped to the page.
0279                 paintPageDecorations(painter, vm);
0280 
0281                 // Paint the grid
0282                 paintGrid(painter, vm);
0283 
0284                 // Paint whatever the tool wants to paint
0285                 m_toolProxy->paint(painter, *(viewConverter()));
0286                 painter.restore();
0287 
0288                 int contentArea = vm.clipRect.width() * vm.clipRect.height();
0289                 if (contentArea > pageContentArea) {
0290                     pageContentArea = contentArea;
0291                 }
0292             }
0293         }
0294         else {
0295 
0296 #if 0
0297     // at the moment we're always caching at the actual zoomlevel anyway, but if we want to
0298     // re-enable this distinction, the massive code duplication between these two code paths
0299     // should first be removed
0300             if (viewConverter()->zoom() <= m_maxZoom) { // we cache at the actual zoom level
0301 #endif
0302                 QVector<KWViewMode::ViewMap> map =
0303                         m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
0304                                                     viewConverter());
0305 
0306                 foreach (KWViewMode::ViewMap vm, map) {
0307 
0308                     painter.save();
0309 
0310                     // Set up the painter to clip the part of the canvas that contains the rect.
0311                     painter.translate(vm.distance.x(), vm.distance.y());
0312                     vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
0313                     painter.setClipRect(vm.clipRect);
0314 
0315                     // Paint the background of the page.
0316                     QColor color = Qt::white;
0317 #ifdef DEBUG_REPAINT
0318                     color = QColor(random() % 255, random() % 255, random() % 255);
0319 #endif
0320                     painter.fillRect(vm.clipRect, QBrush(color));
0321 
0322                     // Paint the contents of the page.
0323                     painter.setRenderHint(QPainter::Antialiasing);
0324 
0325                     // clear the cache if the zoom changed
0326                     qreal zoom = viewConverter()->zoom();
0327                     if (m_currentZoom != zoom) {
0328                         m_pageCacheManager->clear();
0329                         m_currentZoom = zoom;
0330                     }
0331 
0332                     KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
0333 
0334                     if (!pageCache) {
0335                         pageCache = m_pageCacheManager->cache(QSize(viewConverter()->documentToViewX(vm.page.width()),
0336                                                                     viewConverter()->documentToViewY(vm.page.height())));
0337                     }
0338 
0339                     Q_ASSERT(!pageCache->cache.isEmpty());
0340 
0341                     // vm.page is in points, not view units
0342                     QSizeF pageSizeDocument(vm.page.width(), vm.page.height());
0343                     QSizeF pageSizeView = viewConverter()->documentToView(pageSizeDocument);
0344 
0345                     qreal  pageTopDocument = vm.page.offsetInDocument();
0346                     qreal  pageTopView = viewConverter()->documentToViewY(pageTopDocument);
0347 
0348                     QRectF pageRectDocument = vm.page.rect();
0349                     QRectF pageRectView = viewConverter()->documentToView(pageRectDocument);
0350 
0351                     // translated from the page topleft to 0,0 for our cache image
0352                     QRectF clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
0353 
0354                     // create exposed rects when the page is to be completely repainted.
0355                     // we cannot wait for the updateCanvas calls to actually tell us which parts
0356                     // need painting, because updateCanvas is not called when a page is done
0357                     // layouting.
0358                     if (pageCache->allExposed)  {
0359 
0360                         pageCache->exposed.clear();
0361                         QRect rc(QPoint(0,0), pageSizeView.toSize());
0362 
0363                         const int UPDATE_WIDTH = 900;
0364                         const int UPDATE_HEIGHT = 128;
0365 
0366                         int row = 0;
0367                         int heightLeft = rc.height();
0368                         while (heightLeft > 0) {
0369                             int height = qMin(heightLeft, UPDATE_HEIGHT);
0370                             int column = 0;
0371                             int columnLeft = rc.width();
0372                             while (columnLeft > 0) {
0373                                 int width = qMin(columnLeft, UPDATE_WIDTH);
0374                                 QRect rc2(column, row, width, height);
0375                                 pageCache->exposed << rc2;
0376                                 columnLeft -= width;
0377                                 column += width;
0378                             }
0379                             heightLeft -= height;
0380                             row += height;
0381                         }
0382                         pageCache->allExposed = false;
0383                     }
0384 
0385                     // There is stuff to be repainted, so collect all the repaintable
0386                     // rects that are in view and paint them.
0387                     if (!pageCache->exposed.isEmpty()) {
0388                         QRegion paintRegion;
0389                         QVector<QRect> remainingUnExposed;
0390                         const QVector<QRect> &exposed = pageCache->exposed;
0391                         for (int i = 0; i < exposed.size(); ++i) {
0392 
0393                             QRect rc = exposed.at(i);
0394 
0395                             if (rc.intersects(clipRectOnPage.toRect())) {
0396                                 paintRegion += rc;
0397                                 int tilex = 0, tiley = 0;
0398                                 for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
0399                                     int dx = pageCache->cache[i].width();
0400                                     for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
0401                                         QImage& img = pageCache->cache[i];
0402                                         QRect tile(tilex, tiley, img.width(), img.height());
0403                                         QRect toClear = tile.intersected(rc);
0404                                         if (!toClear.isEmpty()) {
0405                                             QPainter gc(&img);
0406                                             gc.eraseRect(toClear.translated(-tilex, -tiley));
0407                                             gc.end();
0408                                         }
0409                                         tiley += img.height();
0410                                     }
0411                                     tilex += dx;
0412                                     tiley = 0;
0413                                 }
0414                             }
0415                             else {
0416                                 remainingUnExposed << rc;
0417                             }
0418                         }
0419                         pageCache->exposed = remainingUnExposed;
0420                         if (!paintRegion.isEmpty()) {
0421                             // paint the exposed regions of the page
0422 
0423                             QRect r = paintRegion.boundingRect();
0424                             QImage img(r.size(), QImage::Format_RGB16);
0425                             img.fill(0xffff);
0426 
0427                             // we paint to a small image as it is much faster the painting to the big image
0428                             QPainter tilePainter(&img);
0429                             tilePainter.setClipRect(QRect(QPoint(0,0), r.size()));
0430                             tilePainter.translate(-r.left(), -pageTopView - r.top());
0431                             tilePainter.setRenderHint(QPainter::Antialiasing);
0432                             shapeManager()->paint(tilePainter, *viewConverter(), false);
0433 
0434                             int tilex = 0, tiley = 0;
0435                             for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
0436                                 int dx = pageCache->cache[i].width();
0437                                 for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
0438                                     QImage& tileImg = pageCache->cache[i];
0439                                     QRect tile(tilex, tiley, tileImg.width(), tileImg.height());
0440                                     QRect toPaint = tile.intersected(r);
0441                                     if (!toPaint.isEmpty()) {
0442                                         QPainter imagePainter(&tileImg);
0443                                         imagePainter.drawImage(r.topLeft() - QPoint(tilex, tiley), img);
0444                                     }
0445                                     tiley += tileImg.height();
0446                                 }
0447                                 tilex += dx;
0448                                 tiley = 0;
0449                             }
0450                         }
0451                     }
0452                     // paint from the cached page image on the original painter
0453 
0454                     int tilex = 0, tiley = 0;
0455                     for (int x = 0, i = 0; x < pageCache->m_tilesx; ++x) {
0456                         int dx = pageCache->cache[i].width();
0457                         for (int y = 0; y < pageCache->m_tilesy; ++y, ++i) {
0458                             const QImage& cacheImage = pageCache->cache[i];
0459                             QRectF tile(tilex, tiley, cacheImage.width(), cacheImage.height());
0460                             QRectF toPaint = tile.intersected(clipRectOnPage);
0461                             QRectF dst = toPaint.translated(pageRectView.topLeft());
0462                             QRectF src = toPaint.translated(-tilex, -tiley);
0463                             painter.drawImage(dst, cacheImage, src);
0464                             tiley += cacheImage.height();
0465                         }
0466                         tilex += dx;
0467                         tiley = 0;
0468                     }
0469 
0470                     // put the cache back
0471                     m_pageCacheManager->insert(vm.page, pageCache);
0472                     // Paint the page decorations: border, shadow, etc.
0473                     paintPageDecorations(painter, vm);
0474 
0475                     // Paint the grid
0476                     paintGrid(painter, vm);
0477 
0478                     // paint whatever the tool wants to paint
0479                     m_toolProxy->paint(painter, *(viewConverter()));
0480                     painter.restore();
0481 
0482                     int contentArea = vm.clipRect.width() * vm.clipRect.height();
0483                     if (contentArea > pageContentArea) {
0484                         pageContentArea = contentArea;
0485                     }
0486                 }
0487 #if 0
0488             }
0489             else { // we cache at 100%, but paint at the actual zoom level
0490 
0491                 KoViewConverter localViewConverter;
0492                 localViewConverter.setZoom(1.0);
0493 
0494                 QVector<KWViewMode::ViewMap> map =
0495                         m_viewMode->mapExposedRects(paintRect.translated(m_documentOffset),
0496                                                     viewConverter());
0497                 foreach (KWViewMode::ViewMap vm, map) {
0498 
0499                     painter.save();
0500 
0501                     // Set up the painter to clip the part of the canvas that contains the rect.
0502                     painter.translate(vm.distance.x(), vm.distance.y());
0503                     vm.clipRect = vm.clipRect.adjusted(-1, -1, 1, 1);
0504                     painter.setClipRect(vm.clipRect);
0505 
0506                     // Paint the background of the page.
0507                     QColor color = Qt::white;
0508 #ifdef DEBUG_REPAINT
0509                     color = QColor(random() % 255, random() % 255, random() % 255);
0510 #endif
0511                     painter.fillRect(vm.clipRect, QBrush(color));
0512 
0513                     // Paint the contents of the page.
0514                     painter.setRenderHint(QPainter::Antialiasing);
0515 
0516                     // clear the cache if the zoom changed
0517                     qreal zoom = 1.0;
0518                     if (m_currentZoom != zoom) {
0519                         m_pageCacheManager->clear();
0520                         m_currentZoom = zoom;
0521                     }
0522 
0523                     KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
0524                     if (!pageCache) {
0525                         pageCache = m_pageCacheManager->cache(QSize(localViewConverter.documentToViewX(vm.page.width()),
0526                                                                     localViewConverter.documentToViewY(vm.page.height())));
0527                     }
0528                     Q_ASSERT(pageCache->cache);
0529 
0530                     // vm.page is in points, not view units
0531                     QSizeF pageSizeDocument(vm.page.width(), vm.page.height());
0532                     QSizeF pageSizeView = localViewConverter.documentToView(pageSizeDocument);
0533 
0534                     qreal  pageTopDocument = vm.page.offsetInDocument();
0535                     qreal  pageTopView = localViewConverter.documentToViewY(pageTopDocument);
0536 
0537                     QRectF pageRectDocument = vm.page.rect();
0538                     QRectF pageRectView = localViewConverter.documentToView(pageRectDocument);
0539 
0540                     // translated from the page topleft to 0,0 for our cache image
0541                     QRectF documentClipRect = m_viewMode->viewToDocument(vm.clipRect, viewConverter());
0542                     QRectF clipRectOnPage = localViewConverter.documentToView(documentClipRect);
0543                     clipRectOnPage = clipRectOnPage.translated(-pageRectView.x(), -pageTopView);
0544 
0545                     // create exposed rects when the page is to be completely repainted.
0546                     // we cannot wait for the updateCanvas calls to actually tell us which parts
0547                     // need painting, because updateCanvas is not called when a page is done
0548                     // layouting.
0549                     if (pageCache->allExposed)  {
0550 
0551                         pageCache->exposed.clear();
0552                         QRect rc(QPoint(0,0), pageSizeView.toSize());
0553 
0554                         const int UPDATE_SIZE = 64; //pixels
0555 
0556                         if (rc.height() < UPDATE_SIZE) {
0557                             pageCache->exposed << rc;
0558                         }
0559                         else {
0560                             int row = 0;
0561                             int hleft = rc.height();
0562                             int w = rc.width();
0563                             while (hleft > 0) {
0564                                 QRect rc2(0, row, w, qMin(hleft, UPDATE_SIZE));
0565                                 pageCache->exposed << rc2;
0566                                 hleft -= UPDATE_SIZE;
0567                                 row += UPDATE_SIZE;
0568                             }
0569                         }
0570                         pageCache->allExposed = false;
0571                     }
0572 
0573                     // There is stuff to be repainted, so collect all the repaintable
0574                     // rects that are in view and paint them.
0575                     if (!pageCache->exposed.isEmpty()) {
0576                         QRegion paintRegion;
0577                         QVector<QRect> remainingUnExposed;
0578                         const QVector<QRect> &exposed = pageCache->exposed;
0579                         for (int i = 0; i < exposed.size(); ++i) {
0580 
0581                             QRect rc = exposed.at(i);
0582 
0583                             if (rc.intersects(clipRectOnPage.toRect())) {
0584                                 paintRegion += rc;
0585                                 QPainter gc(pageCache->cache);
0586                                 gc.eraseRect(rc);
0587                                 gc.end();
0588                             }
0589                             else {
0590                                 remainingUnExposed << rc;
0591                             }
0592                         }
0593 
0594                         pageCache->exposed = remainingUnExposed;
0595 
0596                         // paint the exposed regions of the page
0597                         QPainter gc(pageCache->cache);
0598                         gc.translate(0, -pageTopView);
0599                         gc.setClipRegion(paintRegion.translated(0, pageTopView));
0600 
0601                         // paint into the cache
0602                         shapeManager()->paint(gc, localViewConverter, false);
0603                     }
0604                     QImage copy = pageCache->cache->copy(clipRectOnPage.toRect());
0605 
0606                     // Now calculate where to paint pour stuff
0607                     pageTopView = viewConverter()->documentToViewY(pageTopDocument);
0608                     pageRectView = viewConverter()->documentToView(pageRectDocument);
0609                     clipRectOnPage = viewConverter()->documentToView(documentClipRect);
0610                     clipRectOnPage = clipRectOnPage.translated(-pageRectView.x(), -pageTopView);
0611 
0612                     copy = copy.scaled(clipRectOnPage.width(), clipRectOnPage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0613 
0614                     // paint from the cached page image on the original painter.
0615                     QRect dst = QRect(pageRectView.x() + clipRectOnPage.x(),
0616                                       pageRectView.y() + clipRectOnPage.y(),
0617                                       clipRectOnPage.width(),
0618                                       clipRectOnPage.height());
0619 
0620                     painter.drawImage(dst.x(), dst.y(), copy, 0, 0, copy.width(), copy.height());
0621                     painter.restore();
0622 
0623                     // put the cache back
0624                     m_pageCacheManager->insert(vm.page, pageCache);
0625 
0626                     // Paint the page decorations: border, shadow, etc.
0627                     paintPageDecorations(painter, vm);
0628 
0629                     // Paint the grid
0630                     paintGrid(painter, vm);
0631 
0632                     // paint whatever the tool wants to paint
0633                     m_toolProxy->paint(painter, *(viewConverter()));
0634 
0635 
0636                     int contentArea = qRound(vm.clipRect.width() * vm.clipRect.height());
0637                     if (contentArea > pageContentArea) {
0638                         pageContentArea = contentArea;
0639                     }
0640                 }
0641             }
0642 #endif
0643         }
0644     } else {
0645         // TODO paint the main-text-flake directly
0646         warnWordsUI << "Non-page painting not implemented yet!";
0647     }
0648 }
0649 
0650 void KWCanvasBase::updateCanvas(const QRectF &rc)
0651 {
0652     if (!m_cacheEnabled) { // no caching
0653         QRectF zoomedRect = m_viewMode->documentToView(rc, viewConverter());
0654         QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect,
0655                                                                      viewConverter());
0656         foreach (KWViewMode::ViewMap vm, map) {
0657             vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
0658             QRect finalClip((int)(vm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
0659                             (int)(vm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
0660                             vm.clipRect.width(), vm.clipRect.height());
0661             updateCanvasInternal(finalClip);
0662         }
0663     }
0664     else { // Caching at the actual zoom level
0665         if (viewConverter()->zoom() <= m_maxZoom) {
0666             QRectF zoomedRect = m_viewMode->documentToView(rc, viewConverter());
0667             QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect,
0668                                                                          viewConverter());
0669             foreach (KWViewMode::ViewMap vm, map) {
0670                 vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
0671                 QRect finalClip((int)(vm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
0672                                 (int)(vm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
0673                                 vm.clipRect.width(), vm.clipRect.height());
0674 
0675                 if (!m_pageCacheManager) {
0676                     // no pageCacheManager, so create one for the current view. This happens only once!
0677                     // so on zoom change, we don't re-pre-generate weight/zoom images.
0678                     m_pageCacheManager = new KWPageCacheManager(m_cacheSize);
0679                 }
0680 
0681                 if (m_currentZoom != viewConverter()->zoom()) {
0682                     m_currentZoom = viewConverter()->zoom();
0683                     m_pageCacheManager->clear();
0684                 }
0685 
0686                 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
0687                 if (pageCache) {
0688                     //if (rc.isNull()) {
0689                         pageCache->allExposed = true;
0690                         pageCache->exposed.clear();
0691 #if 0
0692                     }
0693                     else {
0694                         qreal  pageTopDocument = vm.page.offsetInDocument();
0695                         qreal  pageTopView = viewConverter()->documentToViewY(pageTopDocument);
0696                         QRectF pageRectDocument = vm.page.rect();
0697                         QRectF pageRectView = viewConverter()->documentToView(pageRectDocument);
0698 
0699                         // translated from the page topleft to 0,0 for our cache image
0700                         QRect clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
0701 
0702                         pageCache->exposed.append(clipRectOnPage);
0703                     }
0704 #endif
0705                     m_pageCacheManager->insert(vm.page, pageCache);
0706                 }
0707                 updateCanvasInternal(finalClip);
0708             }
0709         }
0710         else { // Cache at 100%, but update the canvas at the actual zoom level
0711 
0712             KoViewConverter localViewConverter;
0713             localViewConverter.setZoom(1.0);
0714 
0715             // Viewmap scaled to 100% for calculating which parts of the cached page image
0716             // are exposed.
0717             QRectF zoomedRect = m_viewMode->documentToView(rc, &localViewConverter);
0718             QVector<KWViewMode::ViewMap> map = m_viewMode->mapExposedRects(zoomedRect, &localViewConverter);
0719 
0720             // Viewmap scaled to the actual size of the canvas, so we know which areas to call
0721             // update() for.
0722             zoomedRect = m_viewMode->documentToView(rc, viewConverter());
0723             QVector<KWViewMode::ViewMap> actualMap = m_viewMode->mapExposedRects(zoomedRect, viewConverter());
0724 
0725             Q_ASSERT(actualMap.size() == map.size());
0726 
0727             for (int index = 0; index < map.size(); ++index) {
0728 
0729                 Q_ASSERT(index < map.size());
0730                 KWViewMode::ViewMap vm = map.at(index);
0731                 vm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
0732 
0733                 Q_ASSERT(index < actualMap.size());
0734                 KWViewMode::ViewMap actualVm = actualMap.at(index);
0735                 actualVm.clipRect.adjust(-2, -2, 2, 2); // grow for anti-aliasing
0736                 QRect finalClip = QRect((int)(actualVm.clipRect.x() + vm.distance.x() - m_documentOffset.x()),
0737                                         (int)(actualVm.clipRect.y() + vm.distance.y() - m_documentOffset.y()),
0738                                         actualVm.clipRect.width(),
0739                                         actualVm.clipRect.height());
0740 
0741                 if (!m_pageCacheManager) {
0742                     // no pageCacheManager, so create one for the current view. This happens only once!
0743                     // so on zoom change, we don't re-pre-generate weight/zoom images.
0744                     m_pageCacheManager = new KWPageCacheManager(m_cacheSize);
0745                 }
0746 
0747                 if (m_currentZoom != 1.0) {
0748                     m_pageCacheManager->clear();
0749                     m_currentZoom = 1.0;
0750                 }
0751 
0752                 KWPageCache *pageCache = m_pageCacheManager->take(vm.page);
0753                 if (pageCache) {
0754                     //if (rc.isNull()) {
0755                         pageCache->allExposed = true;
0756                         pageCache->exposed.clear();
0757 #if 0
0758                     }
0759                     else {
0760                         qreal pageTopDocument = vm.page.offsetInDocument();
0761                         qreal pageTopView = localViewConverter.documentToViewY(pageTopDocument);
0762                         QRectF pageRectDocument = vm.page.rect();
0763                         QRectF pageRectView = localViewConverter.documentToView(pageRectDocument);
0764 
0765                         // translated from the page topleft to 0,0 for our cache image
0766                         QRect clipRectOnPage = vm.clipRect.translated(-pageRectView.x(), -pageTopView);
0767 
0768                         pageCache->exposed.append(clipRectOnPage);
0769                     }
0770 #endif
0771                     m_pageCacheManager->insert(vm.page, pageCache);
0772                 }
0773                 updateCanvasInternal(finalClip);
0774             }
0775         }
0776     }
0777 }
0778 
0779 
0780 KoViewConverter *KWCanvasBase::viewConverter() const
0781 {
0782     return m_viewConverter;
0783 }
0784 
0785 void KWCanvasBase::setCacheEnabled(bool enabled, int cacheSize, qreal maxZoom)
0786 {
0787     if ((!m_pageCacheManager && enabled) || (m_cacheSize != cacheSize)) {
0788         delete m_pageCacheManager;
0789         m_pageCacheManager = new KWPageCacheManager(cacheSize);
0790     }
0791     m_cacheEnabled = enabled;
0792     m_cacheSize = cacheSize;
0793     m_maxZoom = maxZoom;
0794 }
0795 
0796 QPoint KWCanvasBase::documentOffset() const
0797 {
0798     return m_documentOffset;
0799 }
0800 
0801 const qreal KWCanvasBase::AnnotationAreaWidth = 200.0; // only static const integral data members can be initialized within a class