1 /* This file is part of the KDE project
2 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3 Copyright 2009 Thomas Zander <zander@kde.org>
4 Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
5 Copyright 2006 Robert Knight <robertknight@gmail.com>
6 Copyright 2006 Inge Wallin <inge@lysator.liu.se>
7 Copyright 1999-2002,2004 Laurent Montel <montel@kde.org>
8 Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
9 Copyright 1999-2004 David Faure <faure@kde.org>
10 Copyright 2004-2005 Meni Livne <livne@kde.org>
11 Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
12 Copyright 2002-2003 Norbert Andres <nandres@web.de>
13 Copyright 2003 Hamish Rodda <rodda@kde.org>
14 Copyright 2003 Joseph Wenninger <jowenn@kde.org>
15 Copyright 2003 Lukas Tinkl <lukas@kde.org>
16 Copyright 2000-2002 Werner Trobin <trobin@kde.org>
17 Copyright 2002 Harri Porten <porten@kde.org>
18 Copyright 2002 John Dailey <dailey@vt.edu>
19 Copyright 2002 Daniel Naber <daniel.naber@t-online.de>
20 Copyright 1999-2000 Torben Weis <weis@kde.org>
21 Copyright 1999-2000 Stephan Kulow <coolo@kde.org>
22 Copyright 2000 Bernd Wuebben <wuebben@kde.org>
23 Copyright 2000 Wilco Greven <greven@kde.org>
24 Copyright 2000 Simon Hausmann <hausmann@kde.org
25 Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
26 Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
27 Copyright 1999 Reginald Stadlbauer <reggie@kde.org>
28
29 This library is free software; you can redistribute it and/or
30 modify it under the terms of the GNU Library General Public
31 License as published by the Free Software Foundation; either
32 version 2 of the License, or (at your option) any later version.
33
34 This library is distributed in the hope that it will be useful,
35 but WITHOUT ANY WARRANTY; without even the implied warranty of
36 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
37 Library General Public License for more details.
38
39 You should have received a copy of the GNU Library General Public License
40 along with this library; see the file COPYING.LIB. If not, write to
41 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
42 Boston, MA 02110-1301, USA.
43 */
44
45 // Local
46 #include "CanvasBase.h"
47 #include "CanvasBase_p.h"
48
49 // std
50 #include <assert.h>
51 #include <float.h>
52 #include <stdlib.h>
53
54 // Qt
55 #include <QApplication>
56 #include <QBuffer>
57 #include <QByteArray>
58 #include <QClipboard>
59 #include <QDragLeaveEvent>
60 #include <QDragMoveEvent>
61 #include <QDropEvent>
62 #include <QEvent>
63 #include <QFocusEvent>
64 #include <QKeyEvent>
65 #include <QLabel>
66 #include <QList>
67 #include <QMenu>
68 #include <QMouseEvent>
69 #include <QPainter>
70 #include <QPaintEvent>
71 #include <QPixmap>
72 #include <QPoint>
73 #include <QScrollBar>
74 #include <QTextStream>
75 #include <QToolTip>
76
77 // Calligra
78 #include <KoCanvasController.h>
79 #include <KoShapeManager.h>
80 #include <KoToolProxy.h>
81 #include <KoZoomHandler.h>
82 #include <KoPointerEvent.h>
83 #include <KoUnit.h>
84
85 // Sheets
86 #include "SheetsDebug.h"
87 #include "CellStorage.h"
88 #include "Doc.h"
89 #include "Global.h"
90 #include "HeaderWidgets.h"
91 #include "Localization.h"
92 #include "Map.h"
93 #include "RowColumnFormat.h"
94 #include "RowFormatStorage.h"
95 #include "Sheet.h"
96 #include "Util.h"
97 #include "Validity.h"
98 #include "ElapsedTime_p.h"
99
100 // commands
101 #include "commands/CopyCommand.h"
102 #include "commands/DeleteCommand.h"
103 #include "commands/PasteCommand.h"
104 #include "commands/StyleCommand.h"
105
106 // ui
107 #include "ui/CellView.h"
108 #include "ui/Selection.h"
109 #include "ui/SheetView.h"
110
111 #define MIN_SIZE 10
112
113 using namespace Calligra::Sheets;
114
115 /****************************************************************
116 *
117 * CanvasBase
118 *
119 ****************************************************************/
120
CanvasBase(Doc * doc)121 CanvasBase::CanvasBase(Doc* doc)
122 : KoCanvasBase(0)
123 , d(new Private)
124 {
125 d->validationInfo = 0;
126 d->offset = QPointF(0.0, 0.0);
127 d->doc = doc;
128
129 // flake
130 d->shapeManager = new KoShapeManager(this);
131 d->toolProxy = new KoToolProxy(this);
132 }
133
~CanvasBase()134 CanvasBase::~CanvasBase()
135 {
136 delete d->shapeManager;
137 delete d->toolProxy;
138 delete d->validationInfo;
139 delete d;
140 }
141
doc() const142 Doc* CanvasBase::doc() const
143 {
144 return d->doc;
145 }
146
gridSize(qreal * horizontal,qreal * vertical) const147 void CanvasBase::gridSize(qreal* horizontal, qreal* vertical) const
148 {
149 *horizontal = doc()->map()->defaultColumnFormat()->width();
150 *vertical = doc()->map()->defaultRowFormat()->height();
151 }
152
snapToGrid() const153 bool CanvasBase::snapToGrid() const
154 {
155 return false; // FIXME
156 }
157
addCommand(KUndo2Command * command)158 void CanvasBase::addCommand(KUndo2Command* command)
159 {
160 doc()->addCommand(command);
161 }
162
shapeManager() const163 KoShapeManager* CanvasBase::shapeManager() const
164 {
165 return d->shapeManager;
166 }
167
updateCanvas(const QRectF & rc)168 void CanvasBase::updateCanvas(const QRectF& rc)
169 {
170 QRectF clipRect(viewConverter()->documentToView(rc.translated(-offset())));
171 clipRect.adjust(-2, -2, 2, 2); // Resize to fit anti-aliasing
172 update(clipRect);
173 }
174
unit() const175 KoUnit CanvasBase::unit() const
176 {
177 return doc()->unit();
178 }
179
toolProxy() const180 KoToolProxy* CanvasBase::toolProxy() const
181 {
182 return d->toolProxy;
183 }
184
offset() const185 QPointF CanvasBase::offset() const
186 {
187 return d->offset;
188 }
189
xOffset() const190 double CanvasBase::xOffset() const
191 {
192 return d->offset.x();
193 }
194
yOffset() const195 double CanvasBase::yOffset() const
196 {
197 return d->offset.y();
198 }
199
eventFilter(QObject * o,QEvent * e)200 bool CanvasBase::eventFilter(QObject *o, QEvent *e)
201 {
202 /* this canvas event filter acts on events sent to the line edit as well
203 as events to this filter itself.
204 */
205 if (!o || !e)
206 return true;
207 switch (e->type()) {
208 case QEvent::KeyPress: {
209 QKeyEvent * keyev = static_cast<QKeyEvent *>(e);
210 if ((keyev->key() == Qt::Key_Tab) || (keyev->key() == Qt::Key_Backtab)) {
211 keyPressed(keyev);
212 return true;
213 }
214 break;
215 }
216 case QEvent::InputMethod: {
217 //QIMEvent * imev = static_cast<QIMEvent *>(e);
218 //processIMEvent( imev );
219 //break;
220 }
221 case QEvent::ToolTip: {
222 QHelpEvent* helpEvent = static_cast<QHelpEvent*>(e);
223 showToolTip(helpEvent->pos());
224 }
225 default:
226 break;
227 }
228 return false;
229 }
230
validateSelection()231 void CanvasBase::validateSelection()
232 {
233 register Sheet * const sheet = activeSheet();
234 if (!sheet)
235 return;
236 #if 0
237 XXX TODO
238 if (selection()->isSingular()) {
239 const Cell cell = Cell(sheet, selection()->marker()).masterCell();
240 Validity validity = cell.validity();
241 if (validity.displayValidationInformation()) {
242 const QString title = validity.titleInfo();
243 QString message = validity.messageInfo();
244 if (title.isEmpty() && message.isEmpty())
245 return;
246
247 if (!d->validationInfo) {
248 d->validationInfo = new QLabel(this);
249 QPalette palette = d->validationInfo->palette();
250 palette.setBrush(QPalette::Window, palette.toolTipBase());
251 palette.setBrush(QPalette::WindowText, palette.toolTipText());
252 d->validationInfo->setPalette(palette);
253 // d->validationInfo->setWindowFlags(Qt::ToolTip);
254 d->validationInfo->setFrameShape(QFrame::Box);
255 d->validationInfo->setAlignment(Qt::AlignVCenter);
256 d->validationInfo->setTextFormat(Qt::RichText);
257 }
258
259 QString resultText("<html><body>");
260 if (!title.isEmpty()) {
261 resultText += "<h2>" + title + "</h2>";
262 }
263 if (!message.isEmpty()) {
264 message.replace(QChar('\n'), QString("<br>"));
265 resultText += "<p>" + message + "</p>";
266 }
267 resultText += "</body></html>";
268 d->validationInfo->setText(resultText);
269
270 const double xpos = sheet->columnPosition(cell.column()) + cell.width();
271 const double ypos = sheet->rowPosition(cell.row()) + cell.height();
272 const QPointF position = QPointF(xpos, ypos) - offset();
273 const QPoint viewPosition = viewConverter()->documentToView(position).toPoint();
274 d->validationInfo->move(/*mapToGlobal*/(viewPosition)); // Qt::ToolTip!
275 d->validationInfo->show();
276 } else {
277 delete d->validationInfo;
278 d->validationInfo = 0;
279 }
280 } else {
281 delete d->validationInfo;
282 d->validationInfo = 0;
283 }
284 #endif
285 }
286
setDocumentOffset(const QPoint & offset)287 void CanvasBase::setDocumentOffset(const QPoint& offset)
288 {
289 const QPoint delta = offset - viewConverter()->documentToView(d->offset).toPoint();
290 d->offset = viewConverter()->viewToDocument(offset);
291
292 ColumnHeader* ch = columnHeader();
293 if (ch) ch->scroll(-delta.x(), 0);
294 RowHeader* rh = rowHeader();
295 if (rh) rh->scroll(0, -delta.y());
296 }
297
setDocumentSize(const QSizeF & size)298 void CanvasBase::setDocumentSize(const QSizeF& size)
299 {
300 const QSize s = viewConverter()->documentToView(size).toSize();
301 documentSizeChanged(s);
302 }
303
mousePressed(KoPointerEvent * event)304 void CanvasBase::mousePressed(KoPointerEvent* event)
305 {
306 KoPointerEvent *const origEvent = event;
307 QPointF documentPosition;
308 if (layoutDirection() == Qt::LeftToRight) {
309 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
310 } else {
311 const QPoint position(width() - event->x(), event->y());
312 const QPointF offset(this->offset().x(), this->offset().y());
313 documentPosition = viewConverter()->viewToDocument(position) + offset;
314 /*XXX TODO
315 debugSheets << "----------------------------";
316 debugSheets << "event->pos():" << event->pos();
317 debugSheets << "event->globalPos():" << event->globalPos();
318 debugSheets << "position:" << position;
319 debugSheets << "offset:" << offset;
320 debugSheets << "documentPosition:" << documentPosition;
321 event = new QMouseEvent(QEvent::MouseButtonPress, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
322 debugSheets << "newEvent->pos():" << event->pos();
323 debugSheets << "newEvent->globalPos():" << event->globalPos();*/
324 }
325
326 event = new KoPointerEvent(event, documentPosition);
327
328 // flake
329 if(d->toolProxy) {
330 d->toolProxy->mousePressEvent(event);
331 if (!event->isAccepted() && event->button() == Qt::RightButton) {
332 showContextMenu(origEvent->globalPos());
333 origEvent->accept();
334 }
335 }
336 if (layoutDirection() == Qt::RightToLeft) {
337 //delete event;
338 }
339 delete event;
340 }
341
mouseReleased(KoPointerEvent * event)342 void CanvasBase::mouseReleased(KoPointerEvent* event)
343 {
344 QPointF documentPosition;
345 if (layoutDirection() == Qt::LeftToRight) {
346 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
347 } else {
348 const QPoint position(width() - event->x(), event->y());
349 const QPointF offset(this->offset().x(), this->offset().y());
350 documentPosition = viewConverter()->viewToDocument(position) + offset;
351 // XXX TODO event = new QMouseEvent(QEvent::MouseButtonRelease, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
352 }
353
354 event = new KoPointerEvent(event, documentPosition);
355
356 // flake
357 if(d->toolProxy)
358 d->toolProxy->mouseReleaseEvent(event);
359
360 if (layoutDirection() == Qt::RightToLeft) {
361 // delete event;
362 }
363 delete event;
364 }
365
mouseMoved(KoPointerEvent * event)366 void CanvasBase::mouseMoved(KoPointerEvent* event)
367 {
368 QPointF documentPosition;
369 if (layoutDirection() == Qt::LeftToRight) {
370 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
371 } else {
372 const QPoint position(width() - event->x(), event->y());
373 const QPointF offset(this->offset().x(), this->offset().y());
374 documentPosition = viewConverter()->viewToDocument(position) + offset;
375 // XXX TODO event = new QMouseEvent(QEvent::MouseMove, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
376 }
377
378 event = new KoPointerEvent(event, documentPosition);
379
380 // flake
381 if(d->toolProxy)
382 d->toolProxy->mouseMoveEvent(event);
383
384 if (layoutDirection() == Qt::RightToLeft) {
385 // delete event;
386 }
387 delete event;
388 }
389
mouseDoubleClicked(KoPointerEvent * event)390 void CanvasBase::mouseDoubleClicked(KoPointerEvent* event)
391 {
392 QPointF documentPosition;
393 if (layoutDirection() == Qt::LeftToRight) {
394 documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
395 } else {
396 const QPoint position(width() - event->x(), event->y());
397 const QPointF offset(this->offset().x(), this->offset().y());
398 documentPosition = viewConverter()->viewToDocument(position) + offset;
399 // XXX TODO event = new QMouseEvent(QEvent::MouseButtonDblClick, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
400 }
401
402 event = new KoPointerEvent(event, documentPosition);
403
404 // flake
405 if(d->toolProxy)
406 d->toolProxy->mouseDoubleClickEvent(event);
407
408 if (layoutDirection() == Qt::RightToLeft) {
409 // delete event;
410 }
411 delete event;
412 }
413
keyPressed(QKeyEvent * event)414 void CanvasBase::keyPressed(QKeyEvent* event)
415 {
416 // flake
417 if(d->toolProxy)
418 d->toolProxy->keyPressEvent(event);
419 }
420
tabletEvent(QTabletEvent * e)421 void CanvasBase::tabletEvent(QTabletEvent *e)
422 {
423 // flake
424 if(d->toolProxy)
425 d->toolProxy->tabletEvent(e, viewConverter()->viewToDocument(e->pos() + offset()));
426 }
427
inputMethodQuery(Qt::InputMethodQuery query) const428 QVariant CanvasBase::inputMethodQuery(Qt::InputMethodQuery query) const
429 {
430 // flake
431 return d->toolProxy ? d->toolProxy->inputMethodQuery(query, *(viewConverter())) : 0;
432 }
433
inputMethodEvent(QInputMethodEvent * event)434 void CanvasBase::inputMethodEvent(QInputMethodEvent *event)
435 {
436 // flake
437 if(d->toolProxy)
438 d->toolProxy->inputMethodEvent(event);
439 }
440
paint(QPainter * painter,const QRectF & painterRect)441 void CanvasBase::paint(QPainter* painter, const QRectF& painterRect)
442 {
443 if (doc()->map()->isLoading() || isViewLoading())
444 return;
445
446 register Sheet * const sheet = activeSheet();
447 if (!sheet)
448 return;
449
450 // ElapsedTime et("Painting cells", ElapsedTime::PrintOnlyTime);
451
452 painter->setClipRect(painterRect);
453 painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
454 painter->save();
455
456 // After the scaling, the painter methods need document coordinates!
457 qreal zoomX, zoomY;
458 viewConverter()->zoom(&zoomX, &zoomY);
459 painter->scale(zoomX, zoomY);
460
461 const bool layoutReversed = sheet->layoutDirection() == Qt::RightToLeft;
462 const QPointF offset(layoutReversed ? -this->offset().x() : this->offset().x(), this->offset().y());
463 painter->translate(-offset);
464
465 // erase background
466 const QRectF paintRect(viewConverter()->viewToDocument(rect()).translated(offset));
467 painter->fillRect(paintRect, painter->background());
468
469 // paint visible cells
470 const QRect visibleRect = visibleCells();
471 const QPointF topLeft(sheet->columnPosition(visibleRect.left()), sheet->rowPosition(visibleRect.top()));
472 sheetView(sheet)->setPaintCellRange(visibleRect);
473 sheetView(sheet)->paintCells(*painter, paintRect, topLeft, this);
474
475 // flake
476 painter->restore();
477 // d->offset is the negated CanvasController offset in document coordinates.
478 // painter.save();
479 painter->translate(-viewConverter()->documentToView(offset));
480 d->shapeManager->paint(*painter, *viewConverter(), false);
481 // painter.restore();
482 // const QPointF p = -viewConverter()->documentToView(this->offset());
483 // painter.translate(p.x() /*+ width()*/, p.y());
484 painter->setRenderHint(QPainter::Antialiasing, false);
485 if(d->toolProxy)
486 d->toolProxy->paint(*painter, *viewConverter());
487 }
488
focusIn(QFocusEvent * event)489 void CanvasBase::focusIn(QFocusEvent *event)
490 {
491 Q_UNUSED(event);
492 // If we are in editing mode, we redirect the
493 // focus to the CellEditor or ExternalEditor.
494 // Using a focus proxy does not work here, because in reference selection
495 // mode clicking on the canvas to select a reference should end up in the
496 // editor, which got the focus before. This is determined by storing the
497 // last editor with focus. It is set by the editors on getting focus by user
498 // interaction. Setting a focus proxy would always result in the proxy being
499 // the last editor, because clicking the canvas is a user interaction.
500 // This screws up <Tab> though (David)
501 selection()->emitRequestFocusEditor();
502 //XXX TODO QWidget::focusInEvent(event);
503 }
504
dragEnter(const QMimeData * mimeData)505 bool CanvasBase::dragEnter(const QMimeData* mimeData)
506 {
507 if (mimeData->hasText() ||
508 mimeData->hasFormat("application/x-kspread-snippet")) {
509 return true;
510 }
511 return false;
512 }
513
dragMove(const QMimeData * mimeData,const QPointF & eventPos,const QObject * source)514 bool CanvasBase::dragMove(const QMimeData* mimeData, const QPointF& eventPos, const QObject *source)
515 {
516 register Sheet * const sheet = activeSheet();
517 if (!sheet) {
518 return false;
519 }
520
521 if (mimeData->hasText() || mimeData->hasFormat("application/x-kspread-snippet")) {
522 // acceptProposedAction
523 } else {
524 return false;
525 }
526 #if 0 // TODO Stefan: implement drag marking rectangle
527 QRect dragMarkingRect;
528 if (mimeData->hasFormat("application/x-kspread-snippet")) {
529 if (source == canvasWidget()) {
530 debugSheetsUI << "source == this";
531 dragMarkingRect = selection()->boundingRect();
532 } else {
533 debugSheetsUI << "source != this";
534 QByteArray data = mimeData->data("application/x-kspread-snippet");
535 QString errorMsg;
536 int errorLine;
537 int errorColumn;
538 QDomDocument doc;
539 if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorColumn)) {
540 // an error occurred
541 debugSheetsUI << "CanvasBase::daragMoveEvent: an error occurred" << endl
542 << "line: " << errorLine << " col: " << errorColumn
543 << ' ' << errorMsg << endl;
544 dragMarkingRect = QRect(1, 1, 1, 1);
545 } else {
546 QDomElement root = doc.documentElement(); // "spreadsheet-snippet"
547 dragMarkingRect = QRect(1, 1,
548 root.attribute("columns").toInt(),
549 root.attribute("rows").toInt());
550 }
551 }
552 } else { // if ( mimeData->hasText() )
553 debugSheetsUI << "has text";
554 dragMarkingRect = QRect(1, 1, 1, 1);
555 }
556 #else
557 Q_UNUSED(source);
558 #endif
559 const QPoint dragAnchor = selection()->boundingRect().topLeft();
560 double xpos = sheet->columnPosition(dragAnchor.x());
561 double ypos = sheet->rowPosition(dragAnchor.y());
562 double width = sheet->columnFormat(dragAnchor.x())->width();
563 double height = sheet->rowFormats()->rowHeight(dragAnchor.y());
564
565 // consider also the selection rectangle
566 const QRectF noGoArea(xpos - 1, ypos - 1, width + 3, height + 3);
567
568 // determine the current position
569 double eventPosX;
570 if (sheet->layoutDirection() == Qt::RightToLeft) {
571 eventPosX = viewConverter()->viewToDocumentX(this->width() - eventPos.x()) + xOffset();
572 } else {
573 eventPosX = viewConverter()->viewToDocumentX(eventPos.x()) + xOffset();
574 }
575 double eventPosY = viewConverter()->viewToDocumentY(eventPos.y()) + yOffset();
576
577 if (noGoArea.contains(QPointF(eventPosX, eventPosY))) {
578 return false;
579 // XXX TODO event->ignore(noGoArea.toRect());
580 }
581
582 #if 0 // TODO Stefan: implement drag marking rectangle
583 // determine the cell position under the mouse
584 qreal tmp;
585 const int col = sheet->leftColumn(eventPosX, tmp);
586 const int row = sheet->topRow(eventPosY, tmp);
587 dragMarkingRect.moveTo(QPoint(col, row));
588 debugSheetsUI << "MARKING RECT =" << dragMarkingRect;
589 #endif
590 return true;
591 }
592
dragLeave()593 void CanvasBase::dragLeave()
594 {
595 }
596
drop(const QMimeData * mimeData,const QPointF & eventPos,const QObject * source)597 bool CanvasBase::drop(const QMimeData* mimeData, const QPointF& eventPos, const QObject *source)
598 {
599 register Sheet * const sheet = activeSheet();
600 // FIXME Sheet protection: Not all cells have to be protected.
601 if (!sheet || sheet->isProtected()) {
602 return false;
603 }
604
605 if (!PasteCommand::supports(mimeData)) {
606 return false;
607 }
608
609 // Do not allow dropping onto the same position.
610 const QPoint topLeft(selection()->boundingRect().topLeft());
611 const double xpos = sheet->columnPosition(topLeft.x());
612 const double ypos = sheet->rowPosition(topLeft.y());
613 const double width = sheet->columnFormat(topLeft.x())->width();
614 const double height = sheet->rowFormats()->rowHeight(topLeft.y());
615
616 const QRectF noGoArea(xpos - 1, ypos - 1, width + 3, height + 3);
617
618 double ev_PosX;
619 if (sheet->layoutDirection() == Qt::RightToLeft) {
620 ev_PosX = viewConverter()->viewToDocumentX(this->width() - eventPos.x()) + xOffset();
621 } else {
622 ev_PosX = viewConverter()->viewToDocumentX(eventPos.x()) + xOffset();
623 }
624 double ev_PosY = viewConverter()->viewToDocumentY(eventPos.y()) + yOffset();
625
626 if (noGoArea.contains(QPointF(ev_PosX, ev_PosY))) {
627 return false;
628 }
629
630 // The destination cell location.
631 qreal tmp;
632 const int col = sheet->leftColumn(ev_PosX, tmp);
633 const int row = sheet->topRow(ev_PosY, tmp);
634
635 PasteCommand *const command = new PasteCommand();
636 command->setSheet(sheet);
637 command->add(Region(col, row, 1, 1, sheet));
638 command->setMimeData(mimeData);
639
640 if (source == canvasWidget()) {
641 DeleteCommand *const deleteCommand = new DeleteCommand(command);
642 deleteCommand->setSheet(sheet);
643 deleteCommand->add(*selection()); // selection is still, where the drag started
644 deleteCommand->setRegisterUndo(false);
645 }
646
647 command->execute();
648
649 // Select the pasted cells
650 const int columns = selection()->boundingRect().width();
651 const int rows = selection()->boundingRect().height();
652 selection()->initialize(QRect(col, row, columns, rows), sheet);
653
654 return true;
655 }
656
viewToCellCoordinates(const QRectF & viewRect) const657 QRect CanvasBase::viewToCellCoordinates(const QRectF& viewRect) const
658 {
659 register Sheet * const sheet = activeSheet();
660 if (!sheet)
661 return QRect();
662
663 // NOTE Stefan: Do not consider the layout direction in this case.
664 const QRectF rect = viewConverter()->viewToDocument(viewRect.normalized()).translated(offset());
665
666 qreal tmp;
667 const int left = sheet->leftColumn(rect.left(), tmp);
668 const int right = sheet->rightColumn(rect.right());
669 const int top = sheet->topRow(rect.top(), tmp);
670 const int bottom = sheet->bottomRow(rect.bottom());
671
672 return QRect(left, top, right - left + 1, bottom - top + 1);
673 }
674
visibleCells() const675 QRect CanvasBase::visibleCells() const
676 {
677 return viewToCellCoordinates(rect());
678 }
679
680 //---------------------------------------------
681 //
682 // Drawing Engine
683 //
684 //---------------------------------------------
685
cellCoordinatesToView(const QRect & cellRange) const686 QRectF CanvasBase::cellCoordinatesToView(const QRect& cellRange) const
687 {
688 register Sheet * const sheet = activeSheet();
689 if (!sheet)
690 return QRectF();
691
692 QRectF rect = sheet->cellCoordinatesToDocument(cellRange);
693 // apply scrolling offset
694 rect.translate(-xOffset(), -yOffset());
695 // convert it to view coordinates
696 rect = viewConverter()->documentToView(rect);
697 // apply layout direction
698 if (sheet->layoutDirection() == Qt::RightToLeft) {
699 const double left = rect.left();
700 const double right = rect.right();
701 rect.setLeft(width() - right);
702 rect.setRight(width() - left);
703 }
704 return rect;
705 }
706
showToolTip(const QPoint & p)707 void CanvasBase::showToolTip(const QPoint& p)
708 {
709 register Sheet * const sheet = activeSheet();
710 if (!sheet)
711 return;
712 SheetView * const sheetView = this->sheetView(sheet);
713
714 // Over which cell is the mouse ?
715 qreal ypos, xpos;
716 qreal dwidth = viewConverter()->viewToDocumentX(width());
717 int col;
718 if (sheet->layoutDirection() == Qt::RightToLeft)
719 col = sheet->leftColumn((dwidth - viewConverter()->viewToDocumentX(p.x()) +
720 xOffset()), xpos);
721 else
722 col = sheet->leftColumn((viewConverter()->viewToDocumentX(p.x()) +
723 xOffset()), xpos);
724
725
726 int row = sheet->topRow((viewConverter()->viewToDocumentY(p.y()) +
727 yOffset()), ypos);
728
729 Cell cell = Cell(sheet, col, row).masterCell();
730 const CellView& baseCellView = sheetView->cellView(cell.column(), cell.row());
731 const bool baseIsObscured = sheetView->isObscured(cell.cellPosition());
732 const QPoint cellPos = baseIsObscured ? sheetView->obscuringCell(cell.cellPosition())
733 : cell.cellPosition();
734 const CellView& cellView = baseIsObscured
735 ? sheetView->cellView(cellPos)
736 : baseCellView;
737 if (sheetView->isObscured(cellPos)) {
738 cell = Cell(sheet, sheetView->obscuringCell(cellPos));
739 }
740
741 // displayed tool tip, which has the following priorities:
742 // - cell content if the cell dimension is too small
743 // - cell comment
744 // - hyperlink
745 // Ensure that it is plain text.
746 // Not funny if (intentional or not) <a> appears as hyperlink.
747 QString tipText;
748 // If cell is too small, show the content
749 if (!cellView.dimensionFits())
750 tipText = cell.displayText().replace('<', "<");
751
752 // Show hyperlink, if any
753 if (tipText.isEmpty())
754 tipText = cell.link().replace('<', "<");
755
756 // Nothing to display, bail out
757 if (tipText.isEmpty() && cell.comment().isEmpty())
758 return;
759
760 // Cut if the tip is ridiculously long
761 const int maxLen = 256;
762 if (tipText.length() > maxLen)
763 tipText = tipText.left(maxLen).append("...");
764
765 // Determine position and width of the current cell.
766 const double cellWidth = cellView.cellWidth();
767 const double cellHeight = cellView.cellHeight();
768
769 // Get the cell dimensions
770 QRect cellRect;
771 bool insideCellRect = false;
772 if (sheet->layoutDirection() == Qt::RightToLeft) {
773 const QRectF rect(dwidth - cellWidth - xpos + xOffset(), ypos - yOffset(), cellWidth, cellHeight);
774 cellRect = viewConverter()->documentToView(rect).toRect();
775 insideCellRect = cellRect.contains(p);
776 } else {
777 QRectF rect(xpos - xOffset(), ypos - yOffset(), cellWidth, cellHeight);
778 cellRect = viewConverter()->documentToView(rect).toRect();
779 insideCellRect = cellRect.contains(p);
780 }
781
782 // No use if mouse is somewhere else
783 if (!insideCellRect)
784 return;
785
786 // Show comment, if any.
787 if (tipText.isEmpty())
788 tipText = cell.comment().replace('<', "<");
789 else if (!cell.comment().isEmpty())
790 tipText += "</p><h4>" + i18n("Comment:") + "</h4><p>" + cell.comment().replace('<', "<");
791
792 // Now we show the tip
793 QToolTip::showText(mapToGlobal(cellRect.bottomRight()),
794 "<p>" + tipText.replace('\n', "<br>") + "</p>");
795 // TODO XXX this, cellRect.translated(-mapToGlobal(cellRect.topLeft())));
796 }
797
updateInputMethodInfo()798 void CanvasBase::updateInputMethodInfo()
799 {
800 updateMicroFocus();
801 }
802
viewConverter() const803 KoViewConverter* CanvasBase::viewConverter() const
804 {
805 return zoomHandler();
806 }
807