1 /* This file is part of the KDE project
2    Copyright 2006-2008 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3    Copyright 2006 Robert Knight <robertknight@gmail.com>
4    Copyright 2006 Inge Wallin <inge@lysator.liu.se>
5    Copyright 1999-2002,2004 Laurent Montel <montel@kde.org>
6    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
7    Copyright 1999-2004 David Faure <faure@kde.org>
8    Copyright 2004-2005 Meni Livne <livne@kde.org>
9    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
10    Copyright 2002-2003 Norbert Andres <nandres@web.de>
11    Copyright 2003 Hamish Rodda <rodda@kde.org>
12    Copyright 2003 Joseph Wenninger <jowenn@kde.org>
13    Copyright 2003 Lukas Tinkl <lukas@kde.org>
14    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
15    Copyright 2002 Harri Porten <porten@kde.org>
16    Copyright 2002 John Dailey <dailey@vt.edu>
17    Copyright 2002 Daniel Naber <daniel.naber@t-online.de>
18    Copyright 1999-2000 Torben Weis <weis@kde.org>
19    Copyright 1999-2000 Stephan Kulow <coolo@kde.org>
20    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
21    Copyright 2000 Wilco Greven <greven@kde.org>
22    Copyright 2000 Simon Hausmann <hausmann@kde.org
23    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
24    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
25    Copyright 1999 Reginald Stadlbauer <reggie@kde.org>
26 
27    This library is free software; you can redistribute it and/or
28    modify it under the terms of the GNU Library General Public
29    License as published by the Free Software Foundation; either
30    version 2 of the License, or(at your option) any later version.
31 
32    This library is distributed in the hope that it will be useful,
33    but WITHOUT ANY WARRANTY; without even the implied warranty of
34    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
35    Library General Public License for more details.
36 
37    You should have received a copy of the GNU Library General Public License
38    along with this library; see the file COPYING.LIB.  If not, write to
39    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
40    Boston, MA 02110-1301, USA.
41 */
42 
43 #include "CellToolBase_p.h"
44 #include "CellToolBase.h"
45 
46 // Sheets
47 #include "ApplicationSettings.h"
48 #include "CalculationSettings.h"
49 #include "CellStorage.h"
50 #include "Map.h"
51 #include "RowColumnFormat.h"
52 #include "RowFormatStorage.h"
53 #include "Selection.h"
54 #include "Sheet.h"
55 
56 // commands
57 #include "commands/DataManipulators.h"
58 #include "commands/StyleCommand.h"
59 
60 // ui
61 #include "ui/CellEditor.h"
62 #include "ui/ExternalEditor.h"
63 #include "ui/SheetView.h"
64 
65 // Calligra
66 #include <KoCanvasBase.h>
67 #include <KoCanvasController.h>
68 #include <KoCanvasResourceManager.h>
69 #include <KoViewConverter.h>
70 #include <KoColor.h>
71 #include <KoIcon.h>
72 
73 // KF5
74 #include <kfontaction.h>
75 #include <kfontsizeaction.h>
76 #include <klocale.h>
77 
78 // Qt
79 #include <QApplication>
80 #include <QGridLayout>
81 #include <QPainter>
82 #include <QToolButton>
83 
84 using namespace Calligra::Sheets;
85 
updateEditor(const Cell & cell)86 void CellToolBase::Private::updateEditor(const Cell& cell)
87 {
88     if (!externalEditor) return;
89     const Cell& theCell = cell.isPartOfMerged() ? cell.masterCell() : cell;
90     const Style style = theCell.style();
91     if (q->selection()->activeSheet()->isProtected() && style.hideFormula()) {
92         externalEditor->setPlainText(theCell.displayText());
93     } else if (q->selection()->activeSheet()->isProtected() && style.hideAll()) {
94         externalEditor->clear();
95     } else {
96         externalEditor->setPlainText(theCell.userInput());
97     }
98 }
99 
100 #define ACTION_EXEC( name, command ) { \
101         QAction *a = q->action(name); \
102         const bool blocked = a->blockSignals(true); \
103         a->command; \
104         a->blockSignals(blocked); \
105     }
106 
updateActions(const Cell & cell)107 void CellToolBase::Private::updateActions(const Cell& cell)
108 {
109     const Style style = cell.style();
110 
111     // -- font actions --
112     ACTION_EXEC("bold", setChecked(style.bold()));
113     ACTION_EXEC("italic", setChecked(style.italic()));
114     ACTION_EXEC("underline", setChecked(style.underline()));
115     ACTION_EXEC("strikeOut", setChecked(style.strikeOut()));
116 
117     static_cast<KFontAction*>(q->action("font"))->setFont(style.fontFamily());
118     static_cast<KFontSizeAction*>(q->action("fontSize"))->setFontSize(style.fontSize());
119     // -- horizontal alignment actions --
120     ACTION_EXEC("alignLeft", setChecked(style.halign() == Style::Left));
121     ACTION_EXEC("alignCenter", setChecked(style.halign() == Style::Center));
122     ACTION_EXEC("alignRight", setChecked(style.halign() == Style::Right));
123     // -- vertical alignment actions --
124     ACTION_EXEC("alignTop", setChecked(style.valign() == Style::Top));
125     ACTION_EXEC("alignMiddle", setChecked(style.valign() == Style::Middle));
126     ACTION_EXEC("alignBottom", setChecked(style.valign() == Style::Bottom));
127 
128     ACTION_EXEC("verticalText", setChecked(style.verticalText()));
129     ACTION_EXEC("wrapText", setChecked(style.wrapText()));
130 
131     Format::Type ft = style.formatType();
132     ACTION_EXEC("percent", setChecked(ft == Format::Percentage));
133     ACTION_EXEC("currency", setChecked(ft == Format::Money));
134 
135     const bool showFormulas = q->selection()->activeSheet()->getShowFormula();
136     q->action("alignLeft")->setEnabled(!showFormulas);
137     q->action("alignCenter")->setEnabled(!showFormulas);
138     q->action("alignRight")->setEnabled(!showFormulas);
139 
140     if (!q->selection()->activeSheet()->isProtected() || style.notProtected()) {
141         q->action("clearComment")->setEnabled(!cell.comment().isEmpty());
142         q->action("decreaseIndentation")->setEnabled(style.indentation() > 0.0);
143     }
144 
145     // Now, activate/deactivate some actions depending on what is selected.
146     if (!q->selection()->activeSheet()->isProtected()) {
147         const bool colSelected = q->selection()->isColumnSelected();
148         const bool rowSelected = q->selection()->isRowSelected();
149         // -- column & row actions --
150         q->action("resizeCol")->setEnabled(!rowSelected);
151         q->action("insertColumn")->setEnabled(!rowSelected);
152         q->action("deleteColumn")->setEnabled(!rowSelected);
153         q->action("hideColumn")->setEnabled(!rowSelected);
154         q->action("equalizeCol")->setEnabled(!rowSelected);
155         q->action("resizeRow")->setEnabled(!colSelected);
156         q->action("deleteRow")->setEnabled(!colSelected);
157         q->action("insertRow")->setEnabled(!colSelected);
158         q->action("hideRow")->setEnabled(!colSelected);
159         q->action("equalizeRow")->setEnabled(!colSelected);
160         // -- data insert actions --
161         q->action("textToColumns")->setEnabled(!rowSelected);
162 
163         const bool simpleSelection = q->selection()->isSingular() || colSelected || rowSelected;
164         q->action("sheetFormat")->setEnabled(!simpleSelection);
165         q->action("sort")->setEnabled(!simpleSelection);
166         q->action("sortDec")->setEnabled(!simpleSelection);
167         q->action("sortInc")->setEnabled(!simpleSelection);
168         q->action("mergeCells")->setEnabled(!simpleSelection);
169         q->action("mergeCellsHorizontal")->setEnabled(!simpleSelection);
170         q->action("mergeCellsVertical")->setEnabled(!simpleSelection);
171         q->action("fillRight")->setEnabled(!simpleSelection);
172         q->action("fillUp")->setEnabled(!simpleSelection);
173         q->action("fillDown")->setEnabled(!simpleSelection);
174         q->action("fillLeft")->setEnabled(!simpleSelection);
175         q->action("createStyleFromCell")->setEnabled(simpleSelection); // just from one cell
176 
177         const bool contiguousSelection = q->selection()->isContiguous();
178         q->action("subtotals")->setEnabled(contiguousSelection);
179     }
180 }
181 
setProtectedActionsEnabled(bool enable)182 void CellToolBase::Private::setProtectedActionsEnabled(bool enable)
183 {
184     // Enable/disable actions.
185     const QList<QAction*> actions = q->actions().values();
186     for (int i = 0; i < actions.count(); ++i)
187         actions[i]->setEnabled(enable);
188     q->action("insertFormula")->setEnabled(enable);
189     if (externalEditor) externalEditor->setEnabled(enable);
190 
191     // These actions are always enabled.
192     q->action("copy")->setEnabled(true);
193     q->action("gotoCell")->setEnabled(true);
194     q->action("edit_find")->setEnabled(true);
195     q->action("edit_find_next")->setEnabled(true);
196     q->action("edit_find_last")->setEnabled(true);
197 }
198 
processEnterKey(QKeyEvent * event)199 void CellToolBase::Private::processEnterKey(QKeyEvent* event)
200 {
201 // array is true, if ctrl+alt are pressed
202     bool array = (event->modifiers() & Qt::AltModifier) &&
203                  (event->modifiers() & Qt::ControlModifier);
204 
205     /* save changes to the current editor */
206     q->deleteEditor(true, array);
207 
208     /* use the configuration setting to see which direction we're supposed to move
209         when enter is pressed.
210     */
211     Calligra::Sheets::MoveTo direction = q->selection()->activeSheet()->map()->settings()->moveToValue();
212 
213 //if shift Button clicked inverse move direction
214     if (event->modifiers() & Qt::ShiftModifier) {
215         switch (direction) {
216         case Bottom:
217             direction = Top;
218             break;
219         case Top:
220             direction = Bottom;
221             break;
222         case Left:
223             direction = Right;
224             break;
225         case Right:
226             direction = Left;
227             break;
228         case BottomFirst:
229             direction = BottomFirst;
230             break;
231         case NoMovement:
232             direction = NoMovement;
233             break;
234         }
235     }
236 
237     /* never extend a selection with the enter key -- the shift key reverses
238         direction, not extends the selection
239     */
240     QRect r(moveDirection(direction, false));
241     event->accept(); // QKeyEvent
242 }
243 
processArrowKey(QKeyEvent * event)244 void CellToolBase::Private::processArrowKey(QKeyEvent *event)
245 {
246     /* NOTE:  hitting the tab key also calls this function.  Don't forget
247         to account for it
248     */
249     register Sheet * const sheet = q->selection()->activeSheet();
250     if (!sheet)
251         return;
252 
253     /* save changes to the current editor */
254     q->selection()->emitCloseEditor(true);
255 
256     Calligra::Sheets::MoveTo direction = Bottom;
257     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
258 
259     switch (event->key()) {
260     case Qt::Key_Down:
261         direction = Bottom;
262         break;
263     case Qt::Key_Up:
264         direction = Top;
265         break;
266     case Qt::Key_Left:
267         if (sheet->layoutDirection() == Qt::RightToLeft)
268             direction = Right;
269         else
270             direction = Left;
271         break;
272     case Qt::Key_Right:
273         if (sheet->layoutDirection() == Qt::RightToLeft)
274             direction = Left;
275         else
276             direction = Right;
277         break;
278     case Qt::Key_Tab:
279         direction = Right;
280         break;
281     case Qt::Key_Backtab:
282         //Shift+Tab moves to the left
283         direction = Left;
284         makingSelection = false;
285         break;
286     default:
287         Q_ASSERT(false);
288         break;
289     }
290 
291     QRect r(moveDirection(direction, makingSelection));
292     event->accept(); // QKeyEvent
293 }
294 
processEscapeKey(QKeyEvent * event)295 void CellToolBase::Private::processEscapeKey(QKeyEvent * event)
296 {
297     q->selection()->emitCloseEditor(false); // discard changes
298     event->accept(); // QKeyEvent
299 }
300 
processHomeKey(QKeyEvent * event)301 bool CellToolBase::Private::processHomeKey(QKeyEvent* event)
302 {
303     register Sheet * const sheet = q->selection()->activeSheet();
304     if (!sheet)
305         return false;
306 
307     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
308 
309     if (q->editor()) {
310         // We are in edit mode -> go beginning of line
311         QApplication::sendEvent(q->editor()->widget(), event);
312         return false;
313     } else {
314         QPoint destination;
315         /* start at the first used cell in the row and cycle through the right until
316             we find a cell that has some output text.  But don't look past the current
317             marker.
318             The end result we want is to move to the left to the first cell with text,
319             or just to the first column if there is no more text to the left.
320 
321             But why?  In excel, home key sends you to the first column always.
322             We might want to change to that behavior.
323         */
324 
325         if (event->modifiers() & Qt::ControlModifier) {
326             /* ctrl + Home will always just send us to location (1,1) */
327             destination = QPoint(1, 1);
328         } else {
329             QPoint marker = q->selection()->marker();
330 
331             Cell cell = sheet->cellStorage()->firstInRow(marker.y(), CellStorage::VisitContent);
332             while (!cell.isNull() && cell.column() < marker.x() && cell.isEmpty()) {
333                 cell = sheet->cellStorage()->nextInRow(cell.column(), cell.row(), CellStorage::VisitContent);
334             }
335 
336             int col = (!cell.isNull() ? cell.column() : 1);
337             if (col == marker.x())
338                 col = 1;
339             destination = QPoint(col, marker.y());
340         }
341 
342         if (q->selection()->marker() == destination)
343             return false;
344 
345         if (makingSelection) {
346             q->selection()->update(destination);
347         } else {
348             q->selection()->initialize(destination, sheet);
349         }
350         q->scrollToCell(destination);
351         event->accept(); // QKeyEvent
352     }
353     return true;
354 }
355 
processEndKey(QKeyEvent * event)356 bool CellToolBase::Private::processEndKey(QKeyEvent *event)
357 {
358     register Sheet * const sheet = q->selection()->activeSheet();
359     if (!sheet)
360         return false;
361 
362     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
363     Cell cell;
364     QPoint marker = q->selection()->marker();
365 
366     if (q->editor()) {
367         // We are in edit mode -> go end of line
368         QApplication::sendEvent(q->editor()->widget(), event);
369         return false;
370     } else {
371         // move to the last used cell in the row
372         int col = 1;
373 
374         cell = sheet->cellStorage()->lastInRow(marker.y(), CellStorage::VisitContent);
375         while (!cell.isNull() && cell.column() > q->selection()->marker().x() && cell.isEmpty()) {
376             cell = sheet->cellStorage()->prevInRow(cell.column(), cell.row(), CellStorage::VisitContent);
377         }
378 
379         col = (cell.isNull()) ? q->maxCol() : cell.column();
380 
381         QPoint destination(col, marker.y());
382         if (destination == marker)
383             return false;
384 
385         if (makingSelection) {
386             q->selection()->update(destination);
387         } else {
388             q->selection()->initialize(destination, sheet);
389         }
390         q->scrollToCell(destination);
391         event->accept(); // QKeyEvent
392     }
393     return true;
394 }
395 
processPriorKey(QKeyEvent * event)396 bool CellToolBase::Private::processPriorKey(QKeyEvent *event)
397 {
398     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
399     q->selection()->emitCloseEditor(true); // save changes
400 
401     QPoint marker = q->selection()->marker();
402 
403     QPoint destination(marker.x(), qMax(1, marker.y() - 10));
404     if (destination == marker)
405         return false;
406 
407     if (makingSelection) {
408         q->selection()->update(destination);
409     } else {
410         q->selection()->initialize(destination, q->selection()->activeSheet());
411     }
412     q->scrollToCell(destination);
413     event->accept(); // QKeyEvent
414     return true;
415 }
416 
processNextKey(QKeyEvent * event)417 bool CellToolBase::Private::processNextKey(QKeyEvent *event)
418 {
419     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
420 
421     q->selection()->emitCloseEditor(true); // save changes
422 
423     QPoint marker = q->selection()->marker();
424     QPoint destination(marker.x(), qMax(1, marker.y() + 10));
425 
426     if (marker == destination)
427         return false;
428 
429     if (makingSelection) {
430         q->selection()->update(destination);
431     } else {
432         q->selection()->initialize(destination, q->selection()->activeSheet());
433     }
434     q->scrollToCell(destination);
435     event->accept(); // QKeyEvent
436     return true;
437 }
438 
processOtherKey(QKeyEvent * event)439 void CellToolBase::Private::processOtherKey(QKeyEvent *event)
440 {
441     register Sheet * const sheet = q->selection()->activeSheet();
442 
443     // No null character ...
444     if (event->text().isEmpty() || !q->selection()->activeSheet()->map()->isReadWrite() ||
445             !sheet || sheet->isProtected()) {
446         event->accept(); // QKeyEvent
447     } else {
448         if (!q->editor()) {
449             // Switch to editing mode
450             q->createEditor();
451         }
452         // Send it to the embedded editor.
453         QApplication::sendEvent(q->editor()->widget(), event);
454     }
455 }
456 
processControlArrowKey(QKeyEvent * event)457 bool CellToolBase::Private::processControlArrowKey(QKeyEvent *event)
458 {
459     register Sheet * const sheet = q->selection()->activeSheet();
460     if (!sheet)
461         return false;
462 
463     bool makingSelection = event->modifiers() & Qt::ShiftModifier;
464 
465     Cell cell;
466     Cell lastCell;
467     QPoint destination;
468     bool searchThroughEmpty = true;
469     int row;
470     int col;
471 
472     QPoint marker = q->selection()->marker();
473 
474     /* here, we want to move to the first or last cell in the given direction that is
475         actually being used.  Ignore empty cells and cells on hidden rows/columns */
476     switch (event->key()) {
477         //Ctrl+Qt::Key_Up
478     case Qt::Key_Up:
479 
480         cell = Cell(sheet, marker.x(), marker.y());
481         if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.y() != 1)) {
482             lastCell = cell;
483             row = marker.y() - 1;
484             cell = Cell(sheet, cell.column(), row);
485             while ((!cell.isNull()) && (row > 0) && (!cell.isEmpty())) {
486                 if (!sheet->rowFormats()->isHiddenOrFiltered(cell.row())) {
487                     lastCell = cell;
488                     searchThroughEmpty = false;
489                 }
490                 row--;
491                 if (row > 0)
492                     cell = Cell(sheet, cell.column(), row);
493             }
494             cell = lastCell;
495         }
496         if (searchThroughEmpty) {
497             cell = sheet->cellStorage()->prevInColumn(marker.x(), marker.y(), CellStorage::VisitContent);
498 
499             while ((!cell.isNull()) &&
500                     (cell.isEmpty() || (sheet->rowFormats()->isHiddenOrFiltered(cell.row())))) {
501                 cell = sheet->cellStorage()->prevInColumn(cell.column(), cell.row(), CellStorage::VisitContent);
502             }
503         }
504 
505         if (cell.isNull())
506             row = 1;
507         else
508             row = cell.row();
509 
510         int lastHiddenOrFiltered;
511         while (sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenOrFiltered)) {
512             row = lastHiddenOrFiltered + 1;
513         }
514 
515         destination.setX(qBound(1, marker.x(), q->maxCol()));
516         destination.setY(qBound(1, row, q->maxRow()));
517         break;
518 
519         //Ctrl+Qt::Key_Down
520     case Qt::Key_Down:
521 
522         cell = Cell(sheet, marker.x(), marker.y());
523         if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.y() != q->maxRow())) {
524             lastCell = cell;
525             row = marker.y() + 1;
526             cell = Cell(sheet, cell.column(), row);
527             while ((!cell.isNull()) && (row < q->maxRow()) && (!cell.isEmpty())) {
528                 if (!(sheet->rowFormats()->isHiddenOrFiltered(cell.row()))) {
529                     lastCell = cell;
530                     searchThroughEmpty = false;
531                 }
532                 row++;
533                 cell = Cell(sheet, cell.column(), row);
534             }
535             cell = lastCell;
536         }
537         if (searchThroughEmpty) {
538             cell = sheet->cellStorage()->nextInColumn(marker.x(), marker.y(), CellStorage::VisitContent);
539 
540             while ((!cell.isNull()) &&
541                     (cell.isEmpty() || (sheet->rowFormats()->isHiddenOrFiltered(cell.row())))) {
542                 cell = sheet->cellStorage()->nextInColumn(cell.column(), cell.row(), CellStorage::VisitContent);
543             }
544         }
545 
546         if (cell.isNull())
547             row = marker.y();
548         else
549             row = cell.row();
550 
551         int firstHiddenOrFiltered;
552         while (row >= 1 && sheet->rowFormats()->isHiddenOrFiltered(row, 0, &firstHiddenOrFiltered)) {
553             row = firstHiddenOrFiltered - 1;
554         }
555 
556         destination.setX(qBound(1, marker.x(), q->maxCol()));
557         destination.setY(qBound(1, row, q->maxRow()));
558         break;
559 
560 //Ctrl+Qt::Key_Left
561     case Qt::Key_Left:
562 
563         if (sheet->layoutDirection() == Qt::RightToLeft) {
564             cell = Cell(sheet, marker.x(), marker.y());
565             if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.x() != q->maxCol())) {
566                 lastCell = cell;
567                 col = marker.x() + 1;
568                 cell = Cell(sheet, col, cell.row());
569                 while ((!cell.isNull()) && (col < q->maxCol()) && (!cell.isEmpty())) {
570                     if (!(sheet->columnFormat(cell.column())->isHiddenOrFiltered())) {
571                         lastCell = cell;
572                         searchThroughEmpty = false;
573                     }
574                     col++;
575                     cell = Cell(sheet, col, cell.row());
576                 }
577                 cell = lastCell;
578             }
579             if (searchThroughEmpty) {
580                 cell = sheet->cellStorage()->nextInRow(marker.x(), marker.y(), CellStorage::VisitContent);
581 
582                 while ((!cell.isNull()) &&
583                         (cell.isEmpty() || (sheet->columnFormat(cell.column())->isHiddenOrFiltered()))) {
584                     cell = sheet->cellStorage()->nextInRow(cell.column(), cell.row(), CellStorage::VisitContent);
585                 }
586             }
587 
588             if (cell.isNull())
589                 col = marker.x();
590             else
591                 col = cell.column();
592 
593             while (sheet->columnFormat(col)->isHiddenOrFiltered()) {
594                 col--;
595             }
596 
597             destination.setX(qBound(1, col, q->maxCol()));
598             destination.setY(qBound(1, marker.y(), q->maxRow()));
599         } else {
600             cell = Cell(sheet, marker.x(), marker.y());
601             if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.x() != 1)) {
602                 lastCell = cell;
603                 col = marker.x() - 1;
604                 cell = Cell(sheet, col, cell.row());
605                 while ((!cell.isNull()) && (col > 0) && (!cell.isEmpty())) {
606                     if (!(sheet->columnFormat(cell.column())->isHiddenOrFiltered())) {
607                         lastCell = cell;
608                         searchThroughEmpty = false;
609                     }
610                     col--;
611                     if (col > 0)
612                         cell = Cell(sheet, col, cell.row());
613                 }
614                 cell = lastCell;
615             }
616             if (searchThroughEmpty) {
617                 cell = sheet->cellStorage()->prevInRow(marker.x(), marker.y(), CellStorage::VisitContent);
618 
619                 while ((!cell.isNull()) &&
620                         (cell.isEmpty() || (sheet->columnFormat(cell.column())->isHiddenOrFiltered()))) {
621                     cell = sheet->cellStorage()->prevInRow(cell.column(), cell.row(), CellStorage::VisitContent);
622                 }
623             }
624 
625             if (cell.isNull())
626                 col = 1;
627             else
628                 col = cell.column();
629 
630             while (sheet->columnFormat(col)->isHiddenOrFiltered()) {
631                 col++;
632             }
633 
634             destination.setX(qBound(1, col, q->maxCol()));
635             destination.setY(qBound(1, marker.y(), q->maxRow()));
636         }
637         break;
638 
639 //Ctrl+Qt::Key_Right
640     case Qt::Key_Right:
641 
642         if (sheet->layoutDirection() == Qt::RightToLeft) {
643             cell = Cell(sheet, marker.x(), marker.y());
644             if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.x() != 1)) {
645                 lastCell = cell;
646                 col = marker.x() - 1;
647                 cell = Cell(sheet, col, cell.row());
648                 while ((!cell.isNull()) && (col > 0) && (!cell.isEmpty())) {
649                     if (!(sheet->columnFormat(cell.column())->isHiddenOrFiltered())) {
650                         lastCell = cell;
651                         searchThroughEmpty = false;
652                     }
653                     col--;
654                     if (col > 0)
655                         cell = Cell(sheet, col, cell.row());
656                 }
657                 cell = lastCell;
658             }
659             if (searchThroughEmpty) {
660                 cell = sheet->cellStorage()->prevInRow(marker.x(), marker.y(), CellStorage::VisitContent);
661 
662                 while ((!cell.isNull()) &&
663                         (cell.isEmpty() || (sheet->columnFormat(cell.column())->isHiddenOrFiltered()))) {
664                     cell = sheet->cellStorage()->prevInRow(cell.column(), cell.row(), CellStorage::VisitContent);
665                 }
666             }
667 
668             if (cell.isNull())
669                 col = 1;
670             else
671                 col = cell.column();
672 
673             while (sheet->columnFormat(col)->isHiddenOrFiltered()) {
674                 col++;
675             }
676 
677             destination.setX(qBound(1, col, q->maxCol()));
678             destination.setY(qBound(1, marker.y(), q->maxRow()));
679         } else {
680             cell = Cell(sheet, marker.x(), marker.y());
681             if ((!cell.isNull()) && (!cell.isEmpty()) && (marker.x() != q->maxCol())) {
682                 lastCell = cell;
683                 col = marker.x() + 1;
684                 cell = Cell(sheet, col, cell.row());
685                 while ((!cell.isNull()) && (col < q->maxCol()) && (!cell.isEmpty())) {
686                     if (!(sheet->columnFormat(cell.column())->isHiddenOrFiltered())) {
687                         lastCell = cell;
688                         searchThroughEmpty = false;
689                     }
690                     col++;
691                     cell = Cell(sheet, col, cell.row());
692                 }
693                 cell = lastCell;
694             }
695             if (searchThroughEmpty) {
696                 cell = sheet->cellStorage()->nextInRow(marker.x(), marker.y(), CellStorage::VisitContent);
697 
698                 while ((!cell.isNull()) &&
699                         (cell.isEmpty() || (sheet->columnFormat(cell.column())->isHiddenOrFiltered()))) {
700                     cell = sheet->cellStorage()->nextInRow(cell.column(), cell.row(), CellStorage::VisitContent);
701                 }
702             }
703 
704             if (cell.isNull())
705                 col = marker.x();
706             else
707                 col = cell.column();
708 
709             while (sheet->columnFormat(col)->isHiddenOrFiltered()) {
710                 col--;
711             }
712 
713             destination.setX(qBound(1, col, q->maxCol()));
714             destination.setY(qBound(1, marker.y(), q->maxRow()));
715         }
716         break;
717 
718     }
719 
720     if (marker == destination)
721         return false;
722 
723     if (makingSelection) {
724         q->selection()->update(destination);
725     } else {
726         q->selection()->initialize(destination, sheet);
727     }
728     q->scrollToCell(destination);
729     return true;
730 }
731 
formatKeyPress(QKeyEvent * _ev)732 bool CellToolBase::Private::formatKeyPress(QKeyEvent * _ev)
733 {
734     if (!(_ev->modifiers() & Qt::ControlModifier))
735         return false;
736 
737     int key = _ev->key();
738     if (key != Qt::Key_Exclam && key != Qt::Key_At &&
739             key != Qt::Key_Ampersand && key != Qt::Key_Dollar &&
740             key != Qt::Key_Percent && key != Qt::Key_AsciiCircum &&
741             key != Qt::Key_NumberSign)
742         return false;
743 
744     StyleCommand* command = new StyleCommand();
745     command->setSheet(q->selection()->activeSheet());
746 
747     switch (_ev->key()) {
748     case Qt::Key_Exclam:
749         command->setText(kundo2_i18n("Number Format"));
750         command->setFormatType(Format::Number);
751         command->setPrecision(2);
752         break;
753 
754     case Qt::Key_Dollar:
755         command->setText(kundo2_i18n("Currency Format"));
756         command->setFormatType(Format::Money);
757         command->setPrecision(q->selection()->activeSheet()->map()->calculationSettings()->locale()->monetaryDecimalPlaces());
758         break;
759 
760     case Qt::Key_Percent:
761         command->setText(kundo2_i18n("Percentage Format"));
762         command->setFormatType(Format::Percentage);
763         break;
764 
765     case Qt::Key_At:
766         command->setText(kundo2_i18n("Time Format"));
767         command->setFormatType(Format::SecondeTime);
768         break;
769 
770     case Qt::Key_NumberSign:
771         command->setText(kundo2_i18n("Date Format"));
772         command->setFormatType(Format::ShortDate);
773         break;
774 
775     case Qt::Key_AsciiCircum:
776         command->setText(kundo2_i18n("Scientific Format"));
777         command->setFormatType(Format::Scientific);
778         break;
779 
780     case Qt::Key_Ampersand:
781         command->setText(kundo2_i18n("Change Border"));
782         command->setTopBorderPen(QPen(q->canvas()->resourceManager()->foregroundColor().toQColor(), 1, Qt::SolidLine));
783         command->setBottomBorderPen(QPen(q->canvas()->resourceManager()->foregroundColor().toQColor(), 1, Qt::SolidLine));
784         command->setLeftBorderPen(QPen(q->canvas()->resourceManager()->foregroundColor().toQColor(), 1, Qt::SolidLine));
785         command->setRightBorderPen(QPen(q->canvas()->resourceManager()->foregroundColor().toQColor(), 1, Qt::SolidLine));
786         break;
787 
788     default:
789         delete command;
790         return false;
791     }
792 
793     command->add(*q->selection());
794     command->execute();
795     _ev->accept(); // QKeyEvent
796 
797     return true;
798 }
799 
moveDirection(Calligra::Sheets::MoveTo direction,bool extendSelection)800 QRect CellToolBase::Private::moveDirection(Calligra::Sheets::MoveTo direction, bool extendSelection)
801 {
802     debugSheetsUI << "Canvas::moveDirection";
803 
804     register Sheet * const sheet = q->selection()->activeSheet();
805     if (!sheet)
806         return QRect();
807 
808     QPoint destination;
809     QPoint cursor = q->selection()->cursor();
810 
811     QPoint cellCorner = cursor;
812     Cell cell(sheet, cursor.x(), cursor.y());
813 
814     /* cell is either the same as the marker, or the cell that is forced obscuring
815         the marker cell
816     */
817     if (cell.isPartOfMerged()) {
818         cell = cell.masterCell();
819         cellCorner = QPoint(cell.column(), cell.row());
820     }
821 
822     /* how many cells must we move to get to the next cell? */
823     int offset = 0;
824     const ColumnFormat *cl = 0;
825     switch (direction)
826         /* for each case, figure out how far away the next cell is and then keep
827             going one row/col at a time after that until a visible row/col is found
828 
829             NEVER use cell.column() or cell.row() -- it might be a default cell
830         */
831     {
832     case Bottom:
833         offset = cell.mergedYCells() - (cursor.y() - cellCorner.y()) + 1;
834         while (((cursor.y() + offset) <= q->maxRow()) && sheet->rowFormats()->isHiddenOrFiltered(cursor.y() + offset)) {
835             offset++;
836         }
837 
838         destination = QPoint(cursor.x(), qMin(cursor.y() + offset, q->maxRow()));
839         break;
840     case Top:
841         offset = (cellCorner.y() - cursor.y()) - 1;
842         while (((cursor.y() + offset) >= 1) && sheet->rowFormats()->isHiddenOrFiltered(cursor.y() + offset)) {
843             offset--;
844         }
845         destination = QPoint(cursor.x(), qMax(cursor.y() + offset, 1));
846         break;
847     case Left:
848         offset = (cellCorner.x() - cursor.x()) - 1;
849         cl = sheet->columnFormat(cursor.x() + offset);
850         while (((cursor.x() + offset) >= 1) && cl->isHiddenOrFiltered()) {
851             offset--;
852             cl = sheet->columnFormat(cursor.x() + offset);
853         }
854         destination = QPoint(qMax(cursor.x() + offset, 1), cursor.y());
855         break;
856     case Right:
857         offset = cell.mergedXCells() - (cursor.x() - cellCorner.x()) + 1;
858         cl = sheet->columnFormat(cursor.x() + offset);
859         while (((cursor.x() + offset) <= q->maxCol()) && cl->isHiddenOrFiltered()) {
860             offset++;
861             cl = sheet->columnFormat(cursor.x() + offset);
862         }
863         destination = QPoint(qMin(cursor.x() + offset, q->maxCol()), cursor.y());
864         break;
865     case BottomFirst:
866         offset = cell.mergedYCells() - (cursor.y() - cellCorner.y()) + 1;
867         while (((cursor.y() + offset) <= q->maxRow()) && sheet->rowFormats()->isHiddenOrFiltered(cursor.y() + offset)) {
868             ++offset;
869         }
870 
871         destination = QPoint(1, qMin(cursor.y() + offset, q->maxRow()));
872         break;
873     case NoMovement:
874         destination = cursor;
875         break;
876     }
877 
878     if (extendSelection) {
879         q->selection()->update(destination);
880     } else {
881         q->selection()->initialize(destination, sheet);
882     }
883     q->scrollToCell(destination);
884     updateEditor(Cell(q->selection()->activeSheet(), q->selection()->cursor()));
885 
886     return QRect(cursor, destination);
887 }
888 
paintSelection(QPainter & painter,const QRectF & viewRect)889 void CellToolBase::Private::paintSelection(QPainter &painter, const QRectF &viewRect)
890 {
891     if (q->selection()->referenceSelection() || q->editor()) {
892         return;
893     }
894     Sheet *const sheet = q->selection()->activeSheet();
895 
896     // save the painter state
897     painter.save();
898     // disable antialiasing
899     painter.setRenderHint(QPainter::Antialiasing, false);
900     // Extend the clip rect by one in each direction to avoid artefacts caused by rounding errors.
901     // TODO Stefan: This unites the region's rects. May be bad. Check!
902     painter.setClipRegion(painter.clipRegion().boundingRect().adjusted(-1, -1, 1, 1));
903 
904     QLineF line;
905     QPen pen(QApplication::palette().text().color(), q->canvas()->viewConverter()->viewToDocumentX(2.0));
906     painter.setPen(pen);
907 
908     const Calligra::Sheets::Selection* selection = q->selection();
909     const QRect currentRange = selection->extendToMergedAreas(QRect(selection->anchor(), selection->marker()));
910     Region::ConstIterator end(selection->constEnd());
911     for (Region::ConstIterator it(selection->constBegin()); it != end; ++it) {
912         const QRect range = (*it)->isAll() ? (*it)->rect() : selection->extendToMergedAreas((*it)->rect());
913 
914         // Only the active element (the one with the anchor) will be drawn with a border
915         const bool current = (currentRange == range);
916 
917         double positions[4];
918         bool paintSides[4];
919         retrieveMarkerInfo(range, viewRect, positions, paintSides);
920 
921         double left =   positions[0];
922         double top =    positions[1];
923         double right =  positions[2];
924         double bottom = positions[3];
925         if (sheet->layoutDirection() == Qt::RightToLeft) {
926             // The painter's origin is translated by the negative canvas offset.
927             // viewRect.left() is the canvas offset. Add it once to the
928             // coordinates. Then, the upper left corner of the canvas has to
929             // match the correct document position, which is the scrolling
930             // offset (viewRect.left()) plus the width of the visible area
931             // (viewRect.width()); that's the right border (left+width).
932             const qreal offset = /*2 * viewRect.left() +*/ viewRect.width();
933             left = offset - positions[2];
934             right = offset - positions[0];
935         }
936 
937         bool paintLeft =   paintSides[0];
938         bool paintTop =    paintSides[1];
939         bool paintRight =  paintSides[2];
940         bool paintBottom = paintSides[3];
941         if (sheet->layoutDirection() == Qt::RightToLeft) {
942             paintLeft  = paintSides[2];
943             paintRight = paintSides[0];
944         }
945 
946         const double unzoomedPixelX = q->canvas()->viewConverter()->viewToDocumentX(1.0);
947         const double unzoomedPixelY = q->canvas()->viewConverter()->viewToDocumentY(1.0);
948         // get the transparent selection color
949         QColor selectionColor(QApplication::palette().highlight().color());
950         selectionColor.setAlpha(127);
951         if (current) {
952             // save old clip region
953             const QRegion clipRegion = painter.clipRegion();
954             // clip out the cursor region
955             const QRect cursor = QRect(selection->cursor(), selection->cursor());
956             const QRect extCursor = selection->extendToMergedAreas(cursor);
957             QRectF cursorRect = sheet->cellCoordinatesToDocument(extCursor);
958             if (sheet->layoutDirection() == Qt::RightToLeft) {
959                 // See comment above.
960                 const qreal offset = /*2 * viewRect.left() +*/ viewRect.width();
961                 const qreal left = offset - cursorRect.right();
962                 const qreal right = offset - cursorRect.left();
963                 cursorRect.setLeft(left);
964                 cursorRect.setRight(right);
965             }
966             cursorRect.adjust(unzoomedPixelX, unzoomedPixelY, unzoomedPixelX, unzoomedPixelY);
967             painter.setClipRegion(clipRegion.subtracted(cursorRect.toRect()));
968             // draw the transparent selection background
969             painter.fillRect(QRectF(left, top, right - left, bottom - top), selectionColor);
970             // restore clip region
971             painter.setClipRegion(clipRegion);
972         } else {
973             // draw the transparent selection background
974             painter.fillRect(QRectF(left, top, right - left, bottom - top), selectionColor);
975         }
976 
977         if (paintTop) {
978             line = QLineF(left, top, right, top);
979             painter.drawLine(line);
980         }
981         if (selection->activeSheet()->layoutDirection() == Qt::RightToLeft) {
982             if (paintRight) {
983                 line = QLineF(right, top, right, bottom);
984                 painter.drawLine(line);
985             }
986             if (paintLeft && paintBottom && current) {
987                 /* then the 'handle' in the bottom left corner is visible. */
988                 line = QLineF(left, top, left, bottom - 3 * unzoomedPixelY);
989                 painter.drawLine(line);
990                 line = QLineF(left + 4 * unzoomedPixelX,  bottom, right + unzoomedPixelY, bottom);
991                 painter.drawLine(line);
992                 painter.fillRect(QRectF(left - 2 * unzoomedPixelX, bottom - 2 * unzoomedPixelY,
993                                         5 * unzoomedPixelX, 5 * unzoomedPixelY), painter.pen().color());
994             } else {
995                 if (paintLeft) {
996                     line = QLineF(left, top, left, bottom);
997                     painter.drawLine(line);
998                 }
999                 if (paintBottom) {
1000                     line = QLineF(left, bottom, right, bottom);
1001                     painter.drawLine(line);
1002                 }
1003             }
1004         } else { // activeSheet()->layoutDirection() == Qt::LeftToRight
1005             if (paintLeft) {
1006                 line = QLineF(left, top, left, bottom);
1007                 painter.drawLine(line);
1008             }
1009             if (paintRight && paintBottom && current) {
1010                 /* then the 'handle' in the bottom right corner is visible. */
1011                 line = QLineF(right, top, right, bottom - 3 * unzoomedPixelY);
1012                 painter.drawLine(line);
1013                 line = QLineF(left, bottom, right - 3 * unzoomedPixelX, bottom);
1014                 painter.drawLine(line);
1015                 painter.fillRect(QRectF(right - 2 * unzoomedPixelX, bottom - 2 * unzoomedPixelX,
1016                                         5 * unzoomedPixelX, 5 * unzoomedPixelY), painter.pen().color());
1017             } else {
1018                 if (paintRight) {
1019                     line = QLineF(right, top, right, bottom);
1020                     painter.drawLine(line);
1021                 }
1022                 if (paintBottom) {
1023                     line = QLineF(left, bottom, right, bottom);
1024                     painter.drawLine(line);
1025                 }
1026             }
1027         }
1028     }
1029     // restore painter state
1030     painter.restore();
1031 }
1032 
paintReferenceSelection(QPainter & painter,const QRectF & viewRect)1033 void CellToolBase::Private::paintReferenceSelection(QPainter &painter, const QRectF &viewRect)
1034 {
1035     Q_UNUSED(viewRect);
1036     if (!q->selection()->referenceSelection()) {
1037         return;
1038     }
1039     // save painter state
1040     painter.save();
1041 
1042     // Define the reference selection handle.
1043     const qreal pixelX = q->canvas()->viewConverter()->viewToDocumentX(1);
1044     const qreal pixelY = q->canvas()->viewConverter()->viewToDocumentY(1);
1045     const QRectF handleArea(-3 * pixelX, -3 * pixelY, 6 * pixelX, 6 * pixelY);
1046 
1047     // A list of already found regions to color the same region with the same color.
1048     QSet<QString> alreadyFoundRegions;
1049     // The colors for the referenced ranges and the color index.
1050     const QList<QColor> colors = q->selection()->colors();
1051     int index = 0;
1052 
1053     // Iterate over the referenced ranges.
1054     const Region::ConstIterator end(q->selection()->constEnd());
1055     for (Region::ConstIterator it(q->selection()->constBegin()); it != end; ++it) {
1056         Sheet *const sheet = (*it)->sheet();
1057         // Only paint ranges or cells on the current sheet
1058         if (sheet != q->selection()->activeSheet()) {
1059             index++;
1060             continue;
1061         }
1062         // Only paint a reference once.
1063         if (alreadyFoundRegions.contains((*it)->name())) {
1064             continue;
1065         }
1066         alreadyFoundRegions.insert((*it)->name());
1067 
1068         const QRect range = q->selection()->extendToMergedAreas((*it)->rect());
1069         QRectF area = sheet->cellCoordinatesToDocument(range);
1070 
1071         // Convert region from sheet coordinates to canvas coordinates for use with the painter
1072         // retrieveMarkerInfo(region,viewRect,positions,paintSides);
1073 
1074         // Now adjust the highlight rectangle is slightly inside the cell borders (this means
1075         // that multiple highlighted cells look nicer together as the borders do not clash)
1076         area.adjust(pixelX, pixelY, -pixelX, -pixelY);
1077 
1078         // The current color.
1079         const QColor color = colors[index++ % colors.size()];
1080 
1081         // Paint the reference range's outline.
1082         if ((*it)->sheet()->layoutDirection() == Qt::RightToLeft) {
1083             // See comment in paintSelection().
1084             const qreal offset = /*2 * viewRect.left() +*/ viewRect.width();
1085             const qreal left = offset - area.right();
1086             const qreal right = offset - area.left();
1087             area.setLeft(left);
1088             area.setRight(right);
1089         }
1090 
1091         painter.setBrush(QBrush());
1092         painter.setPen(QPen(color, 0));
1093         painter.drawRect(area);
1094 
1095         // Now draw the size grip (the little rectangle on the bottom right-hand corner of
1096         // the range which the user can click and drag to resize the region)
1097         painter.setPen(QPen(Qt::white, 0));
1098         painter.setBrush(color);
1099         const bool rtl = sheet->layoutDirection() == Qt::RightToLeft;
1100         const QPointF corner(rtl ? area.bottomLeft() : area.bottomRight());
1101         painter.drawRect(handleArea.translated(corner));
1102     }
1103 
1104     // restore painter state
1105     painter.restore();
1106 }
1107 
retrieveMarkerInfo(const QRect & cellRange,const QRectF & viewRect,double positions[],bool paintSides[])1108 void CellToolBase::Private::retrieveMarkerInfo(const QRect &cellRange, const QRectF &viewRect,
1109         double positions[], bool paintSides[])
1110 {
1111     // Everything is in document coordinates here.
1112     // The layout direction, which is view dependent, is applied afterwards.
1113 
1114     const Sheet* sheet = q->selection()->activeSheet();
1115     const QRectF visibleRect = sheet->cellCoordinatesToDocument(cellRange);
1116 
1117     /* these vars are used for clarity, the array for simpler function arguments  */
1118     qreal left = visibleRect.left();
1119     qreal top = visibleRect.top();
1120     qreal right = visibleRect.right();
1121     qreal bottom = visibleRect.bottom();
1122 
1123     /* left, top, right, bottom */
1124     paintSides[0] = (viewRect.left() <= left) && (left <= viewRect.right()) &&
1125                     (bottom >= viewRect.top()) && (top <= viewRect.bottom());
1126     paintSides[1] = (viewRect.top() <= top) && (top <= viewRect.bottom()) &&
1127                     (right >= viewRect.left()) && (left <= viewRect.right());
1128     paintSides[2] = (viewRect.left() <= right) && (right <= viewRect.right()) &&
1129                     (bottom >= viewRect.top()) && (top <= viewRect.bottom());
1130     paintSides[3] = (viewRect.top() <= bottom) && (bottom <= viewRect.bottom()) &&
1131                     (right >= viewRect.left()) && (left <= viewRect.right());
1132 
1133     positions[0] = qMax(left,   viewRect.left());
1134     positions[1] = qMax(top,    viewRect.top());
1135     positions[2] = qMin(right,  viewRect.right());
1136     positions[3] = qMin(bottom, viewRect.bottom());
1137 }
1138 
popupActionList() const1139 QList<QAction*> CellToolBase::Private::popupActionList() const
1140 {
1141     QList<QAction*> actions;
1142     const Cell cell = Cell(q->selection()->activeSheet(), q->selection()->marker());
1143     const bool isProtected = !q->selection()->activeSheet()->map()->isReadWrite() ||
1144                              (q->selection()->activeSheet()->isProtected() &&
1145                               !(cell.style().notProtected() && q->selection()->isSingular()));
1146     if (!isProtected) {
1147         actions.append(q->action("cellStyle"));
1148         actions.append(popupMenuActions["separator1"]);
1149         actions.append(q->action("cut"));
1150     }
1151     actions.append(q->action("copy"));
1152     if (!isProtected) {
1153         actions.append(q->action("paste"));
1154         actions.append(q->action("specialPaste"));
1155         actions.append(q->action("pasteWithInsertion"));
1156         actions.append(popupMenuActions["separator2"]);
1157         actions.append(q->action("clearAll"));
1158         actions.append(q->action("adjust"));
1159         actions.append(q->action("setDefaultStyle"));
1160         actions.append(q->action("setAreaName"));
1161 
1162         if (!q->selection()->isColumnOrRowSelected()) {
1163             actions.append(popupMenuActions["separator3"]);
1164             actions.append(popupMenuActions["insertCell"]);
1165             actions.append(popupMenuActions["deleteCell"]);
1166         } else if (q->selection()->isColumnSelected()) {
1167             actions.append(q->action("resizeCol"));
1168             actions.append(popupMenuActions["adjustColumn"]);
1169             actions.append(popupMenuActions["separator4"]);
1170             actions.append(popupMenuActions["insertColumn"]);
1171             actions.append(popupMenuActions["deleteColumn"]);
1172             actions.append(q->action("hideColumn"));
1173 
1174             q->action("showSelColumns")->setEnabled(false);
1175             const ColumnFormat* columnFormat;
1176             Region::ConstIterator endOfList = q->selection()->constEnd();
1177             for (Region::ConstIterator it = q->selection()->constBegin(); it != endOfList; ++it) {
1178                 QRect range = (*it)->rect();
1179                 int col;
1180                 for (col = range.left(); col < range.right(); ++col) {
1181                     columnFormat = q->selection()->activeSheet()->columnFormat(col);
1182                     if (columnFormat->isHidden()) {
1183                         q->action("showSelColumns")->setEnabled(true);
1184                         actions.append(q->action("showSelColumns"));
1185                         break;
1186                     }
1187                 }
1188                 if (range.left() > 1 && col == range.right()) {
1189                     bool allHidden = true;
1190                     for (col = 1; col < range.left(); ++col) {
1191                         columnFormat = q->selection()->activeSheet()->columnFormat(col);
1192                         allHidden &= columnFormat->isHidden();
1193                     }
1194                     if (allHidden) {
1195                         q->action("showSelColumns")->setEnabled(true);
1196                         actions.append(q->action("showSelColumns"));
1197                         break;
1198                     }
1199                 } else {
1200                     break;
1201                 }
1202             }
1203         } else if (q->selection()->isRowSelected()) {
1204             actions.append(q->action("resizeRow"));
1205             actions.append(popupMenuActions["adjustRow"]);
1206             actions.append(popupMenuActions["separator5"]);
1207             actions.append(popupMenuActions["insertRow"]);
1208             actions.append(popupMenuActions["deleteRow"]);
1209             actions.append(q->action("hideRow"));
1210 
1211             q->action("showSelRows")->setEnabled(false);
1212             Region::ConstIterator endOfList = q->selection()->constEnd();
1213             for (Region::ConstIterator it = q->selection()->constBegin(); it != endOfList; ++it) {
1214                 QRect range = (*it)->rect();
1215                 int row;
1216                 for (row = range.top(); row < range.bottom(); ++row) {
1217                     if (q->selection()->activeSheet()->rowFormats()->isHidden(row)) {
1218                         q->action("showSelRows")->setEnabled(true);
1219                         actions.append(q->action("showSelRows"));
1220                         break;
1221                     }
1222                 }
1223                 if (range.top() > 1 && row == range.bottom()) {
1224                     bool allHidden = true;
1225                     for (row = 1; row < range.top(); ++row) {
1226                         allHidden &= q->selection()->activeSheet()->rowFormats()->isHidden(row);
1227                     }
1228                     if (allHidden) {
1229                         q->action("showSelRows")->setEnabled(true);
1230                         actions.append(q->action("showSelRows"));
1231                         break;
1232                     }
1233                 } else {
1234                     break;
1235                 }
1236             }
1237         }
1238         actions.append(popupMenuActions["separator6"]);
1239         actions.append(popupMenuActions["comment"]);
1240         if (!cell.comment().isEmpty()) {
1241             actions.append(popupMenuActions["clearComment"]);
1242         }
1243 
1244         if (testListChoose(q->selection())) {
1245             actions.append(popupMenuActions["separator7"]);
1246             actions.append(popupMenuActions["listChoose"]);
1247         }
1248     }
1249     return actions;
1250 }
1251 
createPopupMenuActions()1252 void CellToolBase::Private::createPopupMenuActions()
1253 {
1254     QAction* action = 0;
1255 
1256     for (int i = 1; i <= 7; ++i) {
1257         action = new QAction(q);
1258         action->setSeparator(true);
1259         popupMenuActions.insert(QString("separator%1").arg(i), action);
1260     }
1261 
1262     action = new QAction(koIcon("insertcell"), i18n("Insert Cells..."), q);
1263     connect(action, SIGNAL(triggered(bool)), q, SLOT(insertCells()));
1264     popupMenuActions.insert("insertCell", action);
1265 
1266     action = new QAction(koIcon("removecell"), i18n("Delete Cells..."), q);
1267     connect(action, SIGNAL(triggered(bool)), q, SLOT(deleteCells()));
1268     popupMenuActions.insert("deleteCell", action);
1269 
1270     action = new QAction(koIcon("adjustcol"), i18n("Adjust Column"), q);
1271     connect(action, SIGNAL(triggered(bool)), q, SLOT(adjustColumn()));
1272     popupMenuActions.insert("adjustColumn", action);
1273 
1274     action = new QAction(koIcon("edit-table-insert-column-left"), i18n("Insert Columns"), q);
1275     connect(action, SIGNAL(triggered(bool)), q, SLOT(insertColumn()));
1276     popupMenuActions.insert("insertColumn", action);
1277 
1278     action = new QAction(koIcon("edit-table-delete-column"), i18n("Delete Columns"), q);
1279     connect(action, SIGNAL(triggered(bool)), q, SLOT(deleteColumn()));
1280     popupMenuActions.insert("deleteColumn", action);
1281 
1282     action = new QAction(koIcon("adjustrow"), i18n("Adjust Row"), q);
1283     connect(action, SIGNAL(triggered(bool)), q, SLOT(adjustRow()));
1284     popupMenuActions.insert("adjustRow", action);
1285 
1286     action = new QAction(koIcon("edit-table-insert-row-above"), i18n("Insert Rows"), q);
1287     connect(action, SIGNAL(triggered(bool)), q, SLOT(insertRow()));
1288     popupMenuActions.insert("insertRow", action);
1289 
1290     action = new QAction(koIcon("edit-table-delete-row"), i18n("Delete Rows"), q);
1291     connect(action, SIGNAL(triggered(bool)), q, SLOT(deleteRow()));
1292     popupMenuActions.insert("deleteRow", action);
1293 
1294     action = new QAction(i18n("Selection List..."), q);
1295     connect(action, SIGNAL(triggered(bool)), q, SLOT(listChoosePopupMenu()));
1296     popupMenuActions.insert("listChoose", action);
1297 
1298     action = new QAction(koIcon("edit-comment"), i18n("Comment"), q);
1299     connect(action, SIGNAL(triggered(bool)), q, SLOT(comment()));
1300     popupMenuActions.insert("comment", action);
1301 
1302     action = new QAction(koIcon("delete-comment"),i18n("Clear Comment"), q);
1303     connect(action, SIGNAL(triggered(bool)), q, SLOT(clearComment()));
1304     popupMenuActions.insert("clearComment", action);
1305 
1306 }
1307 
testListChoose(Selection * selection) const1308 bool CellToolBase::Private::testListChoose(Selection *selection) const
1309 {
1310     const Sheet *const sheet = selection->activeSheet();
1311     const Cell cursorCell(sheet, selection->cursor());
1312     const CellStorage *const storage = sheet->cellStorage();
1313 
1314     const Region::ConstIterator end(selection->constEnd());
1315     for (Region::ConstIterator it(selection->constBegin()); it != end; ++it) {
1316         const QRect range = (*it)->rect();
1317         if (cursorCell.column() < range.left() || cursorCell.column() > range.right()) {
1318             continue; // next range
1319         }
1320         Cell cell;
1321         if (range.top() == 1) {
1322             cell = storage->firstInColumn(cursorCell.column(), CellStorage::Values);
1323         } else {
1324             cell = storage->nextInColumn(cursorCell.column(), range.top() - 1, CellStorage::Values);
1325         }
1326         while (!cell.isNull() && cell.row() <= range.bottom()) {
1327             if (cell.isDefault() || cell.isPartOfMerged()
1328                     || cell.isFormula() || cell.isTime() || cell.isDate()
1329                     || cell.value().isNumber() || cell.value().asString().isEmpty()
1330                     || (cell == cursorCell)) {
1331                 cell = storage->nextInColumn(cell.column(), cell.row(), CellStorage::Values);
1332                 continue;
1333             }
1334             if (cell.userInput() != cursorCell.userInput()) {
1335                 return true;
1336             }
1337             cell = storage->nextInColumn(cell.column(), cell.row(), CellStorage::Values);
1338         }
1339     }
1340     return false;
1341 }
1342