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