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 }