1 /* This file is part of the KDE project
2    Copyright 2009 Thomas Zander <zander@kde.org>
3    Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4    Copyright 2006 Robert Knight <robertknight@gmail.com>
5    Copyright 2006 Inge Wallin <inge@lysator.liu.se>
6    Copyright 1999-2002,2004 Laurent Montel <montel@kde.org>
7    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
8    Copyright 1999-2004 David Faure <faure@kde.org>
9    Copyright 2004-2005 Meni Livne <livne@kde.org>
10    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
11    Copyright 2002-2003 Norbert Andres <nandres@web.de>
12    Copyright 2003 Hamish Rodda <rodda@kde.org>
13    Copyright 2003 Joseph Wenninger <jowenn@kde.org>
14    Copyright 2003 Lukas Tinkl <lukas@kde.org>
15    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
16    Copyright 2002 Harri Porten <porten@kde.org>
17    Copyright 2002 John Dailey <dailey@vt.edu>
18    Copyright 2002 Daniel Naber <daniel.naber@t-online.de>
19    Copyright 1999-2000 Torben Weis <weis@kde.org>
20    Copyright 1999-2000 Stephan Kulow <coolo@kde.org>
21    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
22    Copyright 2000 Wilco Greven <greven@kde.org>
23    Copyright 2000 Simon Hausmann <hausmann@kde.org
24    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
25    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
26    Copyright 1999 Reginald Stadlbauer <reggie@kde.org>
27 
28    This library is free software; you can redistribute it and/or
29    modify it under the terms of the GNU Library General Public
30    License as published by the Free Software Foundation; either
31    version 2 of the License, or (at your option) any later version.
32 
33    This library is distributed in the hope that it will be useful,
34    but WITHOUT ANY WARRANTY; without even the implied warranty of
35    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36    Library General Public License for more details.
37 
38    You should have received a copy of the GNU Library General Public License
39    along with this library; see the file COPYING.LIB.  If not, write to
40    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
41    Boston, MA 02110-1301, USA.
42 */
43 
44 // Local
45 #include "Canvas.h"
46 #include "CanvasBase_p.h"
47 
48 // std
49 #include <assert.h>
50 #include <float.h>
51 #include <stdlib.h>
52 
53 // Qt
54 #include <QApplication>
55 #include <QBuffer>
56 #include <QByteArray>
57 #include <QClipboard>
58 #include <QDragLeaveEvent>
59 #include <QDragMoveEvent>
60 #include <QDropEvent>
61 #include <QEvent>
62 #include <QFocusEvent>
63 #include <QKeyEvent>
64 #include <QLabel>
65 #include <QList>
66 #include <QMenu>
67 #include <QMouseEvent>
68 #include <QPainter>
69 #include <QPaintEvent>
70 #include <QPixmap>
71 #include <QPoint>
72 #include <QScrollBar>
73 #include <QTextStream>
74 #include <QToolTip>
75 #include <QWidget>
76 
77 // KF5
78 #include <kxmlguifactory.h>
79 
80 // Calligra
81 #include <KoCanvasController.h>
82 #include <KoShapeManager.h>
83 #include <KoToolManager.h>
84 #include <KoToolProxy.h>
85 #include <KoZoomHandler.h>
86 #include <KoPointerEvent.h>
87 
88 // Sheets
89 #include "SheetsDebug.h"
90 #include "CellStorage.h"
91 #include "Doc.h"
92 #include "Global.h"
93 #include "HeaderWidgets.h"
94 #include "Localization.h"
95 #include "Map.h"
96 #include "RowColumnFormat.h"
97 #include "Sheet.h"
98 #include "Util.h"
99 #include "Validity.h"
100 #include "View.h"
101 
102 // commands
103 #include "commands/CopyCommand.h"
104 #include "commands/DeleteCommand.h"
105 #include "commands/PasteCommand.h"
106 #include "commands/StyleCommand.h"
107 
108 // ui
109 #include "ui/CellView.h"
110 #include "ui/Selection.h"
111 #include "ui/SheetView.h"
112 
113 #define MIN_SIZE 10
114 
115 using namespace Calligra::Sheets;
116 
117 class Q_DECL_HIDDEN Canvas::Private
118 {
119 public:
120     View *view;
121 };
122 
123 /****************************************************************
124  *
125  * Canvas
126  *
127  ****************************************************************/
128 
Canvas(View * view)129 Canvas::Canvas(View *view)
130         : QWidget(view)
131         , CanvasBase(view ? view->doc() : 0)
132         , cd(new Private)
133 {
134     cd->view = view;
135 
136     setAttribute(Qt::WA_OpaquePaintEvent);
137     setAttribute(Qt::WA_StaticContents);
138     setBackgroundRole(QPalette::Base);
139     QWidget::setFocusPolicy(Qt::StrongFocus);
140     setMouseTracking(true);
141 
142     installEventFilter(this);   // for TAB key processing, otherwise focus change
143     setAcceptDrops(true);
144     setAttribute(Qt::WA_InputMethodEnabled, true); // ensure using the InputMethod
145 }
146 
~Canvas()147 Canvas::~Canvas()
148 {
149     foreach (QAction* action, actions()) {
150         removeAction(action);
151     }
152 
153     delete cd;
154 }
155 
view() const156 View* Canvas::view() const
157 {
158     return cd->view;
159 }
160 
mousePressEvent(QMouseEvent * event)161 void Canvas::mousePressEvent(QMouseEvent* event)
162 {
163     //KoPointerEvent pev(event, QPointF());
164     //mousePressed(&pev);
165 
166     QMouseEvent *const origEvent = event;
167     QPointF documentPosition;
168     if (layoutDirection() == Qt::LeftToRight) {
169         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
170     } else {
171         const QPoint position(QWidget::width() - event->x(), event->y());
172         const QPointF offset(this->offset().x(), this->offset().y());
173         documentPosition = viewConverter()->viewToDocument(position) + offset;
174         debugSheets << "----------------------------";
175         debugSheets << "event->pos():" << event->pos();
176         debugSheets << "event->globalPos():" << event->globalPos();
177         debugSheets << "position:" << position;
178         debugSheets << "offset:" << offset;
179         debugSheets << "documentPosition:" << documentPosition;
180         event = new QMouseEvent(QEvent::MouseButtonPress, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
181         debugSheets << "newEvent->pos():" << event->pos();
182         debugSheets << "newEvent->globalPos():" << event->globalPos();
183     }
184 
185 #if 0  // This is disabled for now as per irc, as it blocks resize.
186     // If the celltool is not active and we initiate a click on something that is not a flake element, we need
187     // to reactivate the cell tool. THis is a temporary solution, pending the flake updates.
188     if (KoToolManager::instance()->activeToolId() != "KSpreadCellToolId")
189         if (!shapeManager()->shapeAt (documentPosition, KoFlake::ShapeOnTop))
190             KoToolManager::instance()->switchToolRequested("KSpreadCellToolId");
191 #endif
192 
193     // flake
194     if(d->toolProxy) {
195         d->toolProxy->mousePressEvent(event, documentPosition);
196 
197         if (!event->isAccepted() && event->button() == Qt::RightButton) {
198             showContextMenu(origEvent->globalPos());
199             origEvent->setAccepted(true);
200         }
201     }
202     if (layoutDirection() == Qt::RightToLeft) {
203         delete event;
204     }
205 }
206 
showContextMenu(const QPoint & globalPos)207 void Canvas::showContextMenu(const QPoint& globalPos)
208 {
209     view()->unplugActionList("toolproxy_action_list");
210     view()->plugActionList("toolproxy_action_list", toolProxy()->popupActionList());
211     if (KXMLGUIFactory *factory = view()->factory()) {
212         QMenu* menu = dynamic_cast<QMenu*>(factory->container("default_canvas_popup", view()));
213         // Only show the menu, if there are items. The plugged action list counts as one action.
214         if (menu && menu->actions().count() > 1) {
215             menu->exec(globalPos);
216         }
217     }
218 }
219 
mouseReleaseEvent(QMouseEvent * event)220 void Canvas::mouseReleaseEvent(QMouseEvent* event)
221 {
222 //    KoPointerEvent pev(event, QPointF());
223 //    mouseReleased(&pev);
224 
225     QPointF documentPosition;
226     if (layoutDirection() == Qt::LeftToRight) {
227         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
228     } else {
229         const QPoint position(QWidget::width() - event->x(), event->y());
230         const QPointF offset(this->offset().x(), this->offset().y());
231         documentPosition = viewConverter()->viewToDocument(position) + offset;
232         event = new QMouseEvent(QEvent::MouseButtonRelease, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
233     }
234 
235     // flake
236     if(d->toolProxy)
237         d->toolProxy->mouseReleaseEvent(event, documentPosition);
238 
239     if (layoutDirection() == Qt::RightToLeft) {
240        delete event;
241     }
242 }
243 
mouseMoveEvent(QMouseEvent * event)244 void Canvas::mouseMoveEvent(QMouseEvent* event)
245 {
246 //    KoPointerEvent pev(event, QPointF());
247 //    mouseMoved(&pev);
248 
249     QPointF documentPosition;
250     if (layoutDirection() == Qt::LeftToRight) {
251         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
252     } else {
253         const QPoint position(QWidget::width() - event->x(), event->y());
254         const QPointF offset(this->offset().x(), this->offset().y());
255         documentPosition = viewConverter()->viewToDocument(position) + offset;
256         event = new QMouseEvent(QEvent::MouseMove, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
257     }
258 
259     // flake
260     if(d->toolProxy)
261         d->toolProxy->mouseMoveEvent(event, documentPosition);
262 
263     if (layoutDirection() == Qt::RightToLeft) {
264        delete event;
265     }
266 }
267 
mouseDoubleClickEvent(QMouseEvent * event)268 void Canvas::mouseDoubleClickEvent(QMouseEvent* event)
269 {
270 //    KoPointerEvent pev(event, QPointF());
271 //    mouseDoubleClicked(&pev);
272 
273     QPointF documentPosition;
274     if (layoutDirection() == Qt::LeftToRight) {
275         documentPosition = viewConverter()->viewToDocument(event->pos()) + offset();
276     } else {
277         const QPoint position(QWidget::width() - event->x(), event->y());
278         const QPointF offset(this->offset().x(), this->offset().y());
279         documentPosition = viewConverter()->viewToDocument(position) + offset;
280         event = new QMouseEvent(QEvent::MouseButtonDblClick, position, mapToGlobal(position), event->button(), event->buttons(), event->modifiers());
281     }
282 
283     // flake
284     if (d->toolProxy) {
285         // If the celltool is not active and the double click is on something that is not a flake element, we need
286         // to reactivate the cell tool. Normally flake would handle this, but the main app is not a shape, so we have to.
287         // TODO: figure out a way to make the flake's functionality work. It'd likely require turning the main app
288         // widget into a KoShape or something along those lines.
289         if (KoToolManager::instance()->activeToolId() != "KSpreadCellToolId") {
290             if (!shapeManager()->shapeAt (documentPosition, KoFlake::ShapeOnTop)) {
291                 KoToolManager::instance()->switchToolRequested("KSpreadCellToolId");
292                 return;
293             }
294         }
295 
296         d->toolProxy->mouseDoubleClickEvent(event, documentPosition);
297     }
298 
299     if (layoutDirection() == Qt::RightToLeft) {
300        // delete event;
301     }
302 
303 }
304 
event(QEvent * e)305 bool Canvas::event(QEvent *e)
306 {
307     if(toolProxy()) {
308         toolProxy()->processEvent(e);
309     }
310     return QWidget::event(e);
311 }
312 
paintEvent(QPaintEvent * event)313 void Canvas::paintEvent(QPaintEvent* event)
314 {
315     QPainter painter(this);
316     paint(&painter, event->rect());
317     event->accept();
318 }
319 
dragEnterEvent(QDragEnterEvent * event)320 void Canvas::dragEnterEvent(QDragEnterEvent* event)
321 {
322     if (CanvasBase::dragEnter(event->mimeData())) {
323         event->acceptProposedAction();
324     }
325 }
326 
dragMoveEvent(QDragMoveEvent * event)327 void Canvas::dragMoveEvent(QDragMoveEvent* event)
328 {
329     if (CanvasBase::dragMove(event->mimeData(), event->pos(), event->source())) {
330         event->acceptProposedAction();
331     } else {
332         event->ignore();
333     }
334 }
335 
dragLeaveEvent(QDragLeaveEvent *)336 void Canvas::dragLeaveEvent(QDragLeaveEvent*)
337 {
338     CanvasBase::dragLeave();
339 }
340 
dropEvent(QDropEvent * event)341 void Canvas::dropEvent(QDropEvent *event)
342 {
343     if (CanvasBase::drop(event->mimeData(), event->pos(), event->source())) {
344         event->setAccepted(true);
345     } else {
346         event->ignore();
347     }
348 }
349 
setVertScrollBarPos(qreal pos)350 void Canvas::setVertScrollBarPos(qreal pos)
351 {
352     if (pos < 0) pos = view()->vertScrollBar()->maximum() - pos;
353     view()->vertScrollBar()->setValue((int)pos);
354 }
355 
setHorizScrollBarPos(qreal pos)356 void Canvas::setHorizScrollBarPos(qreal pos)
357 {
358     if (pos < 0) pos = view()->horzScrollBar()->maximum() - pos;
359     view()->horzScrollBar()->setValue((int)pos);
360 }
361 
zoomHandler() const362 KoZoomHandler* Canvas::zoomHandler() const
363 {
364     return view()->zoomHandler();
365 }
366 
activeSheet() const367 Sheet* Canvas::activeSheet() const
368 {
369     return view()->activeSheet();
370 }
371 
isViewLoading() const372 bool Canvas::isViewLoading() const
373 {
374     return view()->isLoading();
375 }
376 
sheetView(const Sheet * sheet) const377 SheetView* Canvas::sheetView(const Sheet* sheet) const
378 {
379     return view()->sheetView(sheet);
380 }
381 
selection() const382 Calligra::Sheets::Selection* Canvas::selection() const
383 {
384     return view()->selection();
385 }
386 
columnHeader() const387 ColumnHeader* Canvas::columnHeader() const
388 {
389     return view()->columnHeader();
390 }
391 
rowHeader() const392 RowHeader* Canvas::rowHeader() const
393 {
394     return view()->rowHeader();
395 }
396 
enableAutoScroll()397 void Canvas::enableAutoScroll()
398 {
399     view()->enableAutoScroll();
400 }
401 
disableAutoScroll()402 void Canvas::disableAutoScroll()
403 {
404     view()->disableAutoScroll();
405 }
406 
setCursor(const QCursor & cursor)407 void Canvas::setCursor(const QCursor &cursor)
408 {
409     QWidget::setCursor(cursor);
410 }
411