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