1 /* This file is part of the KDE project
2 Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3 Copyright 2005-2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
4 Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
5 Copyright 1999-2003 Laurent Montel <montel@kde.org>
6 Copyright 2002-2003 Norbert Andres <nandres@web.de>
7 Copyright 2002-2003 Philipp Mueller <philipp.mueller@gmx.de>
8 Copyright 2002-2003 John Dailey <dailey@vt.edu>
9 Copyright 1999-2003 David Faure <faure@kde.org>
10 Copyright 1999-2001 Simon Hausmann <hausmann@kde.org>
11 Copyright 1998-2000 Torben Weis <weis@kde.org>
12 Copyright 2010 Boudewijn Rempt <boud@kogmbh.com>
13
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Library General Public
16 License as published by the Free Software Foundation; either
17 version 2 of the License, or (at your option) any later version.
18
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Library General Public License for more details.
23
24 You should have received a copy of the GNU Library General Public License
25 along with this library; see the file COPYING.LIB. If not, write to
26 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 Boston, MA 02110-1301, USA.
28 */
29
30 // Local
31 #include "View.h"
32 #include "TabBar.h"
33
34 // standard C/C++ includes
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <time.h>
38
39 // Qt includes
40 #include <QBuffer>
41 #include <QByteArray>
42 #include <QClipboard>
43 #include <QCursor>
44 #include <QEvent>
45 #include <QFrame>
46 #include <QGridLayout>
47 #include <QHBoxLayout>
48 #include <QKeyEvent>
49 #include <QList>
50 #include <QMenu>
51 #include <QPixmap>
52 #include <QResizeEvent>
53 #include <QToolButton>
54 #ifndef QT_NO_SQL
55 #include <QSqlDatabase>
56 #endif
57 #include <QSizePolicy>
58 #include <QScrollBar>
59 #include <QStatusBar>
60 #include <QInputDialog>
61 #include <QTimer>
62
63 // KF5 includes
64 #include <kactioncollection.h>
65 #include <kconfig.h>
66
67 #include <kmessagebox.h>
68 #include <kstandardaction.h>
69 #include <ktoggleaction.h>
70 #include <kxmlguifactory.h>
71 #include <kstandardguiitem.h>
72
73 // Calligra includes
74 #include <KoComponentData.h>
75 #include <KoPluginLoader.h>
76 #include <KoGlobal.h>
77 #include <KoColor.h>
78 #include <KoCanvasControllerWidget.h>
79 #include <KoMainWindow.h>
80 #include <KoShapeController.h>
81 #include <KoShapeManager.h>
82 #include <KoSelection.h>
83 #include <KoDockerManager.h>
84 #include <KoToolManager.h>
85 #include <KoTemplateCreateDia.h>
86 #include <KoXmlNS.h>
87 #include <KoZoomAction.h>
88 #include <KoZoomController.h>
89 #include <KoZoomHandler.h>
90 #include <KoToolProxy.h>
91 #include <KoModeBoxFactory.h>
92 #include <KoIcon.h>
93 #include <KoCanvasResourceManager.h>
94 #include <KoCanvasResourceIdentities.h>
95
96 // Sheets includes
97 #include "SheetsDebug.h"
98 #include "ApplicationSettings.h"
99 #include "BindingManager.h"
100 #include "CalculationSettings.h"
101 #include "CellStorage.h"
102 #include "Damages.h"
103 #include "DependencyManager.h"
104 #include "Doc.h"
105 #include "Factory.h"
106 #include "HeaderFooter.h"
107 #include "LoadingInfo.h"
108 #include "Canvas.h"
109 #include "Global.h"
110 #include "Headers.h"
111 #include "HeaderWidgets.h"
112 #include "Localization.h"
113 #include "Map.h"
114 #include "NamedAreaManager.h"
115 #include "PrintSettings.h"
116 #include "RecalcManager.h"
117 #include "RowColumnFormat.h"
118 #include "ShapeApplicationData.h"
119 #include "Sheet.h"
120 #include "SheetPrint.h"
121 #include "Style.h"
122 #include "StyleManager.h"
123 #include "StyleStorage.h"
124 #include "ToolRegistry.h"
125 #include "Util.h"
126 #include "ValueCalc.h"
127 #include "ValueConverter.h"
128 #include "PrintJob.h"
129 #include "ElapsedTime_p.h"
130
131 // commands
132 #include "commands/CopyCommand.h"
133 #include "commands/DefinePrintRangeCommand.h"
134 #include "commands/SheetCommands.h"
135
136 // dialogs
137 #include "dialogs/PageLayoutDialog.h"
138 #include "dialogs/PreferenceDialog.h"
139 #include "dialogs/ShowDialog.h"
140
141 #include "dialogs/SheetPropertiesDialog.h"
142
143 // ui
144 #include "ui/CellView.h"
145 #include "ui/MapViewModel.h"
146 #include "ui/RightToLeftPaintingStrategy.h"
147 #include "ui/Selection.h"
148 #include "ui/SheetView.h"
149 #include "ui/PixmapCachingSheetView.h"
150
151 // D-Bus
152 #ifndef QT_NO_DBUS
153 #include "interfaces/ViewAdaptor.h"
154 #include <knotifyconfigwidget.h>
155 #endif
156
157 using namespace Calligra::Sheets;
158
159 class ViewActions;
160
161 class Q_DECL_HIDDEN View::Private
162 {
163 public:
164 View* view;
165 Doc* doc;
166
167 // the active sheet, may be 0
168 // this is the sheet which has the input focus
169 Sheet* activeSheet;
170 MapViewModel* mapViewModel;
171 QHash<const Sheet*, QPointer<SheetView> > sheetViews;
172
173 // GUI elements
174 QWidget *frame;
175 Canvas *canvas;
176 KoCanvasController* canvasController;
177 KoZoomController* zoomController;
178 KoZoomHandler* zoomHandler;
179 RowHeaderWidget *rowHeader;
180 ColumnHeaderWidget *columnHeader;
181 SelectAllButtonWidget* selectAllButton;
182 QScrollBar *horzScrollBar;
183 QScrollBar *vertScrollBar;
184 TabBar *tabBar;
185 QLabel* calcLabel;
186 QGridLayout* viewLayout;
187 QGridLayout* tabScrollBarLayout;
188
189 // all UI actions
190 ViewActions* actions;
191
192 // if true, Calligra Sheets is still loading the document
193 // don't try to refresh the view
194 bool loading;
195
196 // selection/marker
197 Selection* selection;
198 QMap<Sheet*, QPoint> savedAnchors;
199 QMap<Sheet*, QPoint> savedMarkers;
200 QMap<Sheet*, QPointF> savedOffsets;
201
202 void initActions();
203 void adjustActions(bool mode);
204
205 // On timeout this will execute the status bar operation (e.g. SUM).
206 // This is delayed to speed up the selection.
207 QTimer statusBarOpTimer;
208
209 QTimer *scrollTimer;
210 };
211
212 class ViewActions
213 {
214 public:
215 // sheet/workbook operations
216 QAction * sheetProperties;
217 QAction * insertSheet;
218 QAction * duplicateSheet;
219 QAction * deleteSheet;
220 QAction * renameSheet;
221 QAction * hideSheet;
222 QAction * showSheet;
223
224 //Shape manipulation
225 QAction * deleteShape;
226
227 // page layout
228 QAction * paperLayout;
229 QAction * resetPrintRange;
230 KToggleAction* showPageOutline;
231
232 // recalculation
233 QAction * recalcWorksheet;
234 QAction * recalcWorkbook;
235
236 // protection
237 KToggleAction* protectSheet;
238 KToggleAction* protectDoc;
239
240 // navigation
241 QAction * nextSheet;
242 QAction * prevSheet;
243 QAction * firstSheet;
244 QAction * lastSheet;
245
246 // misc
247 QAction * createTemplate;
248 KSelectAction* shapeAnchor;
249
250 // settings
251 KToggleAction* showColumnHeader;
252 KToggleAction* showRowHeader;
253 KToggleAction* showHorizontalScrollBar;
254 KToggleAction* showVerticalScrollBar;
255 KToggleAction* showStatusBar;
256 KToggleAction* showTabBar;
257 QAction * preference;
258
259 // running calculation
260 KToggleAction* calcNone;
261 KToggleAction* calcMin;
262 KToggleAction* calcMax;
263 KToggleAction* calcAverage;
264 KToggleAction* calcCount;
265 KToggleAction* calcSum;
266 KToggleAction* calcCountA;
267 };
268
269
initActions()270 void View::Private::initActions()
271 {
272 actions = new ViewActions;
273
274 KActionCollection* ac = view->actionCollection();
275
276 // -- sheet/workbook actions --
277 actions->sheetProperties = new QAction(i18n("Sheet Properties..."), view);
278 ac->addAction("sheetProperties", actions->sheetProperties);
279 connect(actions->sheetProperties, SIGNAL(triggered(bool)), view, SLOT(sheetProperties()));
280 actions->sheetProperties->setToolTip(i18n("Modify current sheet's properties"));
281
282 actions->insertSheet = new QAction(koIcon("insert-table"), i18n("Sheet"), view);
283 actions->insertSheet->setIconText(i18n("Insert Sheet"));
284 actions->insertSheet->setToolTip(i18n("Insert a new sheet"));
285 ac->addAction("insertSheet", actions->insertSheet);
286 connect(actions->insertSheet, SIGNAL(triggered(bool)), view, SLOT(insertSheet()));
287
288 actions->duplicateSheet = new QAction(/*koIcon("inserttable"),*/ i18n("Duplicate Sheet"), view);
289 actions->duplicateSheet->setToolTip(i18n("Duplicate the selected sheet"));
290 ac->addAction("duplicateSheet", actions->duplicateSheet);
291 connect(actions->duplicateSheet, SIGNAL(triggered(bool)), view, SLOT(duplicateSheet()));
292
293 actions->deleteSheet = new QAction(koIcon("edit-delete"), i18n("Sheet"), view);
294 actions->deleteSheet->setIconText(i18n("Remove Sheet"));
295 actions->deleteSheet->setToolTip(i18n("Remove the active sheet"));
296 ac->addAction("deleteSheet", actions->deleteSheet);
297 connect(actions->deleteSheet, SIGNAL(triggered(bool)), view, SLOT(deleteSheet()));
298
299 actions->renameSheet = new QAction(i18n("Rename Sheet..."), view);
300 ac->addAction("renameSheet", actions->renameSheet);
301 connect(actions->renameSheet, SIGNAL(triggered(bool)), view, SLOT(slotRename()));
302 actions->renameSheet->setToolTip(i18n("Rename the active sheet"));
303
304 actions->showSheet = new QAction(i18n("Show Sheet..."), view);
305 ac->addAction("showSheet", actions->showSheet);
306 connect(actions->showSheet, SIGNAL(triggered(bool)), view, SLOT(showSheet()));
307 actions->showSheet->setToolTip(i18n("Show a hidden sheet"));
308
309 actions->hideSheet = new QAction(i18n("Hide Sheet"), view);
310 ac->addAction("hideSheet", actions->hideSheet);
311 connect(actions->hideSheet, SIGNAL(triggered(bool)), view, SLOT(hideSheet()));
312 actions->hideSheet->setToolTip(i18n("Hide the active sheet"));
313
314 actions->paperLayout = new QAction(i18n("Page Layout..."), view);
315 ac->addAction("paperLayout", actions->paperLayout);
316 connect(actions->paperLayout, SIGNAL(triggered(bool)), view, SLOT(paperLayoutDlg()));
317 actions->paperLayout->setToolTip(i18n("Specify the layout of the spreadsheet for a printout"));
318
319 actions->resetPrintRange = new QAction(i18n("Reset Print Range"), view);
320 ac->addAction("resetPrintRange", actions->resetPrintRange);
321 connect(actions->resetPrintRange, SIGNAL(triggered(bool)), view, SLOT(resetPrintRange()));
322 actions->resetPrintRange->setToolTip(i18n("Reset the print range in the current sheet"));
323
324 actions->showPageOutline = new KToggleAction(i18n("Page Outline"), view);
325 actions->showPageOutline->setToolTip(i18n("Show on the spreadsheet where the page boundary will be"));
326 ac->addAction("showPageOutline", actions->showPageOutline);
327 connect(actions->showPageOutline, SIGNAL(toggled(bool)), view, SLOT(togglePageOutline(bool)));
328
329 actions->recalcWorksheet = new QAction(i18n("Recalculate Sheet"), view);
330 actions->recalcWorksheet->setIcon(koIcon("view-refresh"));
331 actions->recalcWorksheet->setIconText(i18n("Recalculate"));
332 ac->addAction("RecalcWorkSheet", actions->recalcWorksheet);
333 actions->recalcWorksheet->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F9));
334 connect(actions->recalcWorksheet, SIGNAL(triggered(bool)), view, SLOT(recalcWorkSheet()));
335 actions->recalcWorksheet->setToolTip(i18n("Recalculate the value of every cell in the current worksheet"));
336
337 actions->recalcWorkbook = new QAction(i18n("Recalculate Document"), view);
338 actions->recalcWorkbook->setIcon(koIcon("view-refresh"));
339 actions->recalcWorkbook->setIconText(i18n("Recalculate"));
340 ac->addAction("RecalcWorkBook", actions->recalcWorkbook);
341 actions->recalcWorkbook->setShortcut(QKeySequence(Qt::Key_F9));
342 connect(actions->recalcWorkbook, SIGNAL(triggered(bool)), view, SLOT(recalcWorkBook()));
343 actions->recalcWorkbook->setToolTip(i18n("Recalculate the value of every cell in all worksheets"));
344
345 actions->protectSheet = new KToggleAction(i18n("Protect &Sheet..."), view);
346 ac->addAction("protectSheet", actions->protectSheet);
347 actions->protectSheet->setToolTip(i18n("Protect the sheet from being modified"));
348 connect(actions->protectSheet, SIGNAL(triggered(bool)),
349 view, SLOT(toggleProtectSheet(bool)));
350
351 actions->protectDoc = new KToggleAction(i18n("Protect &Document..."), view);
352 ac->addAction("protectDoc", actions->protectDoc);
353 actions->protectDoc->setToolTip(i18n("Protect the document from being modified"));
354 connect(actions->protectDoc, SIGNAL(triggered(bool)), view, SLOT(toggleProtectDoc(bool)));
355
356 // -- misc actions --
357
358 actions->createTemplate = new QAction(i18n("&Create Template From Document..."), view);
359 ac->addAction("createTemplate", actions->createTemplate);
360 connect(actions->createTemplate, SIGNAL(triggered(bool)), view, SLOT(createTemplate()));
361
362 actions->shapeAnchor = new KSelectAction(i18n("Anchor"), view);
363 actions->shapeAnchor->addAction(i18n("Cell"));
364 actions->shapeAnchor->addAction(i18n("Page"));
365 actions->shapeAnchor->setEnabled(false);
366 actions->shapeAnchor->setToolTip(i18n("Switch shape anchoring"));
367 ac->addAction("shapeAnchor", actions->shapeAnchor);
368 connect(actions->shapeAnchor, SIGNAL(triggered(QString)),
369 view, SLOT(setShapeAnchoring(QString)));
370
371 // -- navigation actions --
372
373 actions->nextSheet = new QAction(koIcon("go-next"), i18n("Next Sheet"), view);
374 actions->nextSheet->setIconText(i18n("Next"));
375 actions->nextSheet->setToolTip(i18n("Move to the next sheet"));
376 ac->addAction("go_next", actions->nextSheet);
377 actions->nextSheet->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageDown));
378 connect(actions->nextSheet, SIGNAL(triggered(bool)), view, SLOT(nextSheet()));
379
380 actions->prevSheet = new QAction(koIcon("go-previous"), i18n("Previous Sheet"), view);
381 actions->prevSheet->setIconText(i18n("Previous"));
382 actions->prevSheet->setToolTip(i18n("Move to the previous sheet"));
383 ac->addAction("go_previous", actions->prevSheet);
384 actions->prevSheet->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageUp));
385 connect(actions->prevSheet, SIGNAL(triggered(bool)), view, SLOT(previousSheet()));
386
387 actions->firstSheet = new QAction(koIcon("go-first"), i18n("First Sheet"), view);
388 actions->firstSheet->setIconText(i18n("First"));
389 actions->firstSheet->setToolTip(i18n("Move to the first sheet"));
390 ac->addAction("go_first", actions->firstSheet);
391 connect(actions->firstSheet, SIGNAL(triggered(bool)), view, SLOT(firstSheet()));
392
393 actions->lastSheet = new QAction(koIcon("go-last"), i18n("Last Sheet"), view);
394 actions->lastSheet->setIconText(i18nc("Move to the last sheet", "Last"));
395 actions->lastSheet->setToolTip(i18n("Move to the last sheet"));
396 ac->addAction("go_last", actions->lastSheet);
397 connect(actions->lastSheet, SIGNAL(triggered(bool)), view, SLOT(lastSheet()));
398
399 // -- settings actions --
400
401 actions->showColumnHeader = new KToggleAction(i18n("Column Header"), view);
402 actions->showColumnHeader->setToolTip(i18n("Show the column header"));
403 ac->addAction("showColumnHeader", actions->showColumnHeader);
404 connect(actions->showColumnHeader, SIGNAL(toggled(bool)),
405 view, SLOT(showColumnHeader(bool)));
406
407 actions->showRowHeader = new KToggleAction(i18n("Row Header"), view);
408 actions->showRowHeader->setToolTip(i18n("Show the row header"));
409 ac->addAction("showRowHeader", actions->showRowHeader);
410 connect(actions->showRowHeader, SIGNAL(toggled(bool)),
411 view, SLOT(showRowHeader(bool)));
412
413 actions->showHorizontalScrollBar = new KToggleAction(i18n("Horizontal Scrollbar"), view);
414 actions->showHorizontalScrollBar->setToolTip(i18n("Show the horizontal scrollbar"));
415 ac->addAction("showHorizontalScrollBar", actions->showHorizontalScrollBar);
416 connect(actions->showHorizontalScrollBar, SIGNAL(toggled(bool)),
417 view, SLOT(showHorizontalScrollBar(bool)));
418
419 actions->showVerticalScrollBar = new KToggleAction(i18n("Vertical Scrollbar"), view);
420 actions->showVerticalScrollBar->setToolTip(i18n("Show the vertical scrollbar"));
421 ac->addAction("showVerticalScrollBar", actions->showVerticalScrollBar);
422 connect(actions->showVerticalScrollBar, SIGNAL(toggled(bool)),
423 view, SLOT(showVerticalScrollBar(bool)));
424
425 actions->showStatusBar = new KToggleAction(i18n("Status Bar"), view);
426 actions->showStatusBar->setToolTip(i18n("Show the status bar"));
427 ac->addAction("showStatusBar", actions->showStatusBar);
428 connect(actions->showStatusBar, SIGNAL(toggled(bool)),
429 view, SLOT(showStatusBar(bool)));
430
431 actions->showTabBar = new KToggleAction(i18n("Tab Bar"), view);
432 actions->showTabBar->setToolTip(i18n("Show the tab bar"));
433 ac->addAction("showTabBar", actions->showTabBar);
434 connect(actions->showTabBar, SIGNAL(toggled(bool)),
435 view, SLOT(showTabBar(bool)));
436
437 actions->preference = KStandardAction::preferences(view, SLOT(preference()), view);
438 actions->preference->setToolTip(i18n("Set various Calligra Sheets options"));
439 ac->addAction("preference", actions->preference);
440
441 QAction *notifyAction = KStandardAction::configureNotifications(view, SLOT(optionsNotifications()), view);
442 ac->addAction("configureNotifications", notifyAction);
443
444 // -- calculation actions --
445 //
446 QActionGroup* groupCalc = new QActionGroup(view);
447 actions->calcNone = new KToggleAction(i18n("None"), view);
448 ac->addAction("menu_none", actions->calcNone);
449 connect(actions->calcNone, SIGNAL(toggled(bool)),
450 view, SLOT(menuCalc(bool)));
451 actions->calcNone->setToolTip(i18n("No calculation"));
452 actions->calcNone->setActionGroup(groupCalc);
453
454 actions->calcSum = new KToggleAction(i18n("Sum"), view);
455 ac->addAction("menu_sum", actions->calcSum);
456 connect(actions->calcSum, SIGNAL(toggled(bool)),
457 view, SLOT(menuCalc(bool)));
458 actions->calcSum->setToolTip(i18n("Calculate using sum"));
459 actions->calcSum->setActionGroup(groupCalc);
460
461 actions->calcMin = new KToggleAction(i18n("Min"), view);
462 ac->addAction("menu_min", actions->calcMin);
463 connect(actions->calcMin, SIGNAL(toggled(bool)),
464 view, SLOT(menuCalc(bool)));
465 actions->calcMin->setToolTip(i18n("Calculate using minimum"));
466 actions->calcMin->setActionGroup(groupCalc);
467
468 actions->calcMax = new KToggleAction(i18n("Max"), view);
469 ac->addAction("menu_max", actions->calcMax);
470 connect(actions->calcMax, SIGNAL(toggled(bool)),
471 view, SLOT(menuCalc(bool)));
472 actions->calcMax->setToolTip(i18n("Calculate using maximum"));
473 actions->calcMax->setActionGroup(groupCalc);
474
475 actions->calcAverage = new KToggleAction(i18n("Average"), view);
476 ac->addAction("menu_average", actions->calcAverage);
477 connect(actions->calcAverage, SIGNAL(toggled(bool)),
478 view, SLOT(menuCalc(bool)));
479 actions->calcAverage->setToolTip(i18n("Calculate using average"));
480 actions->calcAverage->setActionGroup(groupCalc);
481
482 actions->calcCount = new KToggleAction(i18n("Count"), view);
483 ac->addAction("menu_count", actions->calcCount);
484 connect(actions->calcCount, SIGNAL(toggled(bool)),
485 view, SLOT(menuCalc(bool)));
486 actions->calcCount->setToolTip(i18n("Calculate using the count"));
487 actions->calcCount->setActionGroup(groupCalc);
488
489 actions->calcCountA = new KToggleAction(i18n("CountA"), view);
490 ac->addAction("menu_counta", actions->calcCountA);
491 connect(actions->calcCountA, SIGNAL(toggled(bool)),
492 view, SLOT(menuCalc(bool)));
493 actions->calcCountA->setToolTip(i18n("Calculate using the countA"));
494 actions->calcCountA->setActionGroup(groupCalc);
495
496 //Shape actions
497 actions->deleteShape = new QAction(koIcon("edit-delete"), i18n("Delete"), view);
498 actions->deleteShape->setShortcut(QKeySequence("Del"));
499 connect(actions->deleteShape, SIGNAL(triggered()), view, SLOT(editDeleteSelection()));
500 connect(canvas->toolProxy(), SIGNAL(selectionChanged(bool)), actions->deleteShape, SLOT(setEnabled(bool)));
501 ac->addAction("edit_delete", actions->deleteShape);
502
503 // -- special action, only for developers --
504 //
505
506 ac->addAssociatedWidget(view->canvasWidget());
507 foreach(QAction* action, ac->actions()) {
508 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
509 }
510 }
511
adjustActions(bool mode)512 void View::Private::adjustActions(bool mode)
513 {
514 actions->recalcWorkbook->setEnabled(mode);
515 actions->recalcWorksheet->setEnabled(mode);
516 actions->paperLayout->setEnabled(mode);
517 actions->resetPrintRange->setEnabled(mode);
518 actions->deleteSheet->setEnabled(mode);
519 actions->calcMin->setEnabled(mode);
520 actions->calcMax->setEnabled(mode);
521 actions->calcAverage->setEnabled(mode);
522 actions->calcCount->setEnabled(mode);
523 actions->calcCountA->setEnabled(mode);
524 actions->calcSum->setEnabled(mode);
525 actions->calcNone->setEnabled(mode);
526
527 if (mode && !view->doc()->map()->isProtected())
528 actions->renameSheet->setEnabled(true);
529 else
530 actions->renameSheet->setEnabled(false);
531
532 actions->showColumnHeader->setChecked(view->doc()->map()->settings()->showColumnHeader());
533 actions->showRowHeader->setChecked(view->doc()->map()->settings()->showRowHeader());
534 actions->showHorizontalScrollBar->setChecked(view->doc()->map()->settings()->showHorizontalScrollBar());
535 actions->showVerticalScrollBar->setChecked(view->doc()->map()->settings()->showVerticalScrollBar());
536 actions->showStatusBar->setChecked(view->doc()->map()->settings()->showStatusBar());
537 actions->showTabBar->setChecked(view->doc()->map()->settings()->showTabBar());
538
539 if (activeSheet)
540 selection->update();
541 }
542
543
544 /*****************************************************************************
545 *
546 * View
547 *
548 *****************************************************************************/
549
View(KoPart * part,QWidget * _parent,Doc * _doc)550 View::View(KoPart *part, QWidget *_parent, Doc *_doc)
551 : KoView(part, _doc, _parent)
552 , d(new Private)
553 {
554 ElapsedTime et("View constructor");
555
556 d->view = this;
557 d->doc = _doc;
558
559 d->activeSheet = 0;
560
561 d->loading = true;
562
563 setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName());
564 setXMLFile("calligrasheets.rc");
565
566 // GUI Initializations
567 initView();
568
569 d->initActions();
570
571 const QList<KPluginFactory *> pluginFactories =
572 KoPluginLoader::instantiatePluginFactories(QStringLiteral("calligrasheets/extensions"));
573
574 foreach (KPluginFactory* factory, pluginFactories) {
575 QObject *object = factory->create<QObject>(this, QVariantList());
576 KXMLGUIClient *clientPlugin = dynamic_cast<KXMLGUIClient*>(object);
577 if (clientPlugin) {
578 insertChildClient(clientPlugin);
579 } else {
580 // not our/valid plugin, so delete the created object
581 object->deleteLater();
582 }
583 }
584
585 // Connect updateView() signal to View::update() in order to repaint its
586 // child widgets: the column/row headers and the select all button.
587 // Connect to Canvas::update() explicitly as it lives in the viewport
588 // of the KoCanvasController.
589 connect(doc(), SIGNAL(updateView()),
590 this, SLOT(update()));
591 connect(doc(), SIGNAL(updateView()),
592 d->canvas, SLOT(update()));
593 connect(doc()->map(), SIGNAL(sheetAdded(Sheet*)),
594 this, SLOT(addSheet(Sheet*)));
595 connect(doc()->map(), SIGNAL(sheetRemoved(Sheet*)),
596 this, SLOT(removeSheet(Sheet*)));
597 connect(doc()->map(), SIGNAL(sheetRevived(Sheet*)),
598 this, SLOT(addSheet(Sheet*)));
599 connect(doc()->map(), SIGNAL(damagesFlushed(QList<Damage*>)),
600 this, SLOT(handleDamages(QList<Damage*>)));
601 if (statusBar()) {
602 connect(doc()->map(), SIGNAL(statusMessage(QString,int)),
603 statusBar(), SLOT(showMessage(QString,int)));
604 }
605
606 connect(&d->statusBarOpTimer, SIGNAL(timeout()), this, SLOT(calcStatusBarOp()));
607
608 // Delay the setting of the initial position, because we need to have
609 // a sensible widget size, which is not always the case from the beginning
610 // of the View's lifetime.
611 // Therefore, initialPosition(), the last operation in the "View loading"
612 // process, is called from resizeEvent(). The loading flag will be unset
613 // at the end of initialPosition().
614
615 #ifndef QT_NO_DBUS
616 new ViewAdaptor(this);
617 #endif
618
619 d->scrollTimer = new QTimer(this);
620 connect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
621
622 initialPosition();
623
624 d->canvas->setFocus();
625 }
626
~View()627 View::~View()
628 {
629 selection()->emitCloseEditor(true); // save changes
630
631 // if (d->calcLabel) disconnect(d->calcLabel,SIGNAL(pressed(int)),this,SLOT(statusBarClicked(int)));
632
633 d->selection->emitCloseEditor(false);
634 d->selection->endReferenceSelection(false);
635 d->activeSheet = 0; // set the active sheet to 0 so that when during destruction
636 // of embedded child documents possible repaints in Sheet are not
637 // performed.
638
639 // delete the sheetView's after calling d->selection->emitCloseEditor cause the
640 // emitCloseEditor may trigger over the Selection::emitChanged a Canvas::scrollToCell
641 // which in turn needs the sheetview's to access the sheet itself.
642 qDeleteAll(d->sheetViews.values());
643
644 delete d->scrollTimer;
645
646 delete d->selection;
647 d->selection = 0;
648 delete d->calcLabel;
649 delete d->actions;
650 delete d->zoomHandler;
651
652 // NOTE sebsauer: first unregister the event-handler, then delete the canvas and then we are save to
653 // call removeCanvasController without crashing.
654 //d->canvasController->canvas()->canvasWidget()->removeEventFilter(d->canvasController);
655 //delete d->canvasController->canvas();
656 // NOTE sebsauer: We need to remove the canvasController right before deleting it and
657 // nothing needs to be done in between cause flake does first delete the TableTool
658 // on removeCanvasController and the proxytool which points to that TableTool later
659 // while the canvasController is destroyed. That means, that we will have a dangling
660 // pointer in the KoToolProxy that points to the KoToolBase the time in between.
661 KoToolManager::instance()->removeCanvasController(d->canvasController);
662 // NOTE Stefan: Delete the Canvas explicitly, even if it has this view as
663 // parent. Otherwise, it leads to crashes, because it tries to
664 // access this View in some events (Bug #126492).
665 // The KoCanvasController takes ownership of the Canvas and does the deletion.
666 delete d->canvasController;
667 delete d;
668 }
669
doc() const670 Doc* View::doc() const
671 {
672 return d->doc;
673 }
674
675 // should be called only once, from the constructor
676 /*
677 * Central part is the canvas, row header and vertical scrollbar.
678 * Bottom part is the tab bar and horizontal scrollbar.
679 *
680 * Note that canvas must the one to be created, since other
681 * widgets might depend on it.
682 */
683
initView()684 void View::initView()
685 {
686 d->viewLayout = new QGridLayout(this);
687 d->viewLayout->setMargin(0);
688 d->viewLayout->setSpacing(0);
689
690 // Setup the Canvas and its controller.
691 d->canvas = new Canvas(this);
692 KoCanvasControllerWidget *canvasController = new KoCanvasControllerWidget(actionCollection(), this);
693 d->canvasController = canvasController;
694 d->canvasController->setCanvas(d->canvas);
695 d->canvasController->setCanvasMode(KoCanvasController::Spreadsheet);
696 canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
697 canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
698
699 // Setup the map model.
700 d->mapViewModel = new MapViewModel(d->doc->map(), d->canvas, this);
701 connect(d->mapViewModel, SIGNAL(addCommandRequested(KUndo2Command*)),
702 doc(), SLOT(addCommand(KUndo2Command*)));
703 connect(d->mapViewModel, SIGNAL(activeSheetChanged(Sheet*)),
704 this, SLOT(setActiveSheet(Sheet*)));
705
706 // Setup the selection.
707 d->selection = new Selection(d->canvas);
708 connect(d->selection, SIGNAL(changed(Region)), this, SLOT(slotChangeSelection(Region)));
709 connect(d->selection, SIGNAL(changed(Region)), this, SLOT(slotScrollChoice(Region)));
710 connect(d->selection, SIGNAL(aboutToModify(Region)), this, SLOT(aboutToModify(Region)));
711 connect(d->selection, SIGNAL(modified(Region)), this, SLOT(refreshSelection(Region)));
712 connect(d->selection, SIGNAL(visibleSheetRequested(Sheet*)), this, SLOT(setActiveSheet(Sheet*)));
713 connect(d->selection, SIGNAL(refreshSheetViews()), this, SLOT(refreshSheetViews()));
714 connect(d->selection, SIGNAL(updateAccessedCellRange(Sheet*,QPoint)), this, SLOT(updateAccessedCellRange(Sheet*,QPoint)));
715 connect(this, SIGNAL(documentReadWriteToggled(bool)),
716 d->selection, SIGNAL(documentReadWriteToggled(bool)));
717 connect(this, SIGNAL(sheetProtectionToggled(bool)),
718 d->selection, SIGNAL(sheetProtectionToggled(bool)));
719
720 // Let the selection pointer become a canvas resource.
721 QVariant variant;
722 variant.setValue<void*>(d->selection);
723 d->canvas->resourceManager()->setResource(::Sheets::CanvasResource::Selection, variant);
724 variant.setValue<QObject*>(doc()->map()->bindingManager());
725
726 // Load the Calligra Sheets Tools
727 ToolRegistry::instance()->loadTools();
728
729 if (mainWindow())
730 {
731 KoToolManager::instance()->addController(d->canvasController);
732 KoToolManager::instance()->registerTools(actionCollection(), d->canvasController);
733 KoModeBoxFactory modeBoxFactory(canvasController, qApp->applicationName(), i18n("Tools"));
734 QDockWidget* modeBox = mainWindow()->createDockWidget(&modeBoxFactory);
735 mainWindow()->dockerManager()->removeToolOptionsDocker();
736 dynamic_cast<KoCanvasObserverBase*>(modeBox)->setObservedCanvas(d->canvas);
737
738 // Setup the tool options dock widget manager.
739 //connect(canvasController, SIGNAL(toolOptionWidgetsChanged(QList<QPointer<QWidget> >)),
740 // mainWindow()->dockerManager(), SLOT(newOptionWidgets(QList<QPointer<QWidget> >)));
741 }
742 // Setup the zoom controller.
743 d->zoomHandler = new KoZoomHandler();
744 d->zoomController = new KoZoomController(d->canvasController, d->zoomHandler, actionCollection(), 0, this);
745 d->zoomController->zoomAction()->setZoomModes(KoZoomMode::ZOOM_CONSTANT);
746 QWidget *zoomWidget = d->zoomController->zoomAction()->createWidget(statusBar());
747 zoomWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
748 addStatusBarItem(zoomWidget, 0, true);
749 connect(d->zoomController, SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
750 this, SLOT(viewZoom(KoZoomMode::Mode,qreal)));
751
752 d->columnHeader = new ColumnHeaderWidget(this, d->canvas, this);
753 d->rowHeader = new RowHeaderWidget(this, d->canvas , this);
754 d->columnHeader->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
755 d->rowHeader->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
756 d->selectAllButton = new SelectAllButtonWidget(d->canvas);
757 d->selectAllButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
758
759 d->canvas->setFocusPolicy(Qt::StrongFocus);
760 QWidget::setFocusPolicy(Qt::StrongFocus);
761 setFocusProxy(d->canvas);
762
763 // Vert. Scroll Bar
764 d->calcLabel = 0;
765 d->vertScrollBar = new QScrollBar(this);
766 canvasController->setVerticalScrollBar(d->vertScrollBar);
767 connect(d->vertScrollBar, SIGNAL(valueChanged(int)), canvasController, SLOT(updateCanvasOffsetY()));
768 d->vertScrollBar->setOrientation(Qt::Vertical);
769 d->vertScrollBar->setSingleStep(60); //just random guess based on what feels okay
770 d->vertScrollBar->setPageStep(60); //This should be controlled dynamically, depending on how many rows are shown
771
772 QWidget* bottomPart = new QWidget(this);
773 d->tabScrollBarLayout = new QGridLayout(bottomPart);
774 d->tabScrollBarLayout->setMargin(0);
775 d->tabScrollBarLayout->setSpacing(0);
776 d->tabScrollBarLayout->setColumnStretch(1, 1);
777 d->tabBar = new TabBar(0);
778 d->tabScrollBarLayout->addWidget(d->tabBar, 0, 0);
779 d->horzScrollBar = new QScrollBar(0);
780 canvasController->setHorizontalScrollBar(d->horzScrollBar);
781 connect(d->horzScrollBar, SIGNAL(valueChanged(int)), canvasController, SLOT(updateCanvasOffsetX()));
782 d->tabScrollBarLayout->addWidget(d->horzScrollBar, 0, 1, 2, 1, Qt::AlignVCenter);
783
784 d->horzScrollBar->setOrientation(Qt::Horizontal);
785 d->horzScrollBar->setSingleStep(60); //just random guess based on what feels okay
786 d->horzScrollBar->setPageStep(60);
787
788 connect(d->tabBar, SIGNAL(tabChanged(QString)), this, SLOT(changeSheet(QString)));
789 connect(d->tabBar, SIGNAL(tabMoved(unsigned,unsigned)),
790 this, SLOT(moveSheet(unsigned,unsigned)));
791 connect(d->tabBar, SIGNAL(contextMenu(QPoint)),
792 this, SLOT(popupTabBarMenu(QPoint)));
793 connect(d->tabBar, SIGNAL(doubleClicked()),
794 this, SLOT(slotRename()));
795
796 int extent = this->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
797 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
798 extent += style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
799 }
800
801 d->viewLayout->setColumnStretch(1, 10);
802 d->viewLayout->setRowStretch(2, 10);
803 d->viewLayout->addWidget(d->selectAllButton, 1, 0);
804 d->viewLayout->addWidget(d->columnHeader, 1, 1, 1, 1);
805 d->viewLayout->addWidget(d->rowHeader, 2, 0);
806 d->viewLayout->addWidget(canvasController, 2, 1);
807 d->viewLayout->addWidget(d->vertScrollBar, 1, 2, 2, 1, Qt::AlignHCenter);
808 d->viewLayout->addWidget(bottomPart, 3, 0, 1, 2);
809 d->viewLayout->setColumnMinimumWidth(2, extent);
810 d->viewLayout->setRowMinimumHeight(3, extent);
811
812 QStatusBar * sb = statusBar();
813 d->calcLabel = sb ? new QLabel(sb) : 0;
814 if (d->calcLabel) {
815 d->calcLabel->setContextMenuPolicy(Qt::CustomContextMenu);
816 addStatusBarItem(d->calcLabel, 0);
817 connect(d->calcLabel , SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(statusBarClicked(QPoint)));
818 }
819
820 // signal slot
821 connect(d->canvas, SIGNAL(documentSizeChanged(QSize)),
822 d->canvasController->proxyObject, SLOT(updateDocumentSize(QSize)));
823 connect(d->canvasController->proxyObject, SIGNAL(moveDocumentOffset(QPoint)),
824 d->canvas, SLOT(setDocumentOffset(QPoint)));
825 connect(d->canvas->shapeManager(), SIGNAL(selectionChanged()),
826 this, SLOT(shapeSelectionChanged()));
827 }
828
canvasWidget() const829 Canvas* View::canvasWidget() const
830 {
831 return d->canvas;
832 }
833
zoomController() const834 KoZoomController *View::zoomController() const
835 {
836 return d->zoomController;
837 }
838
canvasController() const839 KoCanvasController* View::canvasController() const
840 {
841 return d->canvasController;
842 }
843
columnHeader() const844 ColumnHeaderWidget* View::columnHeader()const
845 {
846 return d->columnHeader;
847 }
848
rowHeader() const849 RowHeaderWidget* View::rowHeader()const
850 {
851 return d->rowHeader;
852 }
853
horzScrollBar() const854 QScrollBar* View::horzScrollBar()const
855 {
856 return d->horzScrollBar;
857 }
858
vertScrollBar() const859 QScrollBar* View::vertScrollBar()const
860 {
861 return d->vertScrollBar;
862 }
863
tabBar() const864 TabBar* View::tabBar() const
865 {
866 return d->tabBar;
867 }
868
zoomHandler() const869 KoZoomHandler* View::zoomHandler() const
870 {
871 return d->zoomHandler;
872 }
873
isLoading() const874 bool View::isLoading() const
875 {
876 return d->loading;
877 }
878
selection() const879 Selection* View::selection() const
880 {
881 return d->selection;
882 }
883
activeSheet() const884 Sheet* View::activeSheet() const
885 {
886 return d->activeSheet;
887 }
888
sheetDestroyed(QObject * obj)889 void View::sheetDestroyed(QObject* obj)
890 {
891 if (Sheet *sheet = dynamic_cast<Sheet*>(obj)) {
892 Q_ASSERT(d->sheetViews.contains(sheet));
893 d->sheetViews.remove(sheet);
894 // The SheetView will be proper destroyed already cause it's a QObject-child of the sheet.
895 }
896 }
897
sheetView(const Sheet * sheet) const898 SheetView* View::sheetView(const Sheet* sheet) const
899 {
900 SheetView *sheetView = d->sheetViews.value(sheet);
901 if (!sheetView) {
902 debugSheetsRender << "View: Creating SheetView for" << sheet->sheetName();
903 sheetView = new SheetView(sheet);
904 d->sheetViews.insert(sheet, sheetView);
905 sheetView->setViewConverter(zoomHandler());
906 connect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)),
907 d->canvas, SLOT(setDocumentSize(QSizeF)));
908 connect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)),
909 d->zoomController, SLOT(setDocumentSize(QSizeF)));
910 connect(sheet, SIGNAL(visibleSizeChanged()),
911 sheetView, SLOT(updateAccessedCellRange()));
912 connect(sheet, SIGNAL(destroyed(QObject*)),
913 this, SLOT(sheetDestroyed(QObject*)));
914 }
915 return sheetView;
916 }
917
refreshSheetViews()918 void View::refreshSheetViews()
919 {
920 QList<const Sheet*> sheets = d->sheetViews.keys();
921 QList< QPointer<SheetView> > sheetViews = d->sheetViews.values();
922
923 foreach(const Sheet *sheet, d->sheetViews.keys()) {
924 disconnect(sheet, SIGNAL(destroyed(QObject*)), this, SLOT(sheetDestroyed(QObject*)));
925 }
926
927 foreach (SheetView *sheetView, sheetViews) {
928 disconnect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)),
929 d->canvas, SLOT(setDocumentSize(QSizeF)));
930 disconnect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)),
931 d->zoomController, SLOT(setDocumentSize(QSizeF)));
932 disconnect(sheetView->sheet(), SIGNAL(visibleSizeChanged()),
933 sheetView, SLOT(updateAccessedCellRange()));
934 }
935
936 qDeleteAll(sheetViews);
937 d->sheetViews.clear();
938
939 foreach(const Sheet *sheet, d->doc->map()->sheetList()) {
940 sheet->cellStorage()->invalidateStyleCache();
941 }
942 }
943
refreshSelection(const Region & region)944 void View::refreshSelection(const Region& region)
945 {
946 doc()->map()->addDamage(new CellDamage(activeSheet(), region, CellDamage::Appearance));
947 }
948
aboutToModify(const Region & region)949 void View::aboutToModify(const Region& region)
950 {
951 Q_UNUSED(region);
952 selection()->emitCloseEditor(true); // save changes
953 }
954
initConfig()955 void View::initConfig()
956 {
957 KSharedConfigPtr config = Factory::global().config();
958 const KConfigGroup parameterGroup = config->group("Parameters");
959 const bool configFromDoc = doc()->configLoadFromFile();
960 if (!configFromDoc) {
961 doc()->map()->settings()->setShowHorizontalScrollBar(parameterGroup.readEntry("Horiz ScrollBar", true));
962 doc()->map()->settings()->setShowVerticalScrollBar(parameterGroup.readEntry("Vert ScrollBar", true));
963 }
964 doc()->map()->settings()->setShowColumnHeader(parameterGroup.readEntry("Column Header", true));
965 doc()->map()->settings()->setShowRowHeader(parameterGroup.readEntry("Row Header", true));
966 if (!configFromDoc)
967 doc()->map()->settings()->setCompletionMode((KCompletion::CompletionMode)parameterGroup.readEntry("Completion Mode", (int)(KCompletion::CompletionAuto)));
968 doc()->map()->settings()->setMoveToValue((Calligra::Sheets::MoveTo)parameterGroup.readEntry("Move", (int)(Bottom)));
969 doc()->map()->settings()->setIndentValue(parameterGroup.readEntry("Indent", 10.0));
970 doc()->map()->settings()->setTypeOfCalc((MethodOfCalc)parameterGroup.readEntry("Method of Calc", (int)(SumOfNumber)));
971 if (!configFromDoc)
972 doc()->map()->settings()->setShowTabBar(parameterGroup.readEntry("Tabbar", true));
973
974 doc()->map()->settings()->setShowStatusBar(parameterGroup.readEntry("Status bar", true));
975
976 changeNbOfRecentFiles(parameterGroup.readEntry("NbRecentFile", 10));
977 //autosave value is stored as a minute.
978 //but default value is stored as seconde.
979 doc()->setAutoSave(parameterGroup.readEntry("AutoSave", KoDocument::defaultAutoSave() / 60)*60);
980 doc()->setBackupFile(parameterGroup.readEntry("BackupFile", true));
981
982 const KConfigGroup colorGroup = config->group("KSpread Color");
983 doc()->map()->settings()->setGridColor(colorGroup.readEntry("GridColor", QColor(Qt::lightGray)));
984 doc()->map()->settings()->changePageOutlineColor(colorGroup.readEntry("PageOutlineColor", QColor(Qt::red)));
985
986 initCalcMenu();
987 calcStatusBarOp();
988 }
989
changeNbOfRecentFiles(int _nb)990 void View::changeNbOfRecentFiles(int _nb)
991 {
992 if (mainWindow())
993 mainWindow()->setMaxRecentItems(_nb);
994 }
995
initCalcMenu()996 void View::initCalcMenu()
997 {
998 switch (doc()->map()->settings()->getTypeOfCalc()) {
999 case SumOfNumber:
1000 d->actions->calcSum->setChecked(true);
1001 break;
1002 case Min:
1003 d->actions->calcMin->setChecked(true);
1004 break;
1005 case Max:
1006 d->actions->calcMax->setChecked(true);
1007 break;
1008 case Average:
1009 d->actions->calcAverage->setChecked(true);
1010 break;
1011 case Count:
1012 d->actions->calcCount->setChecked(true);
1013 break;
1014 case CountA:
1015 d->actions->calcCountA->setChecked(true);
1016 break;
1017 case NoneCalc:
1018 d->actions->calcNone->setChecked(true);
1019 break;
1020 default :
1021 d->actions->calcSum->setChecked(true);
1022 break;
1023 }
1024
1025 }
1026
1027
recalcWorkBook()1028 void View::recalcWorkBook()
1029 {
1030 doc()->map()->recalcManager()->recalcMap();
1031 }
1032
recalcWorkSheet()1033 void View::recalcWorkSheet()
1034 {
1035 if (!activeSheet())
1036 return;
1037 doc()->map()->recalcManager()->recalcSheet(activeSheet());
1038 }
1039
shapeSelectionChanged()1040 void View::shapeSelectionChanged()
1041 {
1042 const KoSelection* selection = d->canvas->shapeManager()->selection();
1043 const QList<KoShape*> shapes = selection->selectedShapes(KoFlake::StrippedSelection);
1044
1045 if (shapes.isEmpty()) {
1046 d->actions->shapeAnchor->setEnabled(false);
1047 return;
1048 }
1049 d->actions->shapeAnchor->setEnabled(true);
1050
1051 // start with the first shape
1052 const KoShape* shape = shapes[0];
1053 const ShapeApplicationData* data = dynamic_cast<ShapeApplicationData*>(shape->applicationData());
1054 if (!data) {
1055 // Container children do not have the application data set, deselect the anchoring action.
1056 d->actions->shapeAnchor->setCurrentAction(0);
1057 return;
1058 }
1059 bool anchoredToCell = data->isAnchoredToCell();
1060 d->actions->shapeAnchor->setCurrentAction(anchoredToCell ? i18n("Cell") : i18n("Page"));
1061
1062 // go on with the remaining shapes
1063 for (int i = 1; i < shapes.count(); ++i) {
1064 shape = shapes[i];
1065 data = dynamic_cast<ShapeApplicationData*>(shape->applicationData());
1066 Q_ASSERT(data);
1067 if (anchoredToCell != data->isAnchoredToCell()) {
1068 // If the anchoring differs between shapes, deselect the anchoring action and stop here.
1069 d->actions->shapeAnchor->setCurrentAction(0);
1070 break;
1071 }
1072 }
1073 }
1074
1075
editDeleteSelection()1076 void View::editDeleteSelection()
1077 {
1078 d->canvas->toolProxy()->deleteSelection();
1079 }
1080
initialPosition()1081 void View::initialPosition()
1082 {
1083 // Loading completed, pick initial worksheet
1084 foreach(Sheet* sheet, doc()->map()->sheetList()) {
1085 addSheet(sheet);
1086 }
1087
1088 // Set the initial X and Y offsets for the view (OpenDocument loading)
1089 const LoadingInfo* loadingInfo = doc()->map()->loadingInfo();
1090 if (loadingInfo->fileFormat() == LoadingInfo::OpenDocument) {
1091 d->savedAnchors = loadingInfo->cursorPositions();
1092 d->savedMarkers = loadingInfo->cursorPositions();
1093 d->savedOffsets = loadingInfo->scrollingOffsets();
1094 }
1095
1096 Sheet* sheet = loadingInfo->initialActiveSheet();
1097 if (!sheet) {
1098 //activate first table which is not hiding
1099 sheet = doc()->map()->visibleSheets().isEmpty() ? 0 : doc()->map()->findSheet(doc()->map()->visibleSheets().first());
1100 if (!sheet) {
1101 sheet = doc()->map()->sheet(0);
1102 if (sheet) {
1103 sheet->setHidden(false);
1104 QString tabName = sheet->sheetName();
1105 d->tabBar->addTab(tabName);
1106 }
1107 }
1108 }
1109 setActiveSheet(sheet);
1110 d->mapViewModel->setActiveSheet(sheet);
1111
1112 // Set the initial X and Y offsets for the view (Native format loading)
1113 if (loadingInfo->fileFormat() == LoadingInfo::NativeFormat) {
1114 const QPoint offset = zoomHandler()->documentToView(loadingInfo->scrollingOffsets()[sheet]).toPoint();
1115 d->canvas->setDocumentOffset(offset);
1116 d->horzScrollBar->setValue(offset.x());
1117 d->vertScrollBar->setValue(offset.y());
1118 // Set the initial position for the marker as stored in the XML file,
1119 // (1,1) otherwise
1120 const QPoint marker = loadingInfo->cursorPositions()[sheet];
1121 d->selection->initialize((marker.x() <= 0 || marker.y() <= 0) ? QPoint(1, 1) : marker);
1122 }
1123
1124 updateShowSheetMenu();
1125
1126 // Initialize shape anchoring action.
1127 shapeSelectionChanged();
1128
1129 initConfig();
1130
1131 d->canvas->setFocus();
1132
1133 QTimer::singleShot(50, this, SLOT(finishLoading()));
1134 }
1135
finishLoading()1136 void View::finishLoading()
1137 {
1138 // finish the "View Loading" process
1139 d->loading = false;
1140 doc()->map()->deleteLoadingInfo();
1141
1142 setHeaderMinima();
1143
1144 // Activate the cell tool.
1145 if (mainWindow())
1146 KoToolManager::instance()->switchToolRequested("KSpreadCellToolId");
1147 }
1148
updateReadWrite(bool readwrite)1149 void View::updateReadWrite(bool readwrite)
1150 {
1151 // inform the cell tool
1152 emit documentReadWriteToggled(readwrite);
1153
1154 const QList<QAction*> actions = actionCollection()->actions();
1155 for (int i = 0; i < actions.count(); ++i) {
1156 // The action collection contains also the flake tool actions. Skip them.
1157 if (actions[i]->parent() == this)
1158 actions[i]->setEnabled(readwrite);
1159 }
1160
1161 if (!doc() || !doc()->map() || doc()->map()->isProtected()) {
1162 d->actions->showSheet->setEnabled(false);
1163 d->actions->hideSheet->setEnabled(false);
1164 } else {
1165 d->actions->showSheet->setEnabled(true);
1166 d->actions->hideSheet->setEnabled(true);
1167 }
1168 d->actions->showPageOutline->setEnabled(true);
1169 d->tabBar->setReadOnly(doc()->map()->isProtected());
1170 }
1171
createTemplate()1172 void View::createTemplate()
1173 {
1174 KoTemplateCreateDia::createTemplate(doc()->documentPart()->templatesResourcePath(), ".ots",
1175 doc(), this);
1176 }
1177
setActiveSheet(Sheet * sheet,bool updateSheet)1178 void View::setActiveSheet(Sheet* sheet, bool updateSheet)
1179 {
1180 // It can happen that our tabBar->activeTab() is not in sync with our activeSheet() if
1181 // setActiveSheet() was previously called before the delayed View::initialPosition()
1182 // was called and had a change to proper TabBar::setTabs().
1183 if (sheet == d->activeSheet && (!sheet || d->tabBar->activeTab() == sheet->sheetName()))
1184 return;
1185
1186 if (d->activeSheet != 0 && !d->selection->referenceSelectionMode()) {
1187 selection()->emitCloseEditor(true); // save changes
1188 saveCurrentSheetSelection();
1189 }
1190
1191 const Sheet* oldSheet = d->activeSheet;
1192 d->activeSheet = sheet;
1193
1194 if (d->activeSheet == 0) {
1195 return;
1196 }
1197
1198 // flake
1199 // Change the active shape controller and its shapes.
1200 d->canvas->shapeController()->setShapeControllerBase(d->activeSheet);
1201 // and then update the toolmanager separately
1202 KoToolManager::instance()->updateShapeControllerBase(d->activeSheet, d->canvas->canvasController());
1203
1204 d->canvas->shapeManager()->setShapes(d->activeSheet->shapes());
1205 // Tell the Canvas about the new visible sheet size.
1206 sheetView(d->activeSheet)->updateAccessedCellRange();
1207
1208 // If there was no sheet before or the layout directions differ.
1209 if (!oldSheet || oldSheet->layoutDirection() != d->activeSheet->layoutDirection()) {
1210 // Propagate the layout direction to the canvas and horz. scrollbar.
1211 const Qt::LayoutDirection direction = d->activeSheet->layoutDirection();
1212 d->canvas->setLayoutDirection(direction);
1213 d->horzScrollBar->setLayoutDirection(direction);
1214 // Replace the painting strategy for painting shapes.
1215 KoShapeManager *const shapeManager = d->canvas->shapeManager();
1216 KoShapeManagerPaintingStrategy *paintingStrategy = 0;
1217 if (direction == Qt::LeftToRight) {
1218 paintingStrategy = new KoShapeManagerPaintingStrategy(shapeManager);
1219 } else {
1220 paintingStrategy = new RightToLeftPaintingStrategy(shapeManager, d->canvas);
1221 }
1222 shapeManager->setPaintingStrategy(paintingStrategy);
1223 }
1224 // If there was no sheet before or the formula visibilities differ.
1225 if (!oldSheet || oldSheet->getShowFormula() != d->activeSheet->getShowFormula()) {
1226 const bool showFormulas = d->activeSheet->getShowFormula();
1227 stateChanged("show_formulas", showFormulas ? StateNoReverse : StateReverse);
1228 }
1229
1230 // Restore the old scrolling offset.
1231 QMap<Sheet*, QPointF>::ConstIterator it3 = d->savedOffsets.constFind(d->activeSheet);
1232 if (it3 != d->savedOffsets.constEnd()) {
1233 const QPoint offset = zoomHandler()->documentToView(*it3).toPoint();
1234 d->canvas->setDocumentOffset(offset);
1235 d->horzScrollBar->setValue(offset.x());
1236 d->vertScrollBar->setValue(offset.y());
1237 }
1238
1239 // tell the resource manager of the newly active page
1240 d->canvas->resourceManager()->setResource(KoCanvasResourceManager::CurrentPage, QVariant(sheet->map()->indexOf(sheet) + 1));
1241
1242 // Always repaint the visible cells.
1243 d->canvas->update();
1244 d->rowHeader->update();
1245 d->columnHeader->update();
1246 d->selectAllButton->update();
1247
1248 // Prevents an endless loop, if called by the TabBar.
1249 if (updateSheet) {
1250 d->tabBar->setActiveTab(d->activeSheet->sheetName());
1251 }
1252
1253 if (d->selection->referenceSelectionMode()) {
1254 d->selection->setActiveSheet(d->activeSheet);
1255 return;
1256 }
1257
1258 /* see if there was a previous selection on this other sheet */
1259 QMap<Sheet*, QPoint>::ConstIterator it = d->savedAnchors.constFind(d->activeSheet);
1260 QMap<Sheet*, QPoint>::ConstIterator it2 = d->savedMarkers.constFind(d->activeSheet);
1261
1262 // restore the old anchor and marker
1263 const QPoint newAnchor = (it == d->savedAnchors.constEnd()) ? QPoint(1, 1) : *it;
1264 const QPoint newMarker = (it2 == d->savedMarkers.constEnd()) ? QPoint(1, 1) : *it2;
1265
1266 d->selection->clear();
1267 d->selection->setActiveSheet(d->activeSheet);
1268 d->selection->setOriginSheet(d->activeSheet);
1269 d->selection->initialize(QRect(newMarker, newAnchor));
1270
1271 d->actions->showPageOutline->blockSignals(true);
1272 d->actions->showPageOutline->setChecked(d->activeSheet->isShowPageOutline());
1273 d->actions->showPageOutline->blockSignals(false);
1274
1275 d->actions->protectSheet->blockSignals(true);
1276 d->actions->protectSheet->setChecked(d->activeSheet->isProtected());
1277 d->actions->protectSheet->blockSignals(false);
1278
1279 d->actions->protectDoc->blockSignals(true);
1280 d->actions->protectDoc->setChecked(doc()->map()->isProtected());
1281 d->actions->protectDoc->blockSignals(false);
1282
1283 d->adjustActions(!d->activeSheet->isProtected());
1284 const bool protect = d->activeSheet->isProtected();
1285 stateChanged("sheet_is_protected", protect ? StateNoReverse : StateReverse);
1286
1287 // Auto calculation state for the INFO function.
1288 const bool autoCalc = d->activeSheet->isAutoCalculationEnabled();
1289 d->doc->map()->calculationSettings()->setAutoCalculationEnabled(autoCalc);
1290
1291 calcStatusBarOp();
1292 }
1293
changeSheet(const QString & _name)1294 void View::changeSheet(const QString& _name)
1295 {
1296 if (activeSheet()->sheetName() == _name)
1297 return;
1298
1299 Sheet *t = doc()->map()->findSheet(_name);
1300 if (!t) {
1301 debugSheets << "Unknown sheet" << _name;
1302 return;
1303 }
1304 setActiveSheet(t, false /* False: Endless loop because of setActiveTab() => do the visual area update manually*/);
1305 d->mapViewModel->setActiveSheet(t);
1306 }
1307
moveSheet(unsigned sheet,unsigned target)1308 void View::moveSheet(unsigned sheet, unsigned target)
1309 {
1310 if (doc()->map()->isProtected()) return;
1311
1312 QStringList vs = doc()->map()->visibleSheets();
1313
1314 if (target >= (uint) vs.count())
1315 doc()->map()->moveSheet(vs[ sheet ], vs[ vs.count()-1 ], false);
1316 else
1317 doc()->map()->moveSheet(vs[ sheet ], vs[ target ], true);
1318
1319 d->tabBar->moveTab(sheet, target);
1320 }
1321
sheetProperties()1322 void View::sheetProperties()
1323 {
1324 // sanity check, shouldn't happen
1325 if (doc()->map()->isProtected()) return;
1326 if (d->activeSheet->isProtected()) return;
1327
1328 bool directionChanged = false;
1329 bool formulaVisibilityChanged = false;
1330
1331 QPointer<SheetPropertiesDialog> dlg = new SheetPropertiesDialog(this);
1332 dlg->setLayoutDirection(d->activeSheet->layoutDirection());
1333 dlg->setAutoCalculationEnabled(d->activeSheet->isAutoCalculationEnabled());
1334 dlg->setShowGrid(d->activeSheet->getShowGrid());
1335 dlg->setShowPageOutline(d->activeSheet->isShowPageOutline());
1336 dlg->setShowFormula(d->activeSheet->getShowFormula());
1337 dlg->setHideZero(d->activeSheet->getHideZero());
1338 dlg->setShowFormulaIndicator(d->activeSheet->getShowFormulaIndicator());
1339 dlg->setShowCommentIndicator(d->activeSheet->getShowCommentIndicator());
1340 dlg->setColumnAsNumber(d->activeSheet->getShowColumnNumber());
1341 dlg->setLcMode(d->activeSheet->getLcMode());
1342 dlg->setCapitalizeFirstLetter(d->activeSheet->getFirstLetterUpper());
1343
1344 if (dlg->exec()) {
1345 SheetPropertiesCommand* command = new SheetPropertiesCommand(d->activeSheet);
1346
1347 if (d->activeSheet->layoutDirection() != dlg->layoutDirection())
1348 directionChanged = true;
1349 if (d->activeSheet->getShowFormula() != dlg->showFormula()) {
1350 formulaVisibilityChanged = true;
1351 }
1352
1353 command->setLayoutDirection(dlg->layoutDirection());
1354 command->setAutoCalculationEnabled(dlg->autoCalc());
1355 command->setShowGrid(dlg->showGrid());
1356 command->setShowPageOutline(dlg->showPageOutline());
1357 command->setShowFormula(dlg->showFormula());
1358 command->setHideZero(dlg->hideZero());
1359 command->setShowFormulaIndicator(dlg->showFormulaIndicator());
1360 command->setShowCommentIndicator(dlg->showCommentIndicator());
1361 command->setColumnAsNumber(dlg->columnAsNumber());
1362 command->setLcMode(dlg->lcMode());
1363 command->setCapitalizeFirstLetter(dlg->capitalizeFirstLetter());
1364 doc()->addCommand(command);
1365 }
1366
1367 delete dlg;
1368
1369 if (directionChanged) {
1370 // the scrollbar and hborder remain reversed otherwise
1371 d->canvas->setLayoutDirection(d->activeSheet->layoutDirection()); // for scrolling
1372 d->horzScrollBar->setLayoutDirection(d->activeSheet->layoutDirection());
1373 d->columnHeader->update();
1374 // Replace the painting strategy for painting shapes.
1375 KoShapeManager *const shapeManager = d->canvas->shapeManager();
1376 KoShapeManagerPaintingStrategy *paintingStrategy = 0;
1377 if (d->activeSheet->layoutDirection() == Qt::LeftToRight) {
1378 paintingStrategy = new KoShapeManagerPaintingStrategy(shapeManager);
1379 } else {
1380 paintingStrategy = new RightToLeftPaintingStrategy(shapeManager, d->canvas);
1381 }
1382 shapeManager->setPaintingStrategy(paintingStrategy);
1383 }
1384 if (formulaVisibilityChanged) {
1385 const bool showFormulas = d->activeSheet->getShowFormula();
1386 stateChanged("show_formulas", showFormulas ? StateNoReverse : StateReverse);
1387 sheetView(d->activeSheet)->invalidate();
1388 d->canvas->update();
1389 }
1390 }
1391
insertSheet()1392 void View::insertSheet()
1393 {
1394 if (doc()->map()->isProtected()) {
1395 KMessageBox::error(0, i18n("You cannot change a protected sheet."));
1396 return;
1397 }
1398
1399 selection()->emitCloseEditor(true); // save changes
1400 Sheet * t = doc()->map()->createSheet();
1401 KUndo2Command* command = new AddSheetCommand(t);
1402 doc()->addCommand(command);
1403 setActiveSheet(t);
1404
1405 if (doc()->map()->visibleSheets().count() > 1) {
1406 d->actions->deleteSheet->setEnabled(true);
1407 d->actions->hideSheet->setEnabled(true);
1408 }
1409 }
1410
duplicateSheet()1411 void View::duplicateSheet()
1412 {
1413 if (doc()->map()->isProtected()) {
1414 KMessageBox::error(this, i18n("You cannot change a protected sheet."));
1415 return;
1416 }
1417
1418 DuplicateSheetCommand* command = new DuplicateSheetCommand();
1419 command->setSheet(activeSheet());
1420 doc()->addCommand(command);
1421
1422 if (doc()->map()->visibleSheets().count() > 1) {
1423 d->actions->deleteSheet->setEnabled(true);
1424 d->actions->hideSheet->setEnabled(true);
1425 }
1426 }
1427
hideSheet()1428 void View::hideSheet()
1429 {
1430 if (!d->activeSheet)
1431 return;
1432
1433 if (doc()->map()->visibleSheets().count() == 1) {
1434 KMessageBox::error(this, i18n("You cannot hide the last visible sheet."));
1435 return;
1436 }
1437
1438 QStringList vs = doc()->map()->visibleSheets();
1439 int i = vs.indexOf(d->activeSheet->sheetName()) - 1;
1440 if (i < 0) i = 1;
1441 QString sn = vs[i];
1442
1443 KUndo2Command* command = new HideSheetCommand(activeSheet());
1444 doc()->addCommand(command);
1445
1446 d->tabBar->removeTab(d->activeSheet->sheetName());
1447 d->tabBar->setActiveTab(sn);
1448 }
1449
showSheet()1450 void View::showSheet()
1451 {
1452 if (!d->activeSheet)
1453 return;
1454
1455 ShowDialog dialog(this, d->selection);
1456 dialog.exec();
1457 }
1458
copyAsText()1459 void View::copyAsText()
1460 {
1461 if (!d->activeSheet)
1462 return;
1463 QMimeData* mimeData = new QMimeData();
1464 mimeData->setText(CopyCommand::saveAsPlainText(*selection()));
1465
1466 QApplication::clipboard()->setMimeData(mimeData);
1467 }
1468
1469
setShapeAnchoring(const QString & mode)1470 void View::setShapeAnchoring(const QString& mode)
1471 {
1472 const KoSelection* selection = d->canvas->shapeManager()->selection();
1473 const QList<KoShape*> shapes = selection->selectedShapes(KoFlake::StrippedSelection);
1474 for (int i = 0; i < shapes.count(); ++i) {
1475 const KoShape* shape = shapes[i];
1476 ShapeApplicationData* data = dynamic_cast<ShapeApplicationData*>(shape->applicationData());
1477 Q_ASSERT(data);
1478 data->setAnchoredToCell(mode == i18n("Cell"));
1479 }
1480 }
1481
toggleProtectDoc(bool mode)1482 void View::toggleProtectDoc(bool mode)
1483 {
1484 if (!doc() || !doc()->map())
1485 return;
1486
1487 bool success;
1488 if (mode) {
1489 success = doc()->map()->showPasswordDialog(this, ProtectableObject::Lock,
1490 i18n("Protect Document"));
1491 } else {
1492 success = doc()->map()->showPasswordDialog(this, ProtectableObject::Unlock,
1493 i18n("Unprotect Document"));
1494 }
1495 if (!success) {
1496 d->actions->protectDoc->setChecked(!mode);
1497 return;
1498 }
1499
1500 doc()->setModified(true);
1501 stateChanged("map_is_protected", mode ? StateNoReverse : StateReverse);
1502 d->tabBar->setReadOnly(doc()->map()->isProtected());
1503 }
1504
toggleProtectSheet(bool mode)1505 void View::toggleProtectSheet(bool mode)
1506 {
1507 if (!d->activeSheet)
1508 return;
1509
1510 bool success;
1511 if (mode) {
1512 success = activeSheet()->showPasswordDialog(this, ProtectableObject::Lock,
1513 i18n("Protect Sheet"));
1514 } else {
1515 success = activeSheet()->showPasswordDialog(this, ProtectableObject::Unlock,
1516 i18n("Unprotect Sheet"));
1517 }
1518 if (!success) {
1519 d->actions->protectSheet->setChecked(!mode);
1520 return;
1521 }
1522
1523 doc()->setModified(true);
1524 d->adjustActions(!mode);
1525
1526 // The sheet protection change may hide/unhide some values or formulas,
1527 // so the cached visual data has become invalid.
1528 refreshSheetViews();
1529 d->canvas->update();
1530
1531 // inform the cell tool
1532 emit sheetProtectionToggled(mode);
1533 }
1534
togglePageOutline(bool mode)1535 void View::togglePageOutline(bool mode)
1536 {
1537 if (!d->activeSheet)
1538 return;
1539
1540 d->activeSheet->setShowPageOutline(mode);
1541 }
1542
viewZoom(KoZoomMode::Mode mode,qreal zoom)1543 void View::viewZoom(KoZoomMode::Mode mode, qreal zoom)
1544 {
1545 Q_UNUSED(zoom)
1546 #ifdef NDEBUG
1547 Q_UNUSED(mode);
1548 #endif
1549 Q_ASSERT(mode == KoZoomMode::ZOOM_CONSTANT);
1550 selection()->emitCloseEditor(true); // save changes
1551 setHeaderMinima();
1552 d->canvas->update();
1553 d->columnHeader->update();
1554 d->rowHeader->update();
1555 d->selectAllButton->update();
1556 }
1557
showColumnHeader(bool enable)1558 void View::showColumnHeader(bool enable)
1559 {
1560 doc()->map()->settings()->setShowColumnHeader(enable);
1561 d->columnHeader->setVisible(enable);
1562 d->selectAllButton->setVisible(enable && d->rowHeader->isVisible());
1563 }
1564
showRowHeader(bool enable)1565 void View::showRowHeader(bool enable)
1566 {
1567 doc()->map()->settings()->setShowRowHeader(enable);
1568 d->rowHeader->setVisible(enable);
1569 d->selectAllButton->setVisible(enable && d->columnHeader->isVisible());
1570 }
1571
showHorizontalScrollBar(bool enable)1572 void View::showHorizontalScrollBar(bool enable)
1573 {
1574 doc()->map()->settings()->setShowHorizontalScrollBar(enable);
1575 d->horzScrollBar->setVisible(enable);
1576 }
1577
showVerticalScrollBar(bool enable)1578 void View::showVerticalScrollBar(bool enable)
1579 {
1580 doc()->map()->settings()->setShowVerticalScrollBar(enable);
1581 d->vertScrollBar->setVisible(enable);
1582 }
1583
showStatusBar(bool enable)1584 void View::showStatusBar(bool enable)
1585 {
1586 doc()->map()->settings()->setShowStatusBar(enable);
1587 if (statusBar()) {
1588 statusBar()->setVisible(enable);
1589 }
1590 }
1591
showTabBar(bool enable)1592 void View::showTabBar(bool enable)
1593 {
1594 doc()->map()->settings()->setShowTabBar(enable);
1595 d->tabBar->setVisible(enable);
1596 }
1597
optionsNotifications()1598 void View::optionsNotifications()
1599 {
1600 #ifndef QT_NO_DBUS
1601 KNotifyConfigWidget::configure(this);
1602 #endif
1603 }
1604
preference()1605 void View::preference()
1606 {
1607 PreferenceDialog dialog(this);
1608 dialog.exec();
1609 }
1610
nextSheet()1611 void View::nextSheet()
1612 {
1613 Sheet * t = doc()->map()->nextSheet(activeSheet());
1614 if (!t) {
1615 debugSheets << "Unknown sheet";
1616 return;
1617 }
1618 selection()->emitCloseEditor(true); // save changes
1619 setActiveSheet(t);
1620 d->tabBar->setActiveTab(t->sheetName());
1621 d->tabBar->ensureVisible(t->sheetName());
1622 }
1623
previousSheet()1624 void View::previousSheet()
1625 {
1626 Sheet * t = doc()->map()->previousSheet(activeSheet());
1627 if (!t) {
1628 debugSheets << "Unknown sheet";
1629 return;
1630 }
1631 selection()->emitCloseEditor(true); // save changes
1632 setActiveSheet(t);
1633 d->tabBar->setActiveTab(t->sheetName());
1634 d->tabBar->ensureVisible(t->sheetName());
1635 }
1636
firstSheet()1637 void View::firstSheet()
1638 {
1639 Sheet *t = doc()->map()->sheet(0);
1640 if (!t) {
1641 debugSheets << "Unknown sheet";
1642 return;
1643 }
1644 selection()->emitCloseEditor(true); // save changes
1645 setActiveSheet(t);
1646 d->tabBar->setActiveTab(t->sheetName());
1647 d->tabBar->ensureVisible(t->sheetName());
1648 }
1649
lastSheet()1650 void View::lastSheet()
1651 {
1652 Sheet *t = doc()->map()->sheet(doc()->map()->count() - 1);
1653 if (!t) {
1654 debugSheets << "Unknown sheet";
1655 return;
1656 }
1657 selection()->emitCloseEditor(true); // save changes
1658 setActiveSheet(t);
1659 d->tabBar->setActiveTab(t->sheetName());
1660 d->tabBar->ensureVisible(t->sheetName());
1661 }
1662
keyPressEvent(QKeyEvent * event)1663 void View::keyPressEvent(QKeyEvent *event)
1664 {
1665 #ifndef NDEBUG
1666 if ((event->modifiers() & Qt::ControlModifier) && (event->modifiers() & Qt::ShiftModifier)) {
1667 if (event->key() == Qt::Key_V) { // Ctrl+Shift+V to show debug (similar to Words)
1668 d->activeSheet->printDebug();
1669 }
1670 }
1671 #endif
1672 QWidget::keyPressEvent(event);
1673 }
1674
leftBorder() const1675 int View::leftBorder() const
1676 {
1677 return (int)(((RowHeader*)d->rowHeader)->width());
1678 }
1679
rightBorder() const1680 int View::rightBorder() const
1681 {
1682 return d->vertScrollBar->width();
1683 }
1684
topBorder() const1685 int View::topBorder() const
1686 {
1687 return (int)(((ColumnHeader*)d->columnHeader)->height());
1688 }
1689
bottomBorder() const1690 int View::bottomBorder() const
1691 {
1692 return d->horzScrollBar->height();
1693 }
1694
setHeaderMinima()1695 void View::setHeaderMinima()
1696 {
1697 if (d->loading) // "View Loading" not finished yet
1698 return;
1699 QFont font(KoGlobal::defaultFont());
1700 QFontMetricsF fm(font, 0);
1701 qreal h = fm.height() + 3;
1702 qreal w = fm.width(QString::fromLatin1("99999")) + 3;
1703 d->columnHeader->setMinimumHeight(qRound(h));
1704 d->rowHeader->setMinimumWidth(qRound(w));
1705 d->selectAllButton->setMinimumHeight(qRound(h));
1706 d->selectAllButton->setMinimumWidth(qRound(w));
1707 }
1708
paperLayoutDlg()1709 void View::paperLayoutDlg()
1710 {
1711 selection()->emitCloseEditor(true); // save changes
1712 SheetPrint* print = d->activeSheet->print();
1713
1714 KoPageLayout pl = print->settings()->pageLayout();
1715
1716
1717 /*
1718 const HeaderFooter *const headerFooter = print->headerFooter();
1719 HeadFoot hf;
1720 hf.headLeft = headerFooter->localizeHeadFootLine(headerFooter->headLeft());
1721 hf.headRight = headerFooter->localizeHeadFootLine(headerFooter->headRight());
1722 hf.headMid = headerFooter->localizeHeadFootLine(headerFooter->headMid());
1723 hf.footLeft = headerFooter->localizeHeadFootLine(headerFooter->footLeft());
1724 hf.footRight = headerFooter->localizeHeadFootLine(headerFooter->footRight());
1725 hf.footMid = headerFooter->localizeHeadFootLine(headerFooter->footMid());
1726 */
1727 PageLayoutDialog dialog(this, d->activeSheet);
1728 dialog.exec();
1729 }
1730
resetPrintRange()1731 void View::resetPrintRange()
1732 {
1733 DefinePrintRangeCommand* command = new DefinePrintRangeCommand();
1734 command->setText(kundo2_i18n("Reset Print Range"));
1735 command->setSheet(activeSheet());
1736 command->add(Region(QRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax)), activeSheet()));
1737 doc()->addCommand(command);
1738 }
1739
deleteSheet()1740 void View::deleteSheet()
1741 {
1742 if (doc()->map()->count() <= 1 || (doc()->map()->visibleSheets().count() <= 1)) {
1743 KMessageBox::sorry(this, i18n("You cannot delete the only sheet."), i18n("Remove Sheet"));
1744 return;
1745 }
1746 int ret = KMessageBox::warningContinueCancel(this, i18n("You are about to remove the active sheet.\nDo you want to continue?"),
1747 i18n("Remove Sheet"), KStandardGuiItem::del());
1748
1749 if (ret == KMessageBox::Continue) {
1750 selection()->emitCloseEditor(false); // discard changes
1751 doc()->setModified(true);
1752 Sheet * tbl = activeSheet();
1753 KUndo2Command* command = new RemoveSheetCommand(tbl);
1754 doc()->addCommand(command);
1755 }
1756 }
1757
1758
slotRename()1759 void View::slotRename()
1760 {
1761
1762 Sheet * sheet = activeSheet();
1763
1764 if (sheet->isProtected()) {
1765 KMessageBox::error(0, i18n("You cannot change a protected sheet."));
1766 return;
1767 }
1768
1769 bool ok;
1770 QString activeName = sheet->sheetName();
1771 QString newName = QInputDialog::getText(this, i18n("Rename Sheet"), i18n("Enter name:"), QLineEdit::Normal, activeName, &ok);
1772
1773 if (!ok) return;
1774
1775 if ((newName.trimmed()).isEmpty()) { // Sheet name is empty.
1776 KMessageBox::information(this, i18n("Sheet name cannot be empty."), i18n("Change Sheet Name"));
1777 // Recursion
1778 slotRename();
1779 } else if (newName != activeName) { // Sheet name changed.
1780 // Is the name already used
1781 if (doc()->map()->findSheet(newName)) {
1782 KMessageBox::information(this, i18n("This name is already used."), i18n("Change Sheet Name"));
1783 // Recursion
1784 slotRename();
1785 return;
1786 }
1787
1788 KUndo2Command* command = new RenameSheetCommand(sheet, newName);
1789 doc()->addCommand(command);
1790
1791 doc()->setModified(true);
1792 }
1793 }
1794
1795 //------------------------------------------------
1796 //
1797 // Document signals
1798 //
1799 //------------------------------------------------
1800
slotChangeSelection(const Calligra::Sheets::Region & changedRegion)1801 void View::slotChangeSelection(const Calligra::Sheets::Region& changedRegion)
1802 {
1803 if (!changedRegion.isValid())
1804 return;
1805
1806 if (d->selection->referenceSelectionMode()) {
1807 doc()->map()->addDamage(new SelectionDamage(changedRegion));
1808 debugSheetsFormula << "Choice:" << *selection();
1809 return;
1810 }
1811
1812 // delayed recalculation of the operation shown in the status bar
1813 d->statusBarOpTimer.setSingleShot(true);
1814 d->statusBarOpTimer.start(250);
1815
1816 if (!d->loading && !doc()->map()->isLoading()) {
1817 doc()->map()->addDamage(new SelectionDamage(changedRegion));
1818 }
1819 d->rowHeader->update();
1820 d->columnHeader->update();
1821 d->selectAllButton->update();
1822
1823 if (d->selection->isColumnSelected() || d->selection->isRowSelected()) {
1824 return;
1825 }
1826
1827 d->canvas->validateSelection();
1828 }
1829
slotScrollChoice(const Calligra::Sheets::Region & changedRegion)1830 void View::slotScrollChoice(const Calligra::Sheets::Region& changedRegion)
1831 {
1832 if (!selection()->referenceSelectionMode() || !changedRegion.isValid()) {
1833 return;
1834 }
1835 }
1836
calcStatusBarOp()1837 void View::calcStatusBarOp()
1838 {
1839 Sheet * sheet = activeSheet();
1840 ValueCalc* calc = doc()->map()->calc();
1841 Value val;
1842 QString prefix = "";
1843
1844 MethodOfCalc tmpMethod = doc()->map()->settings()->getTypeOfCalc();
1845 if (sheet && tmpMethod != NoneCalc) {
1846 Value range = sheet->cellStorage()->valueRegion(*d->selection);
1847 switch (tmpMethod) {
1848 case SumOfNumber:
1849 val = calc->sum(range);
1850 prefix = i18n("Sum: ");
1851 break;
1852 case Average:
1853 val = calc->avg(range);
1854 prefix = i18n("Average: ");
1855 break;
1856 case Min:
1857 val = calc->min(range);
1858 prefix = i18n("Min: ");
1859 break;
1860 case Max:
1861 val = calc->max(range);
1862 prefix = i18n("Max: ");
1863 break;
1864 case CountA:
1865 val = Value(calc->count(range));
1866 prefix = i18n("Count: ");
1867 break;
1868 case Count:
1869 val = Value(calc->count(range, false));
1870 prefix = i18n("CountA: ");
1871 case NoneCalc:
1872 break;
1873 default:
1874 break;
1875 }
1876 if ((range.columns() > 1) || (range.rows() > 1)) {
1877 QString size = i18n("%1x%2", range.columns(), range.rows());
1878 prefix = prefix.size() ? size + ", " + prefix : size;
1879 }
1880 }
1881
1882 QString res = doc()->map()->converter()->asString(val).asString();
1883 QString tmp;
1884 if (res.length()) tmp = prefix + res;
1885
1886 if (d->calcLabel)
1887 d->calcLabel->setText(QString(' ') + tmp + ' ');
1888 }
1889
statusBarClicked(const QPoint &)1890 void View::statusBarClicked(const QPoint&)
1891 {
1892 QPoint mousepos = QCursor::pos();
1893 if (factory())
1894 if (QMenu* menu = dynamic_cast<QMenu*>(factory()->container("calc_popup" , this)))
1895 menu->popup(mousepos);
1896 }
1897
menuCalc(bool)1898 void View::menuCalc(bool)
1899 {
1900 if (d->actions->calcMin->isChecked()) {
1901 doc()->map()->settings()->setTypeOfCalc(Min);
1902 } else if (d->actions->calcMax->isChecked()) {
1903 doc()->map()->settings()->setTypeOfCalc(Max);
1904 } else if (d->actions->calcCount->isChecked()) {
1905 doc()->map()->settings()->setTypeOfCalc(Count);
1906 } else if (d->actions->calcAverage->isChecked()) {
1907 doc()->map()->settings()->setTypeOfCalc(Average);
1908 } else if (d->actions->calcSum->isChecked()) {
1909 doc()->map()->settings()->setTypeOfCalc(SumOfNumber);
1910 } else if (d->actions->calcCountA->isChecked()) {
1911 doc()->map()->settings()->setTypeOfCalc(CountA);
1912 } else if (d->actions->calcNone->isChecked())
1913 doc()->map()->settings()->setTypeOfCalc(NoneCalc);
1914
1915 calcStatusBarOp();
1916 }
1917
canvas() const1918 QWidget* View::canvas() const
1919 {
1920 return d->canvas;
1921 }
1922
popupTabBarMenu(const QPoint & _point)1923 void View::popupTabBarMenu(const QPoint & _point)
1924 {
1925 if (!factory())
1926 return;
1927 if (d->tabBar) {
1928 QMenu* const menu = static_cast<QMenu*>(factory()->container("menupage_popup", this));
1929 if (!menu)
1930 return;
1931
1932 QAction* insertSheet = new QAction(koIcon("insert-table"), i18n("Insert Sheet"), this);
1933 insertSheet->setToolTip(i18n("Remove the active sheet"));
1934 connect(insertSheet, SIGNAL(triggered(bool)), this, SLOT(insertSheet()));
1935 menu->insertAction(d->actions->duplicateSheet, insertSheet);
1936
1937 QAction* deleteSheet = new QAction(koIcon("delete_table"), i18n("Remove Sheet"), this);
1938 deleteSheet->setToolTip(i18n("Remove the active sheet"));
1939 connect(deleteSheet, SIGNAL(triggered(bool)), this, SLOT(deleteSheet()));
1940 menu->insertAction(d->actions->hideSheet, deleteSheet);
1941
1942 bool state = (doc()->map()->visibleSheets().count() > 1);
1943 if (d->activeSheet && d->activeSheet->isProtected()) {
1944 deleteSheet->setEnabled(false);
1945 d->actions->hideSheet->setEnabled(false);
1946 d->actions->showSheet->setEnabled(false);
1947 } else {
1948 deleteSheet->setEnabled(state);
1949 d->actions->hideSheet->setEnabled(state);
1950 d->actions->showSheet->setEnabled(doc()->map()->hiddenSheets().count() > 0);
1951 }
1952 if (!doc() || !doc()->map() || doc()->map()->isProtected()) {
1953 insertSheet->setEnabled(false);
1954 deleteSheet->setEnabled(false);
1955 d->actions->renameSheet->setEnabled(false);
1956 d->actions->showSheet->setEnabled(false);
1957 d->actions->hideSheet->setEnabled(false);
1958 }
1959 menu->exec(_point);
1960 menu->removeAction(insertSheet);
1961 menu->removeAction(deleteSheet);
1962 delete insertSheet;
1963 delete deleteSheet;
1964 }
1965 }
1966
updateBorderButton()1967 void View::updateBorderButton()
1968 {
1969 if (d->activeSheet)
1970 d->actions->showPageOutline->setChecked(d->activeSheet->isShowPageOutline());
1971 }
1972
addSheet(Sheet * sheet)1973 void View::addSheet(Sheet *sheet)
1974 {
1975 if (!sheet->isHidden()) {
1976 d->tabBar->addTab(sheet->sheetName());
1977 }
1978 const bool state = (doc()->map()->visibleSheets().count() > 1);
1979 d->actions->deleteSheet->setEnabled(state);
1980 d->actions->hideSheet->setEnabled(state);
1981
1982 // Connect some signals
1983 connect(sheet, SIGNAL(shapeAdded(Sheet*,KoShape*)),
1984 d->mapViewModel, SLOT(addShape(Sheet*,KoShape*)));
1985 connect(sheet, SIGNAL(shapeRemoved(Sheet*,KoShape*)),
1986 d->mapViewModel, SLOT(removeShape(Sheet*,KoShape*)));
1987 }
1988
removeSheet(Sheet * sheet)1989 void View::removeSheet(Sheet *sheet)
1990 {
1991 d->tabBar->removeTab(sheet->sheetName());
1992 setActiveSheet(doc()->map()->sheet(0));
1993
1994 const bool state = (doc()->map()->visibleSheets().count() > 1);
1995 d->actions->deleteSheet->setEnabled(state);
1996 d->actions->hideSheet->setEnabled(state);
1997
1998 // Disconnect signals.
1999 disconnect(sheet, 0, d->mapViewModel, 0);
2000 }
2001
borderColor() const2002 QColor View::borderColor() const
2003 {
2004 return d->canvas->resourceManager()->foregroundColor().toQColor();
2005 }
2006
updateShowSheetMenu()2007 void View::updateShowSheetMenu()
2008 {
2009 if (d->activeSheet) {
2010 if (d->activeSheet->map()->isProtected())
2011 d->actions->showSheet->setEnabled(false);
2012 else
2013 d->actions->showSheet->setEnabled(doc()->map()->hiddenSheets().count() > 0);
2014 }
2015 }
2016
markerFromSheet(Sheet * sheet) const2017 QPoint View::markerFromSheet(Sheet* sheet) const
2018 {
2019 QMap<Sheet*, QPoint>::ConstIterator it = d->savedMarkers.constFind(sheet);
2020 QPoint newMarker = (it == d->savedMarkers.constEnd()) ? QPoint(1, 1) : *it;
2021 return newMarker;
2022 }
2023
offsetFromSheet(Sheet * sheet) const2024 QPointF View::offsetFromSheet(Sheet* sheet) const
2025 {
2026 QMap<Sheet*, QPointF>::ConstIterator it = d->savedOffsets.constFind(sheet);
2027 QPointF offset = (it == d->savedOffsets.constEnd()) ? QPointF() : *it;
2028 return offset;
2029 }
2030
saveCurrentSheetSelection()2031 void View::saveCurrentSheetSelection()
2032 {
2033 /* save the current selection on this sheet */
2034 if (d->activeSheet != 0) {
2035 d->savedAnchors.remove(d->activeSheet);
2036 d->savedAnchors.insert(d->activeSheet, d->selection->anchor());
2037 debugSheetsUI << " Current scrollbar vert value:" << d->vertScrollBar->value();
2038 debugSheetsUI << "Saving marker pos:" << d->selection->marker();
2039 d->savedMarkers.remove(d->activeSheet);
2040 d->savedMarkers.insert(d->activeSheet, d->selection->marker());
2041 d->savedOffsets.remove(d->activeSheet);
2042 d->savedOffsets.insert(d->activeSheet, QPointF(d->canvas->xOffset(),
2043 d->canvas->yOffset()));
2044 }
2045 }
2046
handleDamages(const QList<Damage * > & damages)2047 void View::handleDamages(const QList<Damage*>& damages)
2048 {
2049 QRegion paintRegion;
2050 enum { Nothing, Everything, Clipped } paintMode = Nothing;
2051
2052 QList<Damage*>::ConstIterator end(damages.end());
2053 for (QList<Damage*>::ConstIterator it = damages.begin(); it != end; ++it) {
2054 Damage* damage = *it;
2055 if (!damage) continue;
2056
2057 if (damage->type() == Damage::Cell) {
2058 CellDamage* cellDamage = static_cast<CellDamage*>(damage);
2059 debugSheetsDamage << "Processing\t" << *cellDamage;
2060 Sheet* const damagedSheet = cellDamage->sheet();
2061
2062 if (cellDamage->changes() & CellDamage::Appearance) {
2063 const Region& region = cellDamage->region();
2064 sheetView(damagedSheet)->invalidateRegion(region);
2065 paintMode = Everything;
2066 }
2067 continue;
2068 }
2069
2070 if (damage->type() == Damage::Sheet) {
2071 SheetDamage* sheetDamage = static_cast<SheetDamage*>(damage);
2072 debugSheetsDamage << *sheetDamage;
2073 const SheetDamage::Changes changes = sheetDamage->changes();
2074 if (changes & (SheetDamage::Name | SheetDamage::Shown)) {
2075 d->tabBar->setTabs(doc()->map()->visibleSheets());
2076 paintMode = Everything;
2077 }
2078 if (changes & (SheetDamage::Shown | SheetDamage::Hidden)) {
2079 updateShowSheetMenu();
2080 paintMode = Everything;
2081 }
2082 // The following changes only affect the active sheet.
2083 if (sheetDamage->sheet() != d->activeSheet) {
2084 continue;
2085 }
2086 if (changes.testFlag(SheetDamage::ContentChanged)) {
2087 update();
2088 paintMode = Everything;
2089 }
2090 if (changes.testFlag(SheetDamage::PropertiesChanged)) {
2091 sheetView(d->activeSheet)->invalidate();
2092 paintMode = Everything;
2093 }
2094 if (sheetDamage->changes() & SheetDamage::ColumnsChanged)
2095 columnHeader()->update();
2096 if (sheetDamage->changes() & SheetDamage::RowsChanged)
2097 rowHeader()->update();
2098 continue;
2099 }
2100
2101 if (damage->type() == Damage::Selection) {
2102 SelectionDamage* selectionDamage = static_cast<SelectionDamage*>(damage);
2103 debugSheetsDamage << "Processing\t" << *selectionDamage;
2104 const Region region = selectionDamage->region();
2105
2106 if (paintMode == Clipped) {
2107 const QRectF rect = canvasWidget()->cellCoordinatesToView(region.boundingRect());
2108 paintRegion += rect.toRect().adjusted(-3, -3, 4, 4);
2109 } else {
2110 paintMode = Everything;
2111 }
2112 continue;
2113 }
2114
2115 debugSheetsDamage << "Unhandled\t" << *damage;
2116 }
2117
2118 // At last repaint the dirty cells.
2119 if (paintMode == Clipped) {
2120 canvas()->update(paintRegion);
2121 } else if (paintMode == Everything) {
2122 canvas()->update();
2123 }
2124 }
2125
createPrintJob()2126 KoPrintJob * View::createPrintJob()
2127 {
2128 if (!activeSheet())
2129 return 0;
2130 // About to print; close the editor.
2131 selection()->emitCloseEditor(true); // save changes
2132 return new PrintJob(this);
2133 }
2134
updateAccessedCellRange(Sheet * sheet,const QPoint & location)2135 void View::updateAccessedCellRange(Sheet* sheet, const QPoint &location)
2136 {
2137 sheetView(sheet)->updateAccessedCellRange(location);
2138 }
2139
enableAutoScroll()2140 void View::enableAutoScroll()
2141 {
2142 d->scrollTimer->start(50);
2143 }
2144
disableAutoScroll()2145 void View::disableAutoScroll()
2146 {
2147 d->scrollTimer->stop();
2148 }
2149
autoScrollAcceleration(int offset) const2150 int View::autoScrollAcceleration(int offset) const
2151 {
2152 if (offset < 40)
2153 return offset;
2154 else
2155 return offset*offset / 40;
2156 }
2157
slotAutoScroll()2158 void View::slotAutoScroll()
2159 {
2160 QPoint scrollDistance;
2161 bool actuallyDoScroll = false;
2162 QPoint pos(mapFromGlobal(QCursor::pos()));
2163
2164 //Provide progressive scrolling depending on the mouse position
2165 if (pos.y() < topBorder()) {
2166 scrollDistance.setY((int) - autoScrollAcceleration(- pos.y() + topBorder()));
2167 actuallyDoScroll = true;
2168 } else if (pos.y() > height() - bottomBorder()) {
2169 scrollDistance.setY((int) autoScrollAcceleration(pos.y() - height() + bottomBorder()));
2170 actuallyDoScroll = true;
2171 }
2172
2173 if (pos.x() < leftBorder()) {
2174 scrollDistance.setX((int) - autoScrollAcceleration(- pos.x() + leftBorder()));
2175 actuallyDoScroll = true;
2176 } else if (pos.x() > width() - rightBorder()) {
2177 scrollDistance.setX((int) autoScrollAcceleration(pos.x() - width() + rightBorder()));
2178 actuallyDoScroll = true;
2179 }
2180
2181 if (actuallyDoScroll) {
2182 pos = canvas()->mapFrom(this, pos);
2183 QMouseEvent* event = new QMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton,
2184 QApplication::keyboardModifiers());
2185
2186 QApplication::postEvent(canvas(), event);
2187 emit autoScroll(scrollDistance);
2188 }
2189 }
2190