File indexing completed on 2024-05-12 16:35:43
0001 /* This file is part of the KDE project 0002 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> 0003 Copyright 2009 Thomas Zander <zander@kde.org> 0004 Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0005 Copyright 2006 Robert Knight <robertknight@gmail.com> 0006 Copyright 2006 Inge Wallin <inge@lysator.liu.se> 0007 Copyright 1999-2002,2004 Laurent Montel <montel@kde.org> 0008 Copyright 2002-2005 Ariya Hidayat <ariya@kde.org> 0009 Copyright 1999-2004 David Faure <faure@kde.org> 0010 Copyright 2004-2005 Meni Livne <livne@kde.org> 0011 Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de> 0012 Copyright 2002-2003 Norbert Andres <nandres@web.de> 0013 Copyright 2003 Hamish Rodda <rodda@kde.org> 0014 Copyright 2003 Joseph Wenninger <jowenn@kde.org> 0015 Copyright 2003 Lukas Tinkl <lukas@kde.org> 0016 Copyright 2000-2002 Werner Trobin <trobin@kde.org> 0017 Copyright 2002 Harri Porten <porten@kde.org> 0018 Copyright 2002 John Dailey <dailey@vt.edu> 0019 Copyright 2002 Daniel Naber <daniel.naber@t-online.de> 0020 Copyright 1999-2000 Torben Weis <weis@kde.org> 0021 Copyright 1999-2000 Stephan Kulow <coolo@kde.org> 0022 Copyright 2000 Bernd Wuebben <wuebben@kde.org> 0023 Copyright 2000 Wilco Greven <greven@kde.org> 0024 Copyright 2000 Simon Hausmann <hausmann@kde.org 0025 Copyright 1999 Michael Reiher <michael.reiher@gmx.de> 0026 Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at> 0027 Copyright 1999 Reginald Stadlbauer <reggie@kde.org> 0028 0029 This library is free software; you can redistribute it and/or 0030 modify it under the terms of the GNU Library General Public 0031 License as published by the Free Software Foundation; either 0032 version 2 of the License, or (at your option) any later version. 0033 0034 This library is distributed in the hope that it will be useful, 0035 but WITHOUT ANY WARRANTY; without even the implied warranty of 0036 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0037 Library General Public License for more details. 0038 0039 You should have received a copy of the GNU Library General Public License 0040 along with this library; see the file COPYING.LIB. If not, write to 0041 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0042 Boston, MA 02110-1301, USA. 0043 */ 0044 0045 // Local 0046 #include "CanvasBase.h" 0047 #include "CanvasBase_p.h" 0048 0049 // std 0050 #include <assert.h> 0051 #include <float.h> 0052 #include <stdlib.h> 0053 0054 // Qt 0055 #include <QApplication> 0056 #include <QBuffer> 0057 #include <QByteArray> 0058 #include <QClipboard> 0059 #include <QDragLeaveEvent> 0060 #include <QDragMoveEvent> 0061 #include <QDropEvent> 0062 #include <QEvent> 0063 #include <QFocusEvent> 0064 #include <QKeyEvent> 0065 #include <QLabel> 0066 #include <QList> 0067 #include <QMenu> 0068 #include <QMouseEvent> 0069 #include <QPainter> 0070 #include <QPaintEvent> 0071 #include <QPixmap> 0072 #include <QPoint> 0073 #include <QScrollBar> 0074 #include <QTextStream> 0075 #include <QToolTip> 0076 0077 // Calligra 0078 #include <KoCanvasController.h> 0079 #include <KoShapeManager.h> 0080 #include <KoToolProxy.h> 0081 #include <KoZoomHandler.h> 0082 #include <KoPointerEvent.h> 0083 #include <KoUnit.h> 0084 0085 // Sheets 0086 #include "SheetsDebug.h" 0087 #include "CellStorage.h" 0088 #include "Doc.h" 0089 #include "Global.h" 0090 #include "HeaderWidgets.h" 0091 #include "Localization.h" 0092 #include "Map.h" 0093 #include "RowColumnFormat.h" 0094 #include "RowFormatStorage.h" 0095 #include "Sheet.h" 0096 #include "Util.h" 0097 #include "Validity.h" 0098 #include "ElapsedTime_p.h" 0099 0100 // commands 0101 #include "commands/CopyCommand.h" 0102 #include "commands/DeleteCommand.h" 0103 #include "commands/PasteCommand.h" 0104 #include "commands/StyleCommand.h" 0105 0106 // ui 0107 #include "ui/CellView.h" 0108 #include "ui/Selection.h" 0109 #include "ui/SheetView.h" 0110 0111 #define MIN_SIZE 10 0112 0113 using namespace Calligra::Sheets; 0114 0115 /**************************************************************** 0116 * 0117 * CanvasBase 0118 * 0119 ****************************************************************/ 0120 0121 CanvasBase::CanvasBase(Doc* doc) 0122 : KoCanvasBase(0) 0123 , d(new Private) 0124 { 0125 d->validationInfo = 0; 0126 d->offset = QPointF(0.0, 0.0); 0127 d->doc = doc; 0128 0129 // flake 0130 d->shapeManager = new KoShapeManager(this); 0131 d->toolProxy = new KoToolProxy(this); 0132 } 0133 0134 CanvasBase::~CanvasBase() 0135 { 0136 delete d->shapeManager; 0137 delete d->toolProxy; 0138 delete d->validationInfo; 0139 delete d; 0140 } 0141 0142 Doc* CanvasBase::doc() const 0143 { 0144 return d->doc; 0145 } 0146 0147 void CanvasBase::gridSize(qreal* horizontal, qreal* vertical) const 0148 { 0149 *horizontal = doc()->map()->defaultColumnFormat()->width(); 0150 *vertical = doc()->map()->defaultRowFormat()->height(); 0151 } 0152 0153 bool CanvasBase::snapToGrid() const 0154 { 0155 return false; // FIXME 0156 } 0157 0158 void CanvasBase::addCommand(KUndo2Command* command) 0159 { 0160 doc()->addCommand(command); 0161 } 0162 0163 KoShapeManager* CanvasBase::shapeManager() const 0164 { 0165 return d->shapeManager; 0166 } 0167 0168 void CanvasBase::updateCanvas(const QRectF& rc) 0169 { 0170 QRectF clipRect(viewConverter()->documentToView(rc.translated(-offset()))); 0171 clipRect.adjust(-2, -2, 2, 2); // Resize to fit anti-aliasing 0172 update(clipRect); 0173 } 0174 0175 KoUnit CanvasBase::unit() const 0176 { 0177 return doc()->unit(); 0178 } 0179 0180 KoToolProxy* CanvasBase::toolProxy() const 0181 { 0182 return d->toolProxy; 0183 } 0184 0185 QPointF CanvasBase::offset() const 0186 { 0187 return d->offset; 0188 } 0189 0190 double CanvasBase::xOffset() const 0191 { 0192 return d->offset.x(); 0193 } 0194 0195 double CanvasBase::yOffset() const 0196 { 0197 return d->offset.y(); 0198 } 0199 0200 bool CanvasBase::eventFilter(QObject *o, QEvent *e) 0201 { 0202 /* this canvas event filter acts on events sent to the line edit as well 0203 as events to this filter itself. 0204 */ 0205 if (!o || !e) 0206 return true; 0207 switch (e->type()) { 0208 case QEvent::KeyPress: { 0209 QKeyEvent * keyev = static_cast<QKeyEvent *>(e); 0210 if ((keyev->key() == Qt::Key_Tab) || (keyev->key() == Qt::Key_Backtab)) { 0211 keyPressed(keyev); 0212 return true; 0213 } 0214 break; 0215 } 0216 case QEvent::InputMethod: { 0217 //QIMEvent * imev = static_cast<QIMEvent *>(e); 0218 //processIMEvent( imev ); 0219 //break; 0220 } 0221 case QEvent::ToolTip: { 0222 QHelpEvent* helpEvent = static_cast<QHelpEvent*>(e); 0223 showToolTip(helpEvent->pos()); 0224 } 0225 default: 0226 break; 0227 } 0228 return false; 0229 } 0230 0231 void CanvasBase::validateSelection() 0232 { 0233 register Sheet * const sheet = activeSheet(); 0234 if (!sheet) 0235 return; 0236 #if 0 0237 XXX TODO 0238 if (selection()->isSingular()) { 0239 const Cell cell = Cell(sheet, selection()->marker()).masterCell(); 0240 Validity validity = cell.validity(); 0241 if (validity.displayValidationInformation()) { 0242 const QString title = validity.titleInfo(); 0243 QString message = validity.messageInfo(); 0244 if (title.isEmpty() && message.isEmpty()) 0245 return; 0246 0247 if (!d->validationInfo) { 0248 d->validationInfo = new QLabel(this); 0249 QPalette palette = d->validationInfo->palette(); 0250 palette.setBrush(QPalette::Window, palette.toolTipBase()); 0251 palette.setBrush(QPalette::WindowText, palette.toolTipText()); 0252 d->validationInfo->setPalette(palette); 0253 // d->validationInfo->setWindowFlags(Qt::ToolTip); 0254 d->validationInfo->setFrameShape(QFrame::Box); 0255 d->validationInfo->setAlignment(Qt::AlignVCenter); 0256 d->validationInfo->setTextFormat(Qt::RichText); 0257 } 0258 0259 QString resultText("<html><body>"); 0260 if (!title.isEmpty()) { 0261 resultText += "<h2>" + title + "</h2>"; 0262 } 0263 if (!message.isEmpty()) { 0264 message.replace(QChar('\n'), QString("<br>")); 0265 resultText += "<p>" + message + "</p>"; 0266 } 0267 resultText += "</body></html>"; 0268 d->validationInfo->setText(resultText); 0269 0270 const double xpos = sheet->columnPosition(cell.column()) + cell.width(); 0271 const double ypos = sheet->rowPosition(cell.row()) + cell.height(); 0272 const QPointF position = QPointF(xpos, ypos) - offset(); 0273 const QPoint viewPosition = viewConverter()->documentToView(position).toPoint(); 0274 d->validationInfo->move(/*mapToGlobal*/(viewPosition)); // Qt::ToolTip! 0275 d->validationInfo->show(); 0276 } else { 0277 delete d->validationInfo; 0278 d->validationInfo = 0; 0279 } 0280 } else { 0281 delete d->validationInfo; 0282 d->validationInfo = 0; 0283 } 0284 #endif 0285 } 0286 0287 void CanvasBase::setDocumentOffset(const QPoint& offset) 0288 { 0289 const QPoint delta = offset - viewConverter()->documentToView(d->offset).toPoint(); 0290 d->offset = viewConverter()->viewToDocument(offset); 0291 0292 ColumnHeader* ch = columnHeader(); 0293 if (ch) ch->scroll(-delta.x(), 0); 0294 RowHeader* rh = rowHeader(); 0295 if (rh) rh->scroll(0, -delta.y()); 0296 } 0297 0298 void CanvasBase::setDocumentSize(const QSizeF& size) 0299 { 0300 const QSize s = viewConverter()->documentToView(size).toSize(); 0301 documentSizeChanged(s); 0302 } 0303 0304 void CanvasBase::mousePressed(KoPointerEvent* event) 0305 { 0306 KoPointerEvent *const origEvent = event; 0307 QPointF documentPosition; 0308 if (layoutDirection() == Qt::LeftToRight) { 0309 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset(); 0310 } else { 0311 const QPoint position(width() - event->x(), event->y()); 0312 const QPointF offset(this->offset().x(), this->offset().y()); 0313 documentPosition = viewConverter()->viewToDocument(position) + offset; 0314 /*XXX TODO 0315 debugSheets << "----------------------------"; 0316 debugSheets << "event->pos():" << event->pos(); 0317 debugSheets << "event->globalPos():" << event->globalPos(); 0318 debugSheets << "position:" << position; 0319 debugSheets << "offset:" << offset; 0320 debugSheets << "documentPosition:" << documentPosition; 0321 event = new QMouseEvent(QEvent::MouseButtonPress, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers()); 0322 debugSheets << "newEvent->pos():" << event->pos(); 0323 debugSheets << "newEvent->globalPos():" << event->globalPos();*/ 0324 } 0325 0326 event = new KoPointerEvent(event, documentPosition); 0327 0328 // flake 0329 if(d->toolProxy) { 0330 d->toolProxy->mousePressEvent(event); 0331 if (!event->isAccepted() && event->button() == Qt::RightButton) { 0332 showContextMenu(origEvent->globalPos()); 0333 origEvent->accept(); 0334 } 0335 } 0336 if (layoutDirection() == Qt::RightToLeft) { 0337 //delete event; 0338 } 0339 delete event; 0340 } 0341 0342 void CanvasBase::mouseReleased(KoPointerEvent* event) 0343 { 0344 QPointF documentPosition; 0345 if (layoutDirection() == Qt::LeftToRight) { 0346 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset(); 0347 } else { 0348 const QPoint position(width() - event->x(), event->y()); 0349 const QPointF offset(this->offset().x(), this->offset().y()); 0350 documentPosition = viewConverter()->viewToDocument(position) + offset; 0351 // XXX TODO event = new QMouseEvent(QEvent::MouseButtonRelease, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers()); 0352 } 0353 0354 event = new KoPointerEvent(event, documentPosition); 0355 0356 // flake 0357 if(d->toolProxy) 0358 d->toolProxy->mouseReleaseEvent(event); 0359 0360 if (layoutDirection() == Qt::RightToLeft) { 0361 // delete event; 0362 } 0363 delete event; 0364 } 0365 0366 void CanvasBase::mouseMoved(KoPointerEvent* event) 0367 { 0368 QPointF documentPosition; 0369 if (layoutDirection() == Qt::LeftToRight) { 0370 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset(); 0371 } else { 0372 const QPoint position(width() - event->x(), event->y()); 0373 const QPointF offset(this->offset().x(), this->offset().y()); 0374 documentPosition = viewConverter()->viewToDocument(position) + offset; 0375 // XXX TODO event = new QMouseEvent(QEvent::MouseMove, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers()); 0376 } 0377 0378 event = new KoPointerEvent(event, documentPosition); 0379 0380 // flake 0381 if(d->toolProxy) 0382 d->toolProxy->mouseMoveEvent(event); 0383 0384 if (layoutDirection() == Qt::RightToLeft) { 0385 // delete event; 0386 } 0387 delete event; 0388 } 0389 0390 void CanvasBase::mouseDoubleClicked(KoPointerEvent* event) 0391 { 0392 QPointF documentPosition; 0393 if (layoutDirection() == Qt::LeftToRight) { 0394 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset(); 0395 } else { 0396 const QPoint position(width() - event->x(), event->y()); 0397 const QPointF offset(this->offset().x(), this->offset().y()); 0398 documentPosition = viewConverter()->viewToDocument(position) + offset; 0399 // XXX TODO event = new QMouseEvent(QEvent::MouseButtonDblClick, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers()); 0400 } 0401 0402 event = new KoPointerEvent(event, documentPosition); 0403 0404 // flake 0405 if(d->toolProxy) 0406 d->toolProxy->mouseDoubleClickEvent(event); 0407 0408 if (layoutDirection() == Qt::RightToLeft) { 0409 // delete event; 0410 } 0411 delete event; 0412 } 0413 0414 void CanvasBase::keyPressed(QKeyEvent* event) 0415 { 0416 // flake 0417 if(d->toolProxy) 0418 d->toolProxy->keyPressEvent(event); 0419 } 0420 0421 void CanvasBase::tabletEvent(QTabletEvent *e) 0422 { 0423 // flake 0424 if(d->toolProxy) 0425 d->toolProxy->tabletEvent(e, viewConverter()->viewToDocument(e->pos() + offset())); 0426 } 0427 0428 QVariant CanvasBase::inputMethodQuery(Qt::InputMethodQuery query) const 0429 { 0430 // flake 0431 return d->toolProxy ? d->toolProxy->inputMethodQuery(query, *(viewConverter())) : 0; 0432 } 0433 0434 void CanvasBase::inputMethodEvent(QInputMethodEvent *event) 0435 { 0436 // flake 0437 if(d->toolProxy) 0438 d->toolProxy->inputMethodEvent(event); 0439 } 0440 0441 void CanvasBase::paint(QPainter* painter, const QRectF& painterRect) 0442 { 0443 if (doc()->map()->isLoading() || isViewLoading()) 0444 return; 0445 0446 register Sheet * const sheet = activeSheet(); 0447 if (!sheet) 0448 return; 0449 0450 // ElapsedTime et("Painting cells", ElapsedTime::PrintOnlyTime); 0451 0452 painter->setClipRect(painterRect); 0453 painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); 0454 painter->save(); 0455 0456 // After the scaling, the painter methods need document coordinates! 0457 qreal zoomX, zoomY; 0458 viewConverter()->zoom(&zoomX, &zoomY); 0459 painter->scale(zoomX, zoomY); 0460 0461 const bool layoutReversed = sheet->layoutDirection() == Qt::RightToLeft; 0462 const QPointF offset(layoutReversed ? -this->offset().x() : this->offset().x(), this->offset().y()); 0463 painter->translate(-offset); 0464 0465 // erase background 0466 const QRectF paintRect(viewConverter()->viewToDocument(rect()).translated(offset)); 0467 painter->fillRect(paintRect, painter->background()); 0468 0469 // paint visible cells 0470 const QRect visibleRect = visibleCells(); 0471 const QPointF topLeft(sheet->columnPosition(visibleRect.left()), sheet->rowPosition(visibleRect.top())); 0472 sheetView(sheet)->setPaintCellRange(visibleRect); 0473 sheetView(sheet)->paintCells(*painter, paintRect, topLeft, this); 0474 0475 // flake 0476 painter->restore(); 0477 // d->offset is the negated CanvasController offset in document coordinates. 0478 // painter.save(); 0479 painter->translate(-viewConverter()->documentToView(offset)); 0480 d->shapeManager->paint(*painter, *viewConverter(), false); 0481 // painter.restore(); 0482 // const QPointF p = -viewConverter()->documentToView(this->offset()); 0483 // painter.translate(p.x() /*+ width()*/, p.y()); 0484 painter->setRenderHint(QPainter::Antialiasing, false); 0485 if(d->toolProxy) 0486 d->toolProxy->paint(*painter, *viewConverter()); 0487 } 0488 0489 void CanvasBase::focusIn(QFocusEvent *event) 0490 { 0491 Q_UNUSED(event); 0492 // If we are in editing mode, we redirect the 0493 // focus to the CellEditor or ExternalEditor. 0494 // Using a focus proxy does not work here, because in reference selection 0495 // mode clicking on the canvas to select a reference should end up in the 0496 // editor, which got the focus before. This is determined by storing the 0497 // last editor with focus. It is set by the editors on getting focus by user 0498 // interaction. Setting a focus proxy would always result in the proxy being 0499 // the last editor, because clicking the canvas is a user interaction. 0500 // This screws up <Tab> though (David) 0501 selection()->emitRequestFocusEditor(); 0502 //XXX TODO QWidget::focusInEvent(event); 0503 } 0504 0505 bool CanvasBase::dragEnter(const QMimeData* mimeData) 0506 { 0507 if (mimeData->hasText() || 0508 mimeData->hasFormat("application/x-kspread-snippet")) { 0509 return true; 0510 } 0511 return false; 0512 } 0513 0514 bool CanvasBase::dragMove(const QMimeData* mimeData, const QPointF& eventPos, const QObject *source) 0515 { 0516 register Sheet * const sheet = activeSheet(); 0517 if (!sheet) { 0518 return false; 0519 } 0520 0521 if (mimeData->hasText() || mimeData->hasFormat("application/x-kspread-snippet")) { 0522 // acceptProposedAction 0523 } else { 0524 return false; 0525 } 0526 #if 0 // TODO Stefan: implement drag marking rectangle 0527 QRect dragMarkingRect; 0528 if (mimeData->hasFormat("application/x-kspread-snippet")) { 0529 if (source == canvasWidget()) { 0530 debugSheetsUI << "source == this"; 0531 dragMarkingRect = selection()->boundingRect(); 0532 } else { 0533 debugSheetsUI << "source != this"; 0534 QByteArray data = mimeData->data("application/x-kspread-snippet"); 0535 QString errorMsg; 0536 int errorLine; 0537 int errorColumn; 0538 QDomDocument doc; 0539 if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorColumn)) { 0540 // an error occurred 0541 debugSheetsUI << "CanvasBase::daragMoveEvent: an error occurred" << endl 0542 << "line: " << errorLine << " col: " << errorColumn 0543 << ' ' << errorMsg << endl; 0544 dragMarkingRect = QRect(1, 1, 1, 1); 0545 } else { 0546 QDomElement root = doc.documentElement(); // "spreadsheet-snippet" 0547 dragMarkingRect = QRect(1, 1, 0548 root.attribute("columns").toInt(), 0549 root.attribute("rows").toInt()); 0550 } 0551 } 0552 } else { // if ( mimeData->hasText() ) 0553 debugSheetsUI << "has text"; 0554 dragMarkingRect = QRect(1, 1, 1, 1); 0555 } 0556 #else 0557 Q_UNUSED(source); 0558 #endif 0559 const QPoint dragAnchor = selection()->boundingRect().topLeft(); 0560 double xpos = sheet->columnPosition(dragAnchor.x()); 0561 double ypos = sheet->rowPosition(dragAnchor.y()); 0562 double width = sheet->columnFormat(dragAnchor.x())->width(); 0563 double height = sheet->rowFormats()->rowHeight(dragAnchor.y()); 0564 0565 // consider also the selection rectangle 0566 const QRectF noGoArea(xpos - 1, ypos - 1, width + 3, height + 3); 0567 0568 // determine the current position 0569 double eventPosX; 0570 if (sheet->layoutDirection() == Qt::RightToLeft) { 0571 eventPosX = viewConverter()->viewToDocumentX(this->width() - eventPos.x()) + xOffset(); 0572 } else { 0573 eventPosX = viewConverter()->viewToDocumentX(eventPos.x()) + xOffset(); 0574 } 0575 double eventPosY = viewConverter()->viewToDocumentY(eventPos.y()) + yOffset(); 0576 0577 if (noGoArea.contains(QPointF(eventPosX, eventPosY))) { 0578 return false; 0579 // XXX TODO event->ignore(noGoArea.toRect()); 0580 } 0581 0582 #if 0 // TODO Stefan: implement drag marking rectangle 0583 // determine the cell position under the mouse 0584 qreal tmp; 0585 const int col = sheet->leftColumn(eventPosX, tmp); 0586 const int row = sheet->topRow(eventPosY, tmp); 0587 dragMarkingRect.moveTo(QPoint(col, row)); 0588 debugSheetsUI << "MARKING RECT =" << dragMarkingRect; 0589 #endif 0590 return true; 0591 } 0592 0593 void CanvasBase::dragLeave() 0594 { 0595 } 0596 0597 bool CanvasBase::drop(const QMimeData* mimeData, const QPointF& eventPos, const QObject *source) 0598 { 0599 register Sheet * const sheet = activeSheet(); 0600 // FIXME Sheet protection: Not all cells have to be protected. 0601 if (!sheet || sheet->isProtected()) { 0602 return false; 0603 } 0604 0605 if (!PasteCommand::supports(mimeData)) { 0606 return false; 0607 } 0608 0609 // Do not allow dropping onto the same position. 0610 const QPoint topLeft(selection()->boundingRect().topLeft()); 0611 const double xpos = sheet->columnPosition(topLeft.x()); 0612 const double ypos = sheet->rowPosition(topLeft.y()); 0613 const double width = sheet->columnFormat(topLeft.x())->width(); 0614 const double height = sheet->rowFormats()->rowHeight(topLeft.y()); 0615 0616 const QRectF noGoArea(xpos - 1, ypos - 1, width + 3, height + 3); 0617 0618 double ev_PosX; 0619 if (sheet->layoutDirection() == Qt::RightToLeft) { 0620 ev_PosX = viewConverter()->viewToDocumentX(this->width() - eventPos.x()) + xOffset(); 0621 } else { 0622 ev_PosX = viewConverter()->viewToDocumentX(eventPos.x()) + xOffset(); 0623 } 0624 double ev_PosY = viewConverter()->viewToDocumentY(eventPos.y()) + yOffset(); 0625 0626 if (noGoArea.contains(QPointF(ev_PosX, ev_PosY))) { 0627 return false; 0628 } 0629 0630 // The destination cell location. 0631 qreal tmp; 0632 const int col = sheet->leftColumn(ev_PosX, tmp); 0633 const int row = sheet->topRow(ev_PosY, tmp); 0634 0635 PasteCommand *const command = new PasteCommand(); 0636 command->setSheet(sheet); 0637 command->add(Region(col, row, 1, 1, sheet)); 0638 command->setMimeData(mimeData); 0639 0640 if (source == canvasWidget()) { 0641 DeleteCommand *const deleteCommand = new DeleteCommand(command); 0642 deleteCommand->setSheet(sheet); 0643 deleteCommand->add(*selection()); // selection is still, where the drag started 0644 deleteCommand->setRegisterUndo(false); 0645 } 0646 0647 command->execute(); 0648 0649 // Select the pasted cells 0650 const int columns = selection()->boundingRect().width(); 0651 const int rows = selection()->boundingRect().height(); 0652 selection()->initialize(QRect(col, row, columns, rows), sheet); 0653 0654 return true; 0655 } 0656 0657 QRect CanvasBase::viewToCellCoordinates(const QRectF& viewRect) const 0658 { 0659 register Sheet * const sheet = activeSheet(); 0660 if (!sheet) 0661 return QRect(); 0662 0663 // NOTE Stefan: Do not consider the layout direction in this case. 0664 const QRectF rect = viewConverter()->viewToDocument(viewRect.normalized()).translated(offset()); 0665 0666 qreal tmp; 0667 const int left = sheet->leftColumn(rect.left(), tmp); 0668 const int right = sheet->rightColumn(rect.right()); 0669 const int top = sheet->topRow(rect.top(), tmp); 0670 const int bottom = sheet->bottomRow(rect.bottom()); 0671 0672 return QRect(left, top, right - left + 1, bottom - top + 1); 0673 } 0674 0675 QRect CanvasBase::visibleCells() const 0676 { 0677 return viewToCellCoordinates(rect()); 0678 } 0679 0680 //--------------------------------------------- 0681 // 0682 // Drawing Engine 0683 // 0684 //--------------------------------------------- 0685 0686 QRectF CanvasBase::cellCoordinatesToView(const QRect& cellRange) const 0687 { 0688 register Sheet * const sheet = activeSheet(); 0689 if (!sheet) 0690 return QRectF(); 0691 0692 QRectF rect = sheet->cellCoordinatesToDocument(cellRange); 0693 // apply scrolling offset 0694 rect.translate(-xOffset(), -yOffset()); 0695 // convert it to view coordinates 0696 rect = viewConverter()->documentToView(rect); 0697 // apply layout direction 0698 if (sheet->layoutDirection() == Qt::RightToLeft) { 0699 const double left = rect.left(); 0700 const double right = rect.right(); 0701 rect.setLeft(width() - right); 0702 rect.setRight(width() - left); 0703 } 0704 return rect; 0705 } 0706 0707 void CanvasBase::showToolTip(const QPoint& p) 0708 { 0709 register Sheet * const sheet = activeSheet(); 0710 if (!sheet) 0711 return; 0712 SheetView * const sheetView = this->sheetView(sheet); 0713 0714 // Over which cell is the mouse ? 0715 qreal ypos, xpos; 0716 qreal dwidth = viewConverter()->viewToDocumentX(width()); 0717 int col; 0718 if (sheet->layoutDirection() == Qt::RightToLeft) 0719 col = sheet->leftColumn((dwidth - viewConverter()->viewToDocumentX(p.x()) + 0720 xOffset()), xpos); 0721 else 0722 col = sheet->leftColumn((viewConverter()->viewToDocumentX(p.x()) + 0723 xOffset()), xpos); 0724 0725 0726 int row = sheet->topRow((viewConverter()->viewToDocumentY(p.y()) + 0727 yOffset()), ypos); 0728 0729 Cell cell = Cell(sheet, col, row).masterCell(); 0730 const CellView& baseCellView = sheetView->cellView(cell.column(), cell.row()); 0731 const bool baseIsObscured = sheetView->isObscured(cell.cellPosition()); 0732 const QPoint cellPos = baseIsObscured ? sheetView->obscuringCell(cell.cellPosition()) 0733 : cell.cellPosition(); 0734 const CellView& cellView = baseIsObscured 0735 ? sheetView->cellView(cellPos) 0736 : baseCellView; 0737 if (sheetView->isObscured(cellPos)) { 0738 cell = Cell(sheet, sheetView->obscuringCell(cellPos)); 0739 } 0740 0741 // displayed tool tip, which has the following priorities: 0742 // - cell content if the cell dimension is too small 0743 // - cell comment 0744 // - hyperlink 0745 // Ensure that it is plain text. 0746 // Not funny if (intentional or not) <a> appears as hyperlink. 0747 QString tipText; 0748 // If cell is too small, show the content 0749 if (!cellView.dimensionFits()) 0750 tipText = cell.displayText().replace('<', "<"); 0751 0752 // Show hyperlink, if any 0753 if (tipText.isEmpty()) 0754 tipText = cell.link().replace('<', "<"); 0755 0756 // Nothing to display, bail out 0757 if (tipText.isEmpty() && cell.comment().isEmpty()) 0758 return; 0759 0760 // Cut if the tip is ridiculously long 0761 const int maxLen = 256; 0762 if (tipText.length() > maxLen) 0763 tipText = tipText.left(maxLen).append("..."); 0764 0765 // Determine position and width of the current cell. 0766 const double cellWidth = cellView.cellWidth(); 0767 const double cellHeight = cellView.cellHeight(); 0768 0769 // Get the cell dimensions 0770 QRect cellRect; 0771 bool insideCellRect = false; 0772 if (sheet->layoutDirection() == Qt::RightToLeft) { 0773 const QRectF rect(dwidth - cellWidth - xpos + xOffset(), ypos - yOffset(), cellWidth, cellHeight); 0774 cellRect = viewConverter()->documentToView(rect).toRect(); 0775 insideCellRect = cellRect.contains(p); 0776 } else { 0777 QRectF rect(xpos - xOffset(), ypos - yOffset(), cellWidth, cellHeight); 0778 cellRect = viewConverter()->documentToView(rect).toRect(); 0779 insideCellRect = cellRect.contains(p); 0780 } 0781 0782 // No use if mouse is somewhere else 0783 if (!insideCellRect) 0784 return; 0785 0786 // Show comment, if any. 0787 if (tipText.isEmpty()) 0788 tipText = cell.comment().replace('<', "<"); 0789 else if (!cell.comment().isEmpty()) 0790 tipText += "</p><h4>" + i18n("Comment:") + "</h4><p>" + cell.comment().replace('<', "<"); 0791 0792 // Now we show the tip 0793 QToolTip::showText(mapToGlobal(cellRect.bottomRight()), 0794 "<p>" + tipText.replace('\n', "<br>") + "</p>"); 0795 // TODO XXX this, cellRect.translated(-mapToGlobal(cellRect.topLeft()))); 0796 } 0797 0798 void CanvasBase::updateInputMethodInfo() 0799 { 0800 updateMicroFocus(); 0801 } 0802 0803 KoViewConverter* CanvasBase::viewConverter() const 0804 { 0805 return zoomHandler(); 0806 }