1 /* This file is part of the KDE project
2 Copyright 2005,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19
20 // Local
21 #include "MergeCommand.h"
22
23 #include <KLocalizedString>
24 #include <kmessagebox.h>
25
26 #include "Cell.h"
27 #include "Damages.h"
28 #include "Map.h"
29 #include "ui/Selection.h" // FIXME detach from ui
30 #include "Sheet.h"
31
32 using namespace Calligra::Sheets;
33
MergeCommand(KUndo2Command * parent)34 MergeCommand::MergeCommand(KUndo2Command* parent)
35 : AbstractRegionCommand(parent),
36 m_merge(true),
37 m_mergeHorizontal(false),
38 m_mergeVertical(false),
39 m_unmerger(0),
40 m_selection(0)
41 {
42 m_checkLock = true;
43 }
44
~MergeCommand()45 MergeCommand::~MergeCommand()
46 {
47 delete m_unmerger;
48 }
49
process(Element * element)50 bool MergeCommand::process(Element* element)
51 {
52 if (element->type() != Element::Range || element->isRow() || element->isColumn()) {
53 // TODO Stefan: remove these elements?!
54 return true;
55 }
56
57 // sanity check
58 if (m_sheet->isProtected() || m_sheet->map()->isProtected()) {
59 return false;
60 }
61
62 QRect range = element->rect();
63 int left = range.left();
64 int right = range.right();
65 int top = range.top();
66 int bottom = range.bottom();
67 int height = range.height();
68 int width = range.width();
69
70 bool doMerge = m_reverse ? (!m_merge) : m_merge;
71
72 if (doMerge) {
73 if (m_mergeHorizontal) {
74 for (int row = top; row <= bottom; ++row) {
75 int rows = 0;
76 for (int col = left; col <= right; ++col) {
77 Cell cell = Cell(m_sheet, col, row);
78 if (cell.doesMergeCells()) {
79 rows = qMax(rows, cell.mergedYCells());
80 cell.mergeCells(col, row, 0, 0);
81 }
82 }
83 Cell cell = Cell(m_sheet, left, row);
84 if (!cell.isPartOfMerged()) {
85 cell.mergeCells(left, row, width - 1, rows);
86 }
87 }
88 } else if (m_mergeVertical) {
89 for (int col = left; col <= right; ++col) {
90 int cols = 0;
91 for (int row = top; row <= bottom; ++row) {
92 Cell cell = Cell(m_sheet, col, row);
93 if (cell.doesMergeCells()) {
94 cols = qMax(cols, cell.mergedXCells());
95 cell.mergeCells(col, row, 0, 0);
96 }
97 }
98 Cell cell = Cell(m_sheet, col, top);
99 if (!cell.isPartOfMerged()) {
100 cell.mergeCells(col, top, cols, height - 1);
101 }
102 }
103 } else {
104 Cell cell = Cell(m_sheet, left, top);
105 cell.mergeCells(left, top, width - 1, height - 1);
106 }
107 } else { // dissociate
108 for (int col = left; col <= right; ++col) {
109 for (int row = top; row <= bottom; ++row) {
110 Cell cell = Cell(m_sheet, col, row);
111 if (!cell.doesMergeCells()) {
112 continue;
113 }
114 cell.mergeCells(col, row, 0, 0);
115 }
116 }
117 }
118
119 // adjust selection
120 if (m_selection)
121 m_selection->isEmpty() ? m_selection->initialize(range, m_sheet) : m_selection->extend(range, m_sheet);
122
123 return true;
124 }
125
name() const126 KUndo2MagicString MergeCommand::name() const
127 {
128 if (m_merge) { // MergeCommand
129 if (m_mergeHorizontal) {
130 return kundo2_i18n("Merge Cells Horizontally");
131 } else if (m_mergeVertical) {
132 return kundo2_i18n("Merge Cells Vertically");
133 } else {
134 return kundo2_i18n("Merge Cells");
135 }
136 }
137 return kundo2_i18n("Dissociate Cells");
138 }
139
preProcessing()140 bool MergeCommand::preProcessing()
141 {
142 if (isColumnOrRowSelected()) {
143 KMessageBox::information(0, i18n("Merging of columns or rows is not supported."));
144 return false;
145 }
146
147 if (m_firstrun) {
148 setText(name());
149
150 // reduce the region to the region occupied by merged cells
151 Region mergedCells;
152 ConstIterator endOfList = constEnd();
153 for (ConstIterator it = constBegin(); it != endOfList; ++it) {
154 Element* element = *it;
155 QRect range = element->rect();
156 int right = range.right();
157 int bottom = range.bottom();
158 for (int row = range.top(); row <= bottom; ++row) {
159 for (int col = range.left(); col <= right; ++col) {
160 Cell cell = Cell(m_sheet, col, row);
161 if (cell.doesMergeCells()) {
162 QRect rect(col, row, cell.mergedXCells() + 1, cell.mergedYCells() + 1);
163 mergedCells.add(rect);
164 }
165 }
166 }
167 }
168
169 if (m_merge) { // MergeCommand
170 // we're in the manipulator's first execution
171 // initialize the undo manipulator
172 m_unmerger = new MergeCommand();
173 if (!m_mergeHorizontal && !m_mergeVertical) {
174 m_unmerger->setReverse(true);
175 }
176 m_unmerger->setSheet(m_sheet);
177 m_unmerger->setRegisterUndo(false);
178 m_unmerger->add(mergedCells);
179 } else { // DissociateManipulator
180 clear();
181 add(mergedCells);
182 }
183 }
184
185 if (m_merge) { // MergeCommand
186 if (m_reverse) { // dissociate
187 } else { // merge
188 // Dissociate cells before merging the whole region.
189 // For horizontal/vertical merging the cells stay
190 // as they are. E.g. the region contains a merged cell
191 // occupying two rows. Then the horizontal merge should
192 // keep the height of two rows and extend the merging to the
193 // region's width. In this case the unmerging is done while
194 // processing each region element.
195 if (!m_mergeHorizontal && !m_mergeVertical) {
196 m_unmerger->redo();
197 }
198 }
199 }
200 // Clear the associated selection, if any. The merge/dissociate process will restore
201 // selections. This ensures that the selection isn't broken after merging.
202 if (m_selection) m_selection->Region::clear();
203
204 return AbstractRegionCommand::preProcessing();
205 }
206
postProcessing()207 bool MergeCommand::postProcessing()
208 {
209 if (m_merge) { // MergeCommand
210 if (m_reverse) { // dissociate
211 // restore the old merge status
212 if (m_mergeHorizontal || m_mergeVertical) {
213 m_unmerger->redo();
214 } else {
215 m_unmerger->undo();
216 }
217 }
218 }
219 m_sheet->map()->addDamage(new CellDamage(m_sheet, *this, CellDamage::Appearance));
220 return true;
221 }
222