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

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 "Canvas.h"
0046 #include "CanvasBase_p.h"
0047 
0048 // std
0049 #include <assert.h>
0050 #include <float.h>
0051 #include <stdlib.h>
0052 
0053 // Qt
0054 #include <QApplication>
0055 #include <QBuffer>
0056 #include <QByteArray>
0057 #include <QClipboard>
0058 #include <QDragLeaveEvent>
0059 #include <QDragMoveEvent>
0060 #include <QDropEvent>
0061 #include <QEvent>
0062 #include <QFocusEvent>
0063 #include <QKeyEvent>
0064 #include <QLabel>
0065 #include <QList>
0066 #include <QMenu>
0067 #include <QMouseEvent>
0068 #include <QPainter>
0069 #include <QPaintEvent>
0070 #include <QPixmap>
0071 #include <QPoint>
0072 #include <QScrollBar>
0073 #include <QTextStream>
0074 #include <QToolTip>
0075 #include <QWidget>
0076 
0077 // KF5
0078 #include <kxmlguifactory.h>
0079 
0080 // Calligra
0081 #include <KoCanvasController.h>
0082 #include <KoShapeManager.h>
0083 #include <KoToolManager.h>
0084 #include <KoToolProxy.h>
0085 #include <KoZoomHandler.h>
0086 #include <KoPointerEvent.h>
0087 
0088 // Sheets
0089 #include "SheetsDebug.h"
0090 #include "CellStorage.h"
0091 #include "Doc.h"
0092 #include "Global.h"
0093 #include "HeaderWidgets.h"
0094 #include "Localization.h"
0095 #include "Map.h"
0096 #include "RowColumnFormat.h"
0097 #include "Sheet.h"
0098 #include "Util.h"
0099 #include "Validity.h"
0100 #include "View.h"
0101 
0102 // commands
0103 #include "commands/CopyCommand.h"
0104 #include "commands/DeleteCommand.h"
0105 #include "commands/PasteCommand.h"
0106 #include "commands/StyleCommand.h"
0107 
0108 // ui
0109 #include "ui/CellView.h"
0110 #include "ui/Selection.h"
0111 #include "ui/SheetView.h"
0112 
0113 #define MIN_SIZE 10
0114 
0115 using namespace Calligra::Sheets;
0116 
0117 class Q_DECL_HIDDEN Canvas::Private
0118 {
0119 public:
0120     View *view;
0121 };
0122 
0123 /****************************************************************
0124  *
0125  * Canvas
0126  *
0127  ****************************************************************/
0128 
0129 Canvas::Canvas(View *view)
0130         : QWidget(view)
0131         , CanvasBase(view ? view->doc() : 0)
0132         , cd(new Private)
0133 {
0134     cd->view = view;
0135 
0136     setAttribute(Qt::WA_OpaquePaintEvent);
0137     setAttribute(Qt::WA_StaticContents);
0138     setBackgroundRole(QPalette::Base);
0139     QWidget::setFocusPolicy(Qt::StrongFocus);
0140     setMouseTracking(true);
0141 
0142     installEventFilter(this);   // for TAB key processing, otherwise focus change
0143     setAcceptDrops(true);
0144     setAttribute(Qt::WA_InputMethodEnabled, true); // ensure using the InputMethod
0145 }
0146 
0147 Canvas::~Canvas()
0148 {
0149     foreach (QAction* action, actions()) {
0150         removeAction(action);
0151     }
0152 
0153     delete cd;
0154 }
0155 
0156 View* Canvas::view() const
0157 {
0158     return cd->view;
0159 }
0160 
0161 void Canvas::mousePressEvent(QMouseEvent* event)
0162 {
0163     //KoPointerEvent pev(event, QPointF());
0164     //mousePressed(&pev);
0165 
0166     QMouseEvent *const origEvent = event;
0167     QPointF documentPosition;
0168     if (layoutDirection() == Qt::LeftToRight) {
0169         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
0170     } else {
0171         const QPoint position(QWidget::width() - event->x(), event->y());
0172         const QPointF offset(this->offset().x(), this->offset().y());
0173         documentPosition = viewConverter()->viewToDocument(position) + offset;
0174         debugSheets << "----------------------------";
0175         debugSheets << "event->pos():" << event->pos();
0176         debugSheets << "event->globalPos():" << event->globalPos();
0177         debugSheets << "position:" << position;
0178         debugSheets << "offset:" << offset;
0179         debugSheets << "documentPosition:" << documentPosition;
0180         event = new QMouseEvent(QEvent::MouseButtonPress, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
0181         debugSheets << "newEvent->pos():" << event->pos();
0182         debugSheets << "newEvent->globalPos():" << event->globalPos();
0183     }
0184 
0185 #if 0  // This is disabled for now as per irc, as it blocks resize.
0186     // If the celltool is not active and we initiate a click on something that is not a flake element, we need
0187     // to reactivate the cell tool. THis is a temporary solution, pending the flake updates.
0188     if (KoToolManager::instance()->activeToolId() != "KSpreadCellToolId")
0189         if (!shapeManager()->shapeAt (documentPosition, KoFlake::ShapeOnTop))
0190             KoToolManager::instance()->switchToolRequested("KSpreadCellToolId");
0191 #endif
0192 
0193     // flake
0194     if(d->toolProxy) {
0195         d->toolProxy->mousePressEvent(event, documentPosition);
0196 
0197         if (!event->isAccepted() && event->button() == Qt::RightButton) {
0198             showContextMenu(origEvent->globalPos());
0199             origEvent->setAccepted(true);
0200         }
0201     }
0202     if (layoutDirection() == Qt::RightToLeft) {
0203         delete event;
0204     }
0205 }
0206 
0207 void Canvas::showContextMenu(const QPoint& globalPos)
0208 {
0209     view()->unplugActionList("toolproxy_action_list");
0210     view()->plugActionList("toolproxy_action_list", toolProxy()->popupActionList());
0211     if (KXMLGUIFactory *factory = view()->factory()) {
0212         QMenu* menu = dynamic_cast<QMenu*>(factory->container("default_canvas_popup", view()));
0213         // Only show the menu, if there are items. The plugged action list counts as one action.
0214         if (menu && menu->actions().count() > 1) {
0215             menu->exec(globalPos);
0216         }
0217     }
0218 }
0219 
0220 void Canvas::mouseReleaseEvent(QMouseEvent* event)
0221 {
0222 //    KoPointerEvent pev(event, QPointF());
0223 //    mouseReleased(&pev);
0224 
0225     QPointF documentPosition;
0226     if (layoutDirection() == Qt::LeftToRight) {
0227         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
0228     } else {
0229         const QPoint position(QWidget::width() - event->x(), event->y());
0230         const QPointF offset(this->offset().x(), this->offset().y());
0231         documentPosition = viewConverter()->viewToDocument(position) + offset;
0232         event = new QMouseEvent(QEvent::MouseButtonRelease, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
0233     }
0234 
0235     // flake
0236     if(d->toolProxy)
0237         d->toolProxy->mouseReleaseEvent(event, documentPosition);
0238 
0239     if (layoutDirection() == Qt::RightToLeft) {
0240        delete event;
0241     }
0242 }
0243 
0244 void Canvas::mouseMoveEvent(QMouseEvent* event)
0245 {
0246 //    KoPointerEvent pev(event, QPointF());
0247 //    mouseMoved(&pev);
0248 
0249     QPointF documentPosition;
0250     if (layoutDirection() == Qt::LeftToRight) {
0251         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
0252     } else {
0253         const QPoint position(QWidget::width() - event->x(), event->y());
0254         const QPointF offset(this->offset().x(), this->offset().y());
0255         documentPosition = viewConverter()->viewToDocument(position) + offset;
0256         event = new QMouseEvent(QEvent::MouseMove, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
0257     }
0258 
0259     // flake
0260     if(d->toolProxy)
0261         d->toolProxy->mouseMoveEvent(event, documentPosition);
0262 
0263     if (layoutDirection() == Qt::RightToLeft) {
0264        delete event;
0265     }
0266 }
0267 
0268 void Canvas::mouseDoubleClickEvent(QMouseEvent* event)
0269 {
0270 //    KoPointerEvent pev(event, QPointF());
0271 //    mouseDoubleClicked(&pev);
0272 
0273     QPointF documentPosition;
0274     if (layoutDirection() == Qt::LeftToRight) {
0275         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
0276     } else {
0277         const QPoint position(QWidget::width() - event->x(), event->y());
0278         const QPointF offset(this->offset().x(), this->offset().y());
0279         documentPosition = viewConverter()->viewToDocument(position) + offset;
0280         event = new QMouseEvent(QEvent::MouseButtonDblClick, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
0281     }
0282 
0283     // flake
0284     if (d->toolProxy) {
0285         // If the celltool is not active and the double click is on something that is not a flake element, we need
0286         // to reactivate the cell tool. Normally flake would handle this, but the main app is not a shape, so we have to.
0287         // TODO: figure out a way to make the flake's functionality work. It'd likely require turning the main app
0288         // widget into a KoShape or something along those lines.
0289         if (KoToolManager::instance()->activeToolId() != "KSpreadCellToolId") {
0290             if (!shapeManager()->shapeAt (documentPosition, KoFlake::ShapeOnTop)) {
0291                 KoToolManager::instance()->switchToolRequested("KSpreadCellToolId");
0292                 return;
0293             }
0294         }
0295 
0296         d->toolProxy->mouseDoubleClickEvent(event, documentPosition);
0297     }
0298 
0299     if (layoutDirection() == Qt::RightToLeft) {
0300        // delete event;
0301     }
0302 
0303 }
0304 
0305 bool Canvas::event(QEvent *e)
0306 {
0307     if(toolProxy()) {
0308         toolProxy()->processEvent(e);
0309     }
0310     return QWidget::event(e);
0311 }
0312 
0313 void Canvas::paintEvent(QPaintEvent* event)
0314 {
0315     QPainter painter(this);
0316     paint(&painter, event->rect());
0317     event->accept();
0318 }
0319 
0320 void Canvas::dragEnterEvent(QDragEnterEvent* event)
0321 {
0322     if (CanvasBase::dragEnter(event->mimeData())) {
0323         event->acceptProposedAction();
0324     }
0325 }
0326 
0327 void Canvas::dragMoveEvent(QDragMoveEvent* event)
0328 {
0329     if (CanvasBase::dragMove(event->mimeData(), event->pos(), event->source())) {
0330         event->acceptProposedAction();
0331     } else {
0332         event->ignore();
0333     }
0334 }
0335 
0336 void Canvas::dragLeaveEvent(QDragLeaveEvent*)
0337 {
0338     CanvasBase::dragLeave();
0339 }
0340 
0341 void Canvas::dropEvent(QDropEvent *event)
0342 {
0343     if (CanvasBase::drop(event->mimeData(), event->pos(), event->source())) {
0344         event->setAccepted(true);
0345     } else {
0346         event->ignore();
0347     }
0348 }
0349 
0350 void Canvas::setVertScrollBarPos(qreal pos)
0351 {
0352     if (pos < 0) pos = view()->vertScrollBar()->maximum() - pos;
0353     view()->vertScrollBar()->setValue((int)pos);
0354 }
0355 
0356 void Canvas::setHorizScrollBarPos(qreal pos)
0357 {
0358     if (pos < 0) pos = view()->horzScrollBar()->maximum() - pos;
0359     view()->horzScrollBar()->setValue((int)pos);
0360 }
0361 
0362 KoZoomHandler* Canvas::zoomHandler() const
0363 {
0364     return view()->zoomHandler();
0365 }
0366 
0367 Sheet* Canvas::activeSheet() const
0368 {
0369     return view()->activeSheet();
0370 }
0371 
0372 bool Canvas::isViewLoading() const
0373 {
0374     return view()->isLoading();
0375 }
0376 
0377 SheetView* Canvas::sheetView(const Sheet* sheet) const
0378 {
0379     return view()->sheetView(sheet);
0380 }
0381 
0382 Calligra::Sheets::Selection* Canvas::selection() const
0383 {
0384     return view()->selection();
0385 }
0386 
0387 ColumnHeader* Canvas::columnHeader() const
0388 {
0389     return view()->columnHeader();
0390 }
0391 
0392 RowHeader* Canvas::rowHeader() const
0393 {
0394     return view()->rowHeader();
0395 }
0396 
0397 void Canvas::enableAutoScroll()
0398 {
0399     view()->enableAutoScroll();
0400 }
0401 
0402 void Canvas::disableAutoScroll()
0403 {
0404     view()->disableAutoScroll();
0405 }
0406 
0407 void Canvas::setCursor(const QCursor &cursor)
0408 {
0409     QWidget::setCursor(cursor);
0410 }