File indexing completed on 2024-05-12 16:35:43

0001 /* This file is part of the KDE project
0002    Copyright 2009 Thomas Zander <zander@kde.org>
0003    Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0004    Copyright 2006 Robert Knight <robertknight@gmail.com>
0005    Copyright 2006 Inge Wallin <inge@lysator.liu.se>
0006    Copyright 1999-2002,2004 Laurent Montel <montel@kde.org>
0007    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
0008    Copyright 1999-2004 David Faure <faure@kde.org>
0009    Copyright 2004-2005 Meni Livne <livne@kde.org>
0010    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
0011    Copyright 2002-2003 Norbert Andres <nandres@web.de>
0012    Copyright 2003 Hamish Rodda <rodda@kde.org>
0013    Copyright 2003 Joseph Wenninger <jowenn@kde.org>
0014    Copyright 2003 Lukas Tinkl <lukas@kde.org>
0015    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
0016    Copyright 2002 Harri Porten <porten@kde.org>
0017    Copyright 2002 John Dailey <dailey@vt.edu>
0018    Copyright 2002 Daniel Naber <daniel.naber@t-online.de>
0019    Copyright 1999-2000 Torben Weis <weis@kde.org>
0020    Copyright 1999-2000 Stephan Kulow <coolo@kde.org>
0021    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
0022    Copyright 2000 Wilco Greven <greven@kde.org>
0023    Copyright 2000 Simon Hausmann <hausmann@kde.org
0024    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
0025    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
0026    Copyright 1999 Reginald Stadlbauer <reggie@kde.org>
0027 
0028    This library is free software; you can redistribute it and/or
0029    modify it under the terms of the GNU Library General Public
0030    License as published by the Free Software Foundation; either
0031    version 2 of the License, or (at your option) any later version.
0032 
0033    This library is distributed in the hope that it will be useful,
0034    but WITHOUT ANY WARRANTY; without even the implied warranty of
0035    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0036    Library General Public License for more details.
0037 
0038    You should have received a copy of the GNU Library General Public License
0039    along with this library; see the file COPYING.LIB.  If not, write to
0040    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0041    Boston, MA 02110-1301, USA.
0042 */
0043 
0044 // Local
0045 #include "CanvasItem.h"
0046 
0047 // std
0048 #include <assert.h>
0049 #include <float.h>
0050 #include <stdlib.h>
0051 
0052 // Qt
0053 #include <QApplication>
0054 #include <QBuffer>
0055 #include <QByteArray>
0056 #include <QClipboard>
0057 #include <QDragLeaveEvent>
0058 #include <QDragMoveEvent>
0059 #include <QDropEvent>
0060 #include <QEvent>
0061 #include <QFocusEvent>
0062 #include <QKeyEvent>
0063 #include <QLabel>
0064 #include <QList>
0065 #include <QMenu>
0066 #include <QMouseEvent>
0067 #include <QPainter>
0068 #include <QPaintEvent>
0069 #include <QPixmap>
0070 #include <QPoint>
0071 #include <QScrollBar>
0072 #include <QTextStream>
0073 #include <QToolTip>
0074 #include <QWidget>
0075 #include <QStyleOptionGraphicsItem>
0076 #include <QGraphicsSceneDragDropEvent>
0077 
0078 // Calligra
0079 #include <KoCanvasController.h>
0080 #include <KoShapeManager.h>
0081 #include <KoToolManager.h>
0082 #include <KoToolProxy.h>
0083 #include <KoZoomHandler.h>
0084 #include <KoPointerEvent.h>
0085 #include <KoShapeController.h>
0086 #include <KoShapeManagerPaintingStrategy.h>
0087 #include <KoCanvasResourceManager.h>
0088 
0089 // Sheets
0090 #include "SheetsDebug.h"
0091 #include "CalculationSettings.h"
0092 #include "CellStorage.h"
0093 #include "Damages.h"
0094 #include "Doc.h"
0095 #include "Global.h"
0096 #include "HeaderItems.h"
0097 #include "Localization.h"
0098 #include "Map.h"
0099 #include "RowColumnFormat.h"
0100 #include "Sheet.h"
0101 #include "Util.h"
0102 #include "Validity.h"
0103 #include "View.h"
0104 
0105 // commands
0106 #include "commands/CopyCommand.h"
0107 #include "commands/DeleteCommand.h"
0108 #include "commands/PasteCommand.h"
0109 #include "commands/StyleCommand.h"
0110 
0111 // ui
0112 #include "ui/CellView.h"
0113 #include "ui/Selection.h"
0114 #include "ui/SheetView.h"
0115 #include "ui/RightToLeftPaintingStrategy.h"
0116 
0117 #define MIN_SIZE 10
0118 
0119 using namespace Calligra::Sheets;
0120 
0121 class Q_DECL_HIDDEN CanvasItem::Private
0122 {
0123 public:
0124     Selection* selection;
0125     KoZoomHandler* zoomHandler;
0126     QHash<const Sheet*, SheetView*> sheetViews;
0127     Sheet* activeSheet;
0128     ColumnHeaderItem* columnHeader;
0129     RowHeaderItem* rowHeader;
0130     Doc *doc;
0131 };
0132 
0133 CanvasItem::CanvasItem(Doc *doc, QGraphicsItem *parent)
0134         : QGraphicsWidget(parent)
0135         , CanvasBase(doc)
0136         , d(new Private)
0137 {
0138     setAttribute(Qt::WA_OpaquePaintEvent);
0139     //setBackgroundRole(QPalette::Base);
0140 
0141     QGraphicsWidget::setFocusPolicy(Qt::StrongFocus);
0142 
0143     //setMouseTracking(true);
0144     setAcceptHoverEvents(true);
0145 
0146     installEventFilter(this);   // for TAB key processing, otherwise focus change
0147     setAcceptDrops(true);
0148 
0149     d->doc = doc;
0150     d->rowHeader = 0;
0151     d->columnHeader = 0;
0152 
0153     d->selection = new Selection(this);
0154 
0155     d->zoomHandler = new KoZoomHandler();
0156     d->activeSheet = 0;
0157     setActiveSheet(doc->map()->sheet(0));
0158 
0159     d->selection->setActiveSheet(activeSheet());
0160     connect(d->selection, SIGNAL(refreshSheetViews()), SLOT(refreshSheetViews()));
0161     connect(d->selection, SIGNAL(visibleSheetRequested(Sheet*)), this, SLOT(setActiveSheet(Sheet*)));
0162     connect(d->selection, SIGNAL(updateAccessedCellRange(Sheet*,QPoint)), this, SLOT(updateAccessedCellRange(Sheet*,QPoint)));
0163     connect(doc->map(), SIGNAL(damagesFlushed(QList<Damage*>)),
0164             SLOT(handleDamages(QList<Damage*>)));
0165 }
0166 
0167 CanvasItem::~CanvasItem()
0168 {
0169     if (d->doc->isReadWrite())
0170         selection()->emitCloseEditor(true);
0171     d->selection->emitCloseEditor(false);
0172     d->selection->endReferenceSelection(false);
0173 
0174     d->activeSheet = 0;
0175 
0176     delete d->selection;
0177     delete d->zoomHandler;
0178     delete d;
0179 }
0180 
0181 void CanvasItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
0182 {
0183     KoPointerEvent pev(event, QPointF());
0184     mousePressed(&pev);
0185 }
0186 
0187 void CanvasItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
0188 {
0189     KoPointerEvent pev(event, QPointF());
0190     mouseReleased(&pev);
0191 }
0192 
0193 void CanvasItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
0194 {
0195     KoPointerEvent pev(event, QPointF());
0196     mouseMoved(&pev);
0197 }
0198 
0199 void CanvasItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
0200 {
0201     KoPointerEvent pev(event, QPointF());
0202     mouseDoubleClicked(&pev);
0203 }
0204 
0205 void CanvasItem::paint(QPainter* painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
0206 {
0207     Q_UNUSED(widget);
0208     CanvasBase::paint(painter, option->exposedRect);
0209 }
0210 
0211 void CanvasItem::dragEnterEvent(QGraphicsSceneDragDropEvent* event)
0212 {
0213     if (CanvasBase::dragEnter(event->mimeData())) {
0214         event->acceptProposedAction();
0215     }
0216 }
0217 
0218 void CanvasItem::dragMoveEvent(QGraphicsSceneDragDropEvent* event)
0219 {
0220     if (CanvasBase::dragMove(event->mimeData(), event->pos(), event->source())) {
0221         event->acceptProposedAction();
0222     } else {
0223         event->ignore();
0224     }
0225 }
0226 
0227 void CanvasItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *)
0228 {
0229     CanvasBase::dragLeave();
0230 }
0231 
0232 void CanvasItem::dropEvent(QGraphicsSceneDragDropEvent *event)
0233 {
0234     if (CanvasBase::drop(event->mimeData(), event->pos(), event->source())) {
0235         event->setAccepted(true);
0236     } else {
0237         event->ignore();
0238     }
0239 }
0240 
0241 Selection* CanvasItem::selection() const
0242 {
0243     return d->selection;
0244 }
0245 
0246 Sheet* CanvasItem::activeSheet() const
0247 {
0248     return d->activeSheet;
0249 }
0250 
0251 KoZoomHandler* CanvasItem::zoomHandler() const
0252 {
0253     return d->zoomHandler;
0254 }
0255 
0256 SheetView* CanvasItem::sheetView(const Sheet* sheet) const
0257 {
0258     if (!d->sheetViews.contains(sheet)) {
0259         debugSheetsRender << "Creating SheetView for" << sheet->sheetName();
0260         d->sheetViews.insert(sheet, new SheetView(sheet));
0261         d->sheetViews[ sheet ]->setViewConverter(zoomHandler());
0262         connect(d->sheetViews[ sheet ], SIGNAL(visibleSizeChanged(QSizeF)),
0263                 this, SLOT(setDocumentSize(QSizeF)));
0264         connect(d->sheetViews[ sheet ], SIGNAL(obscuredRangeChanged(QSize)),
0265                 this, SLOT(setObscuredRange(QSize)));
0266         //connect(d->sheetViews[ sheet ], SIGNAL(visibleSizeChanged(QSizeF)),
0267                 //d->zoomController, SLOT(setDocumentSize(QSizeF)));
0268         connect(sheet, SIGNAL(visibleSizeChanged()),
0269                 d->sheetViews[ sheet ], SLOT(updateAccessedCellRange()));
0270     }
0271     return d->sheetViews[ sheet ];
0272 }
0273 
0274 void CanvasItem::refreshSheetViews()
0275 {
0276     const QList<SheetView*> sheetViews = d->sheetViews.values();
0277     for (int i = 0; i < sheetViews.count(); ++i) {
0278         disconnect(sheetViews[i], SIGNAL(visibleSizeChanged(QSizeF)),
0279                    this, SLOT(setDocumentSize(QSizeF)));
0280         disconnect(sheetViews[i], SIGNAL(obscuredRangeChanged(QSize)),
0281                 this, SLOT(setObscuredRange(QSize)));
0282         //disconnect(sheetViews[i], SIGNAL(visibleSizeChanged(QSizeF)),
0283                    //d->zoomController, SLOT(setDocumentSize(QSizeF)));
0284         disconnect(sheetViews[i]->sheet(), SIGNAL(visibleSizeChanged()),
0285                    sheetViews[i], SLOT(updateAccessedCellRange()));
0286     }
0287     qDeleteAll(d->sheetViews);
0288     d->sheetViews.clear();
0289     const QList<Sheet*> sheets = doc()->map()->sheetList();
0290     for (int i = 0; i < sheets.count(); ++i)
0291         sheets[i]->cellStorage()->invalidateStyleCache();
0292 }
0293 
0294 void CanvasItem::setActiveSheet(Sheet* sheet)
0295 {
0296     if (sheet == d->activeSheet)
0297         return;
0298 
0299     if (d->activeSheet != 0 && !d->selection->referenceSelectionMode()) {
0300         selection()->emitCloseEditor(true);
0301         //saveCurrentSheetSelection();
0302     }
0303 
0304     const Sheet* oldSheet = d->activeSheet;
0305     d->activeSheet = sheet;
0306 
0307     if (d->activeSheet == 0) {
0308         return;
0309     }
0310 
0311     // flake
0312     // Change the active shape controller and its shapes.
0313     shapeController()->setShapeControllerBase(d->activeSheet);
0314     // and then update the toolmanager separately
0315     KoToolManager::instance()->updateShapeControllerBase(d->activeSheet, canvasController());
0316 
0317     shapeManager()->setShapes(d->activeSheet->shapes());
0318     // Tell the Canvas about the new visible sheet size.
0319     sheetView(d->activeSheet)->updateAccessedCellRange();
0320 
0321     // If there was no sheet before or the layout directions differ.
0322     if (!oldSheet || oldSheet->layoutDirection() != d->activeSheet->layoutDirection()) {
0323         // Propagate the layout direction to the canvas and horz. scrollbar.
0324         const Qt::LayoutDirection direction = d->activeSheet->layoutDirection();
0325         setLayoutDirection(direction);
0326         // XXX d->horzScrollBar->setLayoutDirection(direction);
0327         // Replace the painting strategy for painting shapes.
0328         KoShapeManager *const shapeManager = this->shapeManager();
0329         KoShapeManagerPaintingStrategy *paintingStrategy = 0;
0330         if (direction == Qt::LeftToRight) {
0331             paintingStrategy = new KoShapeManagerPaintingStrategy(shapeManager);
0332         } else {
0333             paintingStrategy = new RightToLeftPaintingStrategy(shapeManager, this);
0334         }
0335         shapeManager->setPaintingStrategy(paintingStrategy);
0336     }
0337 
0338     /*
0339     // Restore the old scrolling offset.
0340     QMap<Sheet*, QPointF>::Iterator it3 = d->savedOffsets.find(d->activeSheet);
0341     if (it3 != d->savedOffsets.end()) {
0342         const QPoint offset = zoomHandler()->documentToView(*it3).toPoint();
0343         d->canvas->setDocumentOffset(offset);
0344         d->horzScrollBar->setValue(offset.x());
0345         d->vertScrollBar->setValue(offset.y());
0346     }*/
0347 
0348     // tell the resource manager of the newly active page
0349     resourceManager()->setResource(KoCanvasResourceManager::CurrentPage, QVariant(sheet->map()->indexOf(sheet) + 1));
0350 
0351     // Always repaint the visible cells.
0352     update();
0353     if (d->rowHeader) d->rowHeader->update();
0354     if (d->columnHeader) d->columnHeader->update();
0355     //d->selectAllButton->update();
0356 
0357     if (d->selection->referenceSelectionMode()) {
0358         d->selection->setActiveSheet(d->activeSheet);
0359         return;
0360     }
0361 
0362 #if 0
0363     /* see if there was a previous selection on this other sheet */
0364     QMap<Sheet*, QPoint>::Iterator it = d->savedAnchors.find(d->activeSheet);
0365     QMap<Sheet*, QPoint>::Iterator it2 = d->savedMarkers.find(d->activeSheet);
0366 
0367     // restore the old anchor and marker
0368     const QPoint newAnchor = (it == d->savedAnchors.end()) ? QPoint(1, 1) : *it;
0369     const QPoint newMarker = (it2 == d->savedMarkers.end()) ? QPoint(1, 1) : *it2;
0370 
0371 #endif
0372     d->selection->clear();
0373     d->selection->setActiveSheet(d->activeSheet);
0374     d->selection->setOriginSheet(d->activeSheet);
0375     //d->selection->initialize(QRect(newMarker, newAnchor));
0376 
0377     // Auto calculation state for the INFO function.
0378     const bool autoCalc = d->activeSheet->isAutoCalculationEnabled();
0379     doc()->map()->calculationSettings()->setAutoCalculationEnabled(autoCalc);
0380 }
0381 
0382 ColumnHeader* CanvasItem::columnHeader() const
0383 {
0384     if (!d->columnHeader)
0385         d->columnHeader = new ColumnHeaderItem(0, const_cast<CanvasItem*>(this));
0386     return d->columnHeader;
0387 }
0388 
0389 RowHeader* CanvasItem::rowHeader() const
0390 {
0391     if (!d->rowHeader)
0392         d->rowHeader = new RowHeaderItem(0, const_cast<CanvasItem*>(this));
0393     return d->rowHeader;
0394 }
0395 
0396 void CanvasItem::setCursor(const QCursor &cursor)
0397 {
0398     QGraphicsWidget::setCursor(cursor);
0399 }
0400 
0401 void CanvasItem::handleDamages(const QList<Damage*>& damages)
0402 {
0403     QRegion paintRegion;
0404     enum { Nothing, Everything, Clipped } paintMode = Nothing;
0405 
0406     QList<Damage*>::ConstIterator end(damages.end());
0407     for (QList<Damage*>::ConstIterator it = damages.begin(); it != end; ++it) {
0408         Damage* damage = *it;
0409         if (!damage) continue;
0410 
0411         if (damage->type() == Damage::Cell) {
0412             CellDamage* cellDamage = static_cast<CellDamage*>(damage);
0413             debugSheetsDamage << "Processing\t" << *cellDamage;
0414             Sheet* const damagedSheet = cellDamage->sheet();
0415 
0416             if (cellDamage->changes() & CellDamage::Appearance) {
0417                 const Region& region = cellDamage->region();
0418                 sheetView(damagedSheet)->invalidateRegion(region);
0419                 paintMode = Everything;
0420             }
0421             continue;
0422         }
0423 
0424         if (damage->type() == Damage::Sheet) {
0425             SheetDamage* sheetDamage = static_cast<SheetDamage*>(damage);
0426             debugSheetsDamage << *sheetDamage;
0427             const SheetDamage::Changes changes = sheetDamage->changes();
0428             if (changes & (SheetDamage::Name | SheetDamage::Shown)) {
0429 //                d->tabBar->setTabs(doc()->map()->visibleSheets());
0430                 paintMode = Everything;
0431             }
0432             if (changes & (SheetDamage::Shown | SheetDamage::Hidden)) {
0433 //                updateShowSheetMenu();
0434                 paintMode = Everything;
0435             }
0436             // The following changes only affect the active sheet.
0437             if (sheetDamage->sheet() != d->activeSheet) {
0438                 continue;
0439             }
0440             if (changes.testFlag(SheetDamage::ContentChanged)) {
0441                 update();
0442                 paintMode = Everything;
0443             }
0444             if (changes.testFlag(SheetDamage::PropertiesChanged)) {
0445                 sheetView(d->activeSheet)->invalidate();
0446                 paintMode = Everything;
0447             }
0448             if (sheetDamage->changes() & SheetDamage::ColumnsChanged)
0449                 columnHeader()->update();
0450             if (sheetDamage->changes() & SheetDamage::RowsChanged)
0451                 rowHeader()->update();
0452             continue;
0453         }
0454 
0455         if (damage->type() == Damage::Selection) {
0456             SelectionDamage* selectionDamage = static_cast<SelectionDamage*>(damage);
0457             debugSheetsDamage << "Processing\t" << *selectionDamage;
0458             const Region region = selectionDamage->region();
0459 
0460             if (paintMode == Clipped) {
0461                 const QRectF rect = cellCoordinatesToView(region.boundingRect());
0462                 paintRegion += rect.toRect().adjusted(-3, -3, 4, 4);
0463             } else {
0464                 paintMode = Everything;
0465             }
0466             continue;
0467         }
0468 
0469         debugSheetsDamage << "Unhandled\t" << *damage;
0470     }
0471 
0472     // At last repaint the dirty cells.
0473     if (paintMode == Clipped) {
0474         update(paintRegion.boundingRect());
0475     } else if (paintMode == Everything) {
0476         update();
0477     }
0478 }
0479 
0480 void CanvasItem::setObscuredRange(const QSize &size)
0481 {
0482     SheetView* sheetView = qobject_cast<SheetView*>(sender());
0483     if (!sheetView) return;
0484 
0485     emit obscuredRangeChanged(sheetView->sheet(), size);
0486 }
0487 
0488 void CanvasItem::updateAccessedCellRange(Sheet* sheet, const QPoint &location)
0489 {
0490     sheetView(sheet)->updateAccessedCellRange(location);
0491 }