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('<', "&lt;");
0751 
0752     // Show hyperlink, if any
0753     if (tipText.isEmpty())
0754         tipText = cell.link().replace('<', "&lt;");
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('<', "&lt;");
0789     else if (!cell.comment().isEmpty())
0790         tipText += "</p><h4>" + i18n("Comment:") + "</h4><p>" + cell.comment().replace('<', "&lt;");
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 }