1 /* This file is part of the KDE project
2    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3    Copyright 2005,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19 */
20 
21 // Local
22 #include "AbstractRegionCommand.h"
23 
24 #include <QApplication>
25 
26 #include <KLocalizedString>
27 #include <kpassivepopup.h>
28 
29 #include <KoCanvasBase.h>
30 
31 #include "Cell.h"
32 #include "CellStorage.h"
33 #include "Damages.h"
34 #include "Map.h"
35 #include "Sheet.h"
36 
37 using namespace Calligra::Sheets;
38 
39 //BEGIN NOTE Stefan: some words on operations
40 //
41 // 1. SubTotal
42 // a) Makes no sense to extend to non-contiguous selections (NCS) as
43 //    it refers to a change in one column.
44 // b) No special undo command available yet.
45 //
46 // 2. AutoSum
47 // a) should insert cell at the end of the selection, if the last
48 //    is not empty
49 // b) opens an editor, if the user's intention is fuzzy -> hard to
50 //    convert to NCS
51 //END
52 
53 /***************************************************************************
54   class AbstractRegionCommand
55 ****************************************************************************/
56 
AbstractRegionCommand(KUndo2Command * parent)57 AbstractRegionCommand::AbstractRegionCommand(KUndo2Command* parent)
58         : Region(),
59         KUndo2Command(parent),
60         m_sheet(0),
61         m_reverse(false),
62         m_firstrun(true),
63         m_register(true),
64         m_success(true),
65         m_checkLock(false)
66 {
67 }
68 
~AbstractRegionCommand()69 AbstractRegionCommand::~AbstractRegionCommand()
70 {
71 }
72 
execute(KoCanvasBase * canvas)73 bool AbstractRegionCommand::execute(KoCanvasBase* canvas)
74 {
75     if (!m_firstrun)
76         return false;
77     if (!isApproved())
78         return false;
79     // registering in undo history?
80     if (m_register)
81         canvas ? canvas->addCommand(this) : m_sheet->map()->addCommand(this);
82     else
83         redo();
84     return m_success;
85 }
86 
redo()87 void AbstractRegionCommand::redo()
88 {
89     //sebsauer; following conditions and warning makes no sense cause we would crash
90     //later on cause m_sheet is direct accessed without NULL-check. So, let's add
91     //some asserts to check for the m_sheet=NULL case to be able to fix it if we
92     //can reproduce the situation.
93 #if 0
94     if (!m_sheet) {
95         warnSheets << "AbstractRegionCommand::redo(): No explicit m_sheet is set. "
96         << "Manipulating all sheets of the region." << endl;
97     }
98 #else
99     Q_ASSERT(m_sheet);
100     if (!m_sheet) { m_success = false; return; }
101 #endif
102 
103     m_success = true;
104     bool successfully = true;
105     successfully = preProcessing();
106     if (!successfully) {
107         m_success = false;
108         return;   // do nothing if pre-processing fails
109     }
110 
111     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
112     // FIXME Stefan: Does every derived command damage the visual cache? No!
113     m_sheet->map()->addDamage(new CellDamage(m_sheet, *this, CellDamage::Appearance));
114 
115     successfully = mainProcessing();
116     if (!successfully) {
117         m_success = false;
118         warnSheets << "AbstractRegionCommand::redo(): processing was not successful!";
119     }
120 
121     successfully = true;
122     successfully = postProcessing();
123     if (!successfully) {
124         m_success = false;
125         warnSheets << "AbstractRegionCommand::redo(): postprocessing was not successful!";
126     }
127 
128     QApplication::restoreOverrideCursor();
129 
130     m_firstrun = false;
131 }
132 
undo()133 void AbstractRegionCommand::undo()
134 {
135     m_reverse = !m_reverse;
136     redo();
137     m_reverse = !m_reverse;
138 }
139 
isApproved() const140 bool AbstractRegionCommand::isApproved() const
141 {
142     //sebsauer; same as in AbstractRegionCommand::redo
143     Q_ASSERT(m_sheet);
144     if (!m_sheet) return false;
145 
146     const QList<Element *> elements = cells();
147     const int begin = m_reverse ? elements.count() - 1 : 0;
148     const int end = m_reverse ? -1 : elements.count();
149     if (m_checkLock && m_sheet->cellStorage()->hasLockedCells(*this)) {
150         KPassivePopup::message(i18n("Processing is not possible, because some "
151                                     "cells are locked as elements of a matrix."),
152                                QApplication::activeWindow());
153         return false;
154     }
155     if (m_sheet->isProtected()) {
156         for (int i = begin; i != end; m_reverse ? --i : ++i) {
157             const QRect range = elements[i]->rect();
158 
159             for (int col = range.left(); col <= range.right(); ++col) {
160                 for (int row = range.top(); row <= range.bottom(); ++row) {
161                     Cell cell(m_sheet, col, row);
162                     if (!cell.style().notProtected()) {
163                         KPassivePopup::message(i18n("Processing is not possible, "
164                                                     "because some cells are protected."),
165                                                QApplication::activeWindow());
166                         return false;
167                     }
168                 }
169             }
170         }
171     }
172     return true;
173 }
174 
mainProcessing()175 bool AbstractRegionCommand::mainProcessing()
176 {
177     //sebsauer; same as in AbstractRegionCommand::redo
178     Q_ASSERT(m_sheet);
179     if (!m_sheet) return false;
180 
181     bool successfully = true;
182     const QList<Element *> elements = cells();
183     const int begin = m_reverse ? elements.count() - 1 : 0;
184     const int end = m_reverse ? -1 : elements.count();
185     for (int i = begin; i != end; m_reverse ? --i : ++i) {
186         successfully = successfully && process(elements[i]);
187     }
188     return successfully;
189 }
190