File indexing completed on 2024-05-12 16:35:12

0001 /* This file is part of the KDE project
0002    Copyright 2005,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 // Local
0021 #include "MergeCommand.h"
0022 
0023 #include <KLocalizedString>
0024 #include <kmessagebox.h>
0025 
0026 #include "Cell.h"
0027 #include "Damages.h"
0028 #include "Map.h"
0029 #include "ui/Selection.h" // FIXME detach from ui
0030 #include "Sheet.h"
0031 
0032 using namespace Calligra::Sheets;
0033 
0034 MergeCommand::MergeCommand(KUndo2Command* parent)
0035         : AbstractRegionCommand(parent),
0036         m_merge(true),
0037         m_mergeHorizontal(false),
0038         m_mergeVertical(false),
0039         m_unmerger(0),
0040         m_selection(0)
0041 {
0042     m_checkLock = true;
0043 }
0044 
0045 MergeCommand::~MergeCommand()
0046 {
0047     delete m_unmerger;
0048 }
0049 
0050 bool MergeCommand::process(Element* element)
0051 {
0052     if (element->type() != Element::Range || element->isRow() || element->isColumn()) {
0053         // TODO Stefan: remove these elements?!
0054         return true;
0055     }
0056 
0057     // sanity check
0058     if (m_sheet->isProtected() || m_sheet->map()->isProtected()) {
0059         return false;
0060     }
0061 
0062     QRect range = element->rect();
0063     int left   = range.left();
0064     int right  = range.right();
0065     int top    = range.top();
0066     int bottom = range.bottom();
0067     int height = range.height();
0068     int width  = range.width();
0069 
0070     bool doMerge = m_reverse ? (!m_merge) : m_merge;
0071 
0072     if (doMerge) {
0073         if (m_mergeHorizontal) {
0074             for (int row = top; row <= bottom; ++row) {
0075                 int rows = 0;
0076                 for (int col = left; col <= right; ++col) {
0077                     Cell cell = Cell(m_sheet, col, row);
0078                     if (cell.doesMergeCells()) {
0079                         rows = qMax(rows, cell.mergedYCells());
0080                         cell.mergeCells(col, row, 0, 0);
0081                     }
0082                 }
0083                 Cell cell = Cell(m_sheet,  left, row);
0084                 if (!cell.isPartOfMerged()) {
0085                     cell.mergeCells(left, row, width - 1, rows);
0086                 }
0087             }
0088         } else if (m_mergeVertical) {
0089             for (int col = left; col <= right; ++col) {
0090                 int cols = 0;
0091                 for (int row = top; row <= bottom; ++row) {
0092                     Cell cell = Cell(m_sheet, col, row);
0093                     if (cell.doesMergeCells()) {
0094                         cols = qMax(cols, cell.mergedXCells());
0095                         cell.mergeCells(col, row, 0, 0);
0096                     }
0097                 }
0098                 Cell cell = Cell(m_sheet, col, top);
0099                 if (!cell.isPartOfMerged()) {
0100                     cell.mergeCells(col, top, cols, height - 1);
0101                 }
0102             }
0103         } else {
0104             Cell cell = Cell(m_sheet,  left, top);
0105             cell.mergeCells(left, top, width - 1, height - 1);
0106         }
0107     } else { // dissociate
0108         for (int col = left; col <= right; ++col) {
0109             for (int row = top; row <= bottom; ++row) {
0110                 Cell cell = Cell(m_sheet, col, row);
0111                 if (!cell.doesMergeCells()) {
0112                     continue;
0113                 }
0114                 cell.mergeCells(col, row, 0, 0);
0115             }
0116         }
0117     }
0118 
0119     // adjust selection
0120     if (m_selection)
0121         m_selection->isEmpty() ? m_selection->initialize(range, m_sheet) : m_selection->extend(range, m_sheet);
0122 
0123     return true;
0124 }
0125 
0126 KUndo2MagicString MergeCommand::name() const
0127 {
0128     if (m_merge) { // MergeCommand
0129         if (m_mergeHorizontal) {
0130             return kundo2_i18n("Merge Cells Horizontally");
0131         } else if (m_mergeVertical) {
0132             return kundo2_i18n("Merge Cells Vertically");
0133         } else {
0134             return kundo2_i18n("Merge Cells");
0135         }
0136     }
0137     return kundo2_i18n("Dissociate Cells");
0138 }
0139 
0140 bool MergeCommand::preProcessing()
0141 {
0142     if (isColumnOrRowSelected()) {
0143         KMessageBox::information(0, i18n("Merging of columns or rows is not supported."));
0144         return false;
0145     }
0146 
0147     if (m_firstrun) {
0148         setText(name());
0149 
0150         // reduce the region to the region occupied by merged cells
0151         Region mergedCells;
0152         ConstIterator endOfList = constEnd();
0153         for (ConstIterator it = constBegin(); it != endOfList; ++it) {
0154             Element* element = *it;
0155             QRect range = element->rect();
0156             int right = range.right();
0157             int bottom = range.bottom();
0158             for (int row = range.top(); row <= bottom; ++row) {
0159                 for (int col = range.left(); col <= right; ++col) {
0160                     Cell cell = Cell(m_sheet, col, row);
0161                     if (cell.doesMergeCells()) {
0162                         QRect rect(col, row, cell.mergedXCells() + 1, cell.mergedYCells() + 1);
0163                         mergedCells.add(rect);
0164                     }
0165                 }
0166             }
0167         }
0168 
0169         if (m_merge) { // MergeCommand
0170             // we're in the manipulator's first execution
0171             // initialize the undo manipulator
0172             m_unmerger = new MergeCommand();
0173             if (!m_mergeHorizontal && !m_mergeVertical) {
0174                 m_unmerger->setReverse(true);
0175             }
0176             m_unmerger->setSheet(m_sheet);
0177             m_unmerger->setRegisterUndo(false);
0178             m_unmerger->add(mergedCells);
0179         } else { // DissociateManipulator
0180             clear();
0181             add(mergedCells);
0182         }
0183     }
0184 
0185     if (m_merge) { // MergeCommand
0186         if (m_reverse) { // dissociate
0187         } else { // merge
0188             // Dissociate cells before merging the whole region.
0189             // For horizontal/vertical merging the cells stay
0190             // as they are. E.g. the region contains a merged cell
0191             // occupying two rows. Then the horizontal merge should
0192             // keep the height of two rows and extend the merging to the
0193             // region's width. In this case the unmerging is done while
0194             // processing each region element.
0195             if (!m_mergeHorizontal && !m_mergeVertical) {
0196                 m_unmerger->redo();
0197             }
0198         }
0199     }
0200     // Clear the associated selection, if any. The merge/dissociate process will restore
0201     // selections. This ensures that the selection isn't broken after merging.
0202     if (m_selection) m_selection->Region::clear();
0203 
0204     return AbstractRegionCommand::preProcessing();
0205 }
0206 
0207 bool MergeCommand::postProcessing()
0208 {
0209     if (m_merge) { // MergeCommand
0210         if (m_reverse) { // dissociate
0211             // restore the old merge status
0212             if (m_mergeHorizontal || m_mergeVertical) {
0213                 m_unmerger->redo();
0214             } else {
0215                 m_unmerger->undo();
0216             }
0217         }
0218     }
0219     m_sheet->map()->addDamage(new CellDamage(m_sheet, *this, CellDamage::Appearance));
0220     return true;
0221 }