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