File indexing completed on 2024-05-12 16:35:12
0001 /* This file is part of the KDE project 0002 Copyright (C) 2005 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 #include <algorithm> 0021 0022 // Local 0023 #include "RowColumnManipulators.h" 0024 0025 #include <float.h> 0026 0027 #include <QFontMetricsF> 0028 #include <QWidget> 0029 #include <QPen> 0030 0031 #include <KLocalizedString> 0032 0033 #include "CellStorage.h" 0034 #include "Damages.h" 0035 #include "Map.h" 0036 #include "RowColumnFormat.h" 0037 #include "RowFormatStorage.h" 0038 #include "Sheet.h" 0039 #include "Value.h" 0040 0041 using namespace Calligra::Sheets; 0042 0043 /*************************************************************************** 0044 class ResizeColumnManipulator 0045 ****************************************************************************/ 0046 0047 ResizeColumnManipulator::ResizeColumnManipulator(KUndo2Command* parent) 0048 : AbstractRegionCommand(parent) 0049 { 0050 setText(kundo2_i18n("Resize Column")); 0051 } 0052 0053 ResizeColumnManipulator::~ResizeColumnManipulator() 0054 { 0055 } 0056 0057 bool ResizeColumnManipulator::process(Element* element) 0058 { 0059 QRect range = element->rect(); 0060 for (int col = range.right(); col >= range.left(); --col) { 0061 ColumnFormat *format = m_sheet->nonDefaultColumnFormat(col); 0062 if (m_firstrun) 0063 m_oldSizes[col] = format->width(); 0064 qreal delta = format->width(); 0065 format->setWidth(m_reverse ? m_oldSizes[col] : qMax(2.0, m_newSize)); 0066 delta = format->width() - delta; 0067 m_sheet->adjustCellAnchoredShapesX(delta, col+1); 0068 } 0069 // Just repaint everything visible; no need to invalidate the visual cache. 0070 m_sheet->map()->addDamage(new SheetDamage(m_sheet, SheetDamage::ContentChanged)); 0071 // TODO: only invalidate the cells that are actually effected by this resize (so everythin in this column, and everything that covers something in this column) 0072 m_sheet->map()->addDamage(new CellDamage(m_sheet, Region(1, 1, KS_colMax, KS_rowMax, m_sheet), CellDamage::Appearance)); 0073 return true; 0074 } 0075 0076 0077 0078 /*************************************************************************** 0079 class ResizeRowManipulator 0080 ****************************************************************************/ 0081 0082 ResizeRowManipulator::ResizeRowManipulator(KUndo2Command* parent) 0083 : AbstractRegionCommand(parent) 0084 { 0085 setText(kundo2_i18n("Resize Row")); 0086 } 0087 0088 ResizeRowManipulator::~ResizeRowManipulator() 0089 { 0090 } 0091 0092 bool ResizeRowManipulator::process(Element* element) 0093 { 0094 QRect range = element->rect(); 0095 // TODO: more efficiently store old sizes 0096 if (m_firstrun) { 0097 for (int row = range.bottom(); row >= range.top(); --row) { 0098 m_oldSizes[row] = m_sheet->rowFormats()->rowHeight(row); 0099 } 0100 } 0101 if (m_reverse) { 0102 for (int row = range.bottom(); row >= range.top(); --row) { 0103 m_sheet->rowFormats()->setRowHeight(row, row, m_oldSizes[row]); 0104 } 0105 } else { 0106 m_sheet->rowFormats()->setRowHeight(range.top(), range.bottom(), m_newSize); 0107 } 0108 // TODO: more efficiently update positions of cell-anchored shapes 0109 for (int row = range.top(); row <= range.bottom(); ++row) { 0110 qreal delta = m_newSize - m_oldSizes[row]; 0111 if (m_reverse) delta = -delta; 0112 m_sheet->adjustCellAnchoredShapesY(delta, row+1); 0113 } 0114 // Just repaint everything visible; no need to invalidate the visual cache. 0115 m_sheet->map()->addDamage(new SheetDamage(m_sheet, SheetDamage::ContentChanged)); 0116 // TODO: only invalidate the cells that are actually effected by this resize (so everythin in this row, and everything that covers something in this row) 0117 m_sheet->map()->addDamage(new CellDamage(m_sheet, Region(1, 1, KS_colMax, KS_rowMax, m_sheet), CellDamage::Appearance)); 0118 return true; 0119 } 0120 0121 0122 /*************************************************************************** 0123 class HideShowManipulator 0124 ****************************************************************************/ 0125 0126 HideShowManipulator::HideShowManipulator(KUndo2Command* parent) 0127 : AbstractRegionCommand(parent), 0128 m_manipulateColumns(false), 0129 m_manipulateRows(false) 0130 { 0131 } 0132 0133 HideShowManipulator::~HideShowManipulator() 0134 { 0135 } 0136 0137 bool HideShowManipulator::process(Element* element) 0138 { 0139 QRect range = element->rect(); 0140 if (m_manipulateColumns) { 0141 for (int col = range.left(); col <= range.right(); ++col) { 0142 ColumnFormat* format = m_sheet->nonDefaultColumnFormat(col); 0143 format->setHidden(!m_reverse); 0144 m_sheet->adjustCellAnchoredShapesX(m_reverse ? format->width() : -format->width(), col); 0145 } 0146 } 0147 if (m_manipulateRows) { 0148 m_sheet->rowFormats()->setHidden(range.top(), range.bottom(), !m_reverse); 0149 qreal delta = m_sheet->rowFormats()->totalRowHeight(range.top(), range.bottom()); 0150 if (!m_reverse) delta = -delta; 0151 m_sheet->adjustCellAnchoredShapesY(delta, range.top()); 0152 } 0153 return true; 0154 } 0155 0156 bool HideShowManipulator::preProcessing() 0157 { 0158 if (m_firstrun) 0159 setText(name()); 0160 Region region; 0161 ConstIterator endOfList = cells().constEnd(); 0162 for (ConstIterator it = cells().constBegin(); it != endOfList; ++it) { 0163 if (m_reverse) { 0164 QRect range = (*it)->rect(); 0165 if (m_manipulateColumns) { 0166 if (range.left() > 1) { 0167 int col; 0168 for (col = 1; col < range.left(); ++col) { 0169 if (!m_sheet->columnFormat(col)->isHidden()) 0170 break; 0171 } 0172 if (col == range.left()) { 0173 region.add(QRect(1, 1, range.left() - 1, KS_rowMax)); 0174 } 0175 } 0176 for (int col = range.left(); col <= range.right(); ++col) { 0177 if (m_sheet->columnFormat(col)->isHidden()) { 0178 region.add(QRect(col, 1, 1, KS_rowMax)); 0179 } 0180 } 0181 } 0182 if (m_manipulateRows) { 0183 if (range.top() > 1) { 0184 int row; 0185 for (row = 1; row < range.top(); ++row) { 0186 if (!m_sheet->rowFormats()->isHidden(row)) { 0187 break; 0188 } 0189 } 0190 if (row == range.top()) { 0191 region.add(QRect(1, 1, KS_colMax, range.top() - 1)); 0192 } 0193 } 0194 for (int row = range.top(); row <= range.bottom(); ++row) { 0195 if (m_sheet->rowFormats()->isHidden(row)) { 0196 region.add(QRect(1, row, KS_colMax, 1)); 0197 } 0198 } 0199 } 0200 } 0201 0202 if (((*it)->isRow() && m_manipulateColumns) || 0203 ((*it)->isColumn() && m_manipulateRows)) { 0204 /* KMessageBox::error( this, i18n( "Area is too large." ) );*/ 0205 return false; 0206 } 0207 } 0208 0209 if (m_reverse) { 0210 clear(); 0211 add(region); 0212 } 0213 0214 return AbstractRegionCommand::preProcessing(); 0215 } 0216 0217 bool HideShowManipulator::postProcessing() 0218 { 0219 // Just repaint everything visible; no need to invalidate the visual cache. 0220 m_sheet->map()->addDamage(new SheetDamage(m_sheet, SheetDamage::ContentChanged)); 0221 return true; 0222 } 0223 0224 KUndo2MagicString HideShowManipulator::name() const 0225 { 0226 if (m_reverse && m_manipulateColumns && m_manipulateRows) { 0227 return kundo2_i18n("Show Rows/Columns"); 0228 } else if (m_reverse && m_manipulateColumns) { 0229 return kundo2_i18n("Show Columns"); 0230 } else if (m_reverse && m_manipulateRows) { 0231 return kundo2_i18n("Show Rows"); 0232 } else if (!m_reverse && m_manipulateColumns && m_manipulateRows) { 0233 return kundo2_i18n("Hide Rows/Columns"); 0234 } else if (!m_reverse && m_manipulateColumns) { 0235 return kundo2_i18n("Hide Columns"); 0236 } else if (!m_reverse && m_manipulateRows) { 0237 return kundo2_i18n("Hide Rows"); 0238 } 0239 0240 return kundo2_noi18n("XXX: bug!"); 0241 } 0242 0243 /*************************************************************************** 0244 class AdjustColumnRowManipulator 0245 ****************************************************************************/ 0246 0247 AdjustColumnRowManipulator::AdjustColumnRowManipulator(KUndo2Command* parent) 0248 : AbstractRegionCommand(parent), 0249 m_adjustColumn(false), 0250 m_adjustRow(false) 0251 { 0252 } 0253 0254 AdjustColumnRowManipulator::~AdjustColumnRowManipulator() 0255 { 0256 } 0257 0258 bool AdjustColumnRowManipulator::process(Element* element) 0259 { 0260 Sheet* sheet = m_sheet; // TODO Stefan: element->sheet(); 0261 if (m_sheet && sheet != m_sheet) { 0262 return true; 0263 } 0264 0265 QMap<int, double> heights; 0266 QMap<int, double> widths; 0267 if (m_reverse) { 0268 heights = m_oldHeights; 0269 widths = m_oldWidths; 0270 } else { 0271 heights = m_newHeights; 0272 widths = m_newWidths; 0273 } 0274 0275 QRect range = element->rect(); 0276 if (m_adjustColumn) { 0277 if (element->isRow()) { 0278 for (int row = range.top(); row <= range.bottom(); ++row) { 0279 Cell cell = sheet->cellStorage()->firstInRow(row); 0280 while (!cell.isNull()) { 0281 int col = cell.column(); 0282 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0283 if (widths.contains(col) && widths[col] != -1.0) { 0284 ColumnFormat* format = sheet->nonDefaultColumnFormat(col); 0285 if (qAbs(format->width() - widths[col]) > DBL_EPSILON) { 0286 format->setWidth(qMax(2.0, widths[col])); 0287 } 0288 } 0289 } 0290 cell = sheet->cellStorage()->nextInRow(col, row); 0291 } 0292 } 0293 } else { 0294 for (int col = range.left(); col <= range.right(); ++col) { 0295 if (widths.contains(col) && widths[col] != -1.0) { 0296 ColumnFormat* format = sheet->nonDefaultColumnFormat(col); 0297 if (qAbs(format->width() - widths[col]) > DBL_EPSILON) { 0298 format->setWidth(qMax(2.0, widths[col])); 0299 } 0300 } 0301 } 0302 } 0303 } 0304 if (m_adjustRow) { 0305 if (element->isColumn()) { 0306 for (int col = range.left(); col <= range.right(); ++col) { 0307 Cell cell = sheet->cellStorage()->firstInColumn(col); 0308 while (!cell.isNull()) { 0309 int row = cell.row(); 0310 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0311 if (heights.contains(row) && heights[row] != -1.0) { 0312 sheet->rowFormats()->setRowHeight(row, row, heights[row]); 0313 } 0314 } 0315 cell = sheet->cellStorage()->nextInColumn(col, row); 0316 } 0317 } 0318 } else { 0319 for (int row = range.top(); row <= range.bottom(); ++row) { 0320 if (heights.contains(row) && heights[row] != -1.0) { 0321 sheet->rowFormats()->setRowHeight(row, row, heights[row]); 0322 } 0323 } 0324 } 0325 } 0326 // The cell width(s) or height(s) changed, which are cached: rebuild them. 0327 const Region region(m_adjustRow ? 1 : range.left(), 0328 m_adjustColumn ? 1 : range.top(), 0329 m_adjustRow ? KS_colMax : range.width(), 0330 m_adjustColumn ? KS_rowMax : range.height()); 0331 m_sheet->map()->addDamage(new CellDamage(m_sheet, region, CellDamage::Appearance)); 0332 return true; 0333 } 0334 0335 bool AdjustColumnRowManipulator::preProcessing() 0336 { 0337 if (m_firstrun) 0338 setText(name()); 0339 if (m_reverse) { 0340 } else { 0341 if (!m_newHeights.isEmpty() || !m_newWidths.isEmpty()) { 0342 return AbstractRegionCommand::preProcessing(); 0343 } 0344 // createUndo(); 0345 0346 ConstIterator endOfList(cells().constEnd()); 0347 for (ConstIterator it = cells().constBegin(); it != endOfList; ++it) { 0348 Element* element = *it; 0349 QRect range = element->rect(); 0350 if (element->isColumn()) { 0351 for (int col = range.left(); col <= range.right(); ++col) { 0352 Cell cell = m_sheet->cellStorage()->firstInColumn(col); 0353 while (!cell.isNull()) { 0354 int row = cell.row(); 0355 if (m_adjustColumn) { 0356 if (!m_newWidths.contains(col)) { 0357 m_newWidths[col] = -1.0; 0358 m_oldWidths[col] = m_sheet->columnFormat(col)->width(); 0359 } 0360 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0361 m_newWidths[col] = qMax(adjustColumnHelper(cell), 0362 m_newWidths[col]); 0363 } 0364 } 0365 if (m_adjustRow) { 0366 if (!m_newHeights.contains(row)) { 0367 m_newHeights[row] = -1.0; 0368 m_oldHeights[row] = m_sheet->rowFormats()->rowHeight(row); 0369 } 0370 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0371 m_newHeights[row] = qMax(adjustRowHelper(cell), 0372 m_newHeights[row]); 0373 } 0374 } 0375 cell = m_sheet->cellStorage()->nextInColumn(col, row); 0376 } 0377 } 0378 } else if (element->isRow()) { 0379 for (int row = range.top(); row <= range.bottom(); ++row) { 0380 Cell cell = m_sheet->cellStorage()->firstInRow(row); 0381 while (!cell.isNull()) { 0382 int col = cell.column(); 0383 if (m_adjustColumn) { 0384 if (!m_newWidths.contains(col)) { 0385 m_newWidths[col] = -1.0; 0386 m_oldWidths[col] = m_sheet->columnFormat(col)->width(); 0387 } 0388 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0389 m_newWidths[col] = qMax(adjustColumnHelper(cell), 0390 m_newWidths[col]); 0391 } 0392 } 0393 if (m_adjustRow) { 0394 if (!m_newHeights.contains(row)) { 0395 m_newHeights[row] = -1.0; 0396 m_oldHeights[row] = m_sheet->rowFormats()->rowHeight(row); 0397 } 0398 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0399 m_newHeights[row] = qMax(adjustRowHelper(cell), 0400 m_newHeights[row]); 0401 } 0402 } 0403 cell = m_sheet->cellStorage()->nextInRow(col, row); 0404 } 0405 } 0406 } else { 0407 Cell cell; 0408 for (int col = range.left(); col <= range.right(); ++col) { 0409 for (int row = range.top(); row <= range.bottom(); ++row) { 0410 cell = Cell(m_sheet, col, row); 0411 if (m_adjustColumn) { 0412 if (!m_newWidths.contains(col)) { 0413 m_newWidths[col] = -1.0; 0414 m_oldWidths[col] = m_sheet->columnFormat(col)->width(); 0415 } 0416 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0417 m_newWidths[col] = qMax(adjustColumnHelper(cell), 0418 m_newWidths[col]); 0419 } 0420 } 0421 if (m_adjustRow) { 0422 if (!m_newHeights.contains(row)) { 0423 m_newHeights[row] = -1.0; 0424 m_oldHeights[row] = m_sheet->rowFormats()->rowHeight(row); 0425 } 0426 if (!cell.isEmpty() && !cell.isPartOfMerged()) { 0427 m_newHeights[row] = qMax(adjustRowHelper(cell), 0428 m_newHeights[row]); 0429 } 0430 } 0431 } 0432 } 0433 } 0434 } 0435 } 0436 return AbstractRegionCommand::preProcessing(); 0437 } 0438 0439 bool AdjustColumnRowManipulator::postProcessing() 0440 { 0441 if (!m_adjustColumn && !m_adjustRow) { 0442 return false; 0443 } 0444 // Update the column/row header, if necessary. 0445 SheetDamage::Changes changes = SheetDamage::None; 0446 if (m_adjustColumn) { 0447 changes |= SheetDamage::ColumnsChanged; 0448 } 0449 if (m_adjustRow) { 0450 changes |= SheetDamage::RowsChanged; 0451 } 0452 m_sheet->map()->addDamage(new SheetDamage(m_sheet, changes)); 0453 return AbstractRegionCommand::postProcessing(); 0454 } 0455 0456 class DummyWidget : public QWidget 0457 { 0458 int metric(PaintDeviceMetric metric) const override { 0459 switch (metric) { 0460 case QPaintDevice::PdmDpiX: 0461 case QPaintDevice::PdmDpiY: 0462 case QPaintDevice::PdmPhysicalDpiX: 0463 case QPaintDevice::PdmPhysicalDpiY: 0464 return 72; 0465 default: 0466 break; 0467 } 0468 return QWidget::metric(metric); 0469 } 0470 }; 0471 0472 QSizeF AdjustColumnRowManipulator::textSize(const QString& text, const Style& style) const 0473 { 0474 QSizeF size; 0475 DummyWidget dummyWiget; 0476 const QFontMetricsF fontMetrics(style.font(), &dummyWiget); 0477 0478 // Set size to correct values according to 0479 // if the text is horizontal, vertical or rotated. 0480 if (!style.verticalText() && !style.angle()) { 0481 // Horizontal text. 0482 0483 size = fontMetrics.size(0, text); 0484 double offsetFont = 0.0; 0485 if ((style.valign() == Style::Bottom) && style.underline()) 0486 offsetFont = fontMetrics.underlinePos() + 1; 0487 0488 size.setHeight((fontMetrics.ascent() + fontMetrics.descent() + offsetFont) 0489 *(text.count('\n') + 1)); 0490 } else if (style.angle() != 0) { 0491 // Rotated text. 0492 0493 const double height = fontMetrics.ascent() + fontMetrics.descent(); 0494 const double width = fontMetrics.width(text); 0495 size.setHeight(height * ::cos(style.angle() * M_PI / 180) 0496 + qAbs(width * ::sin(style.angle() * M_PI / 180))); 0497 size.setWidth(qAbs(height * ::sin(style.angle() * M_PI / 180)) 0498 + width * ::cos(style.angle() * M_PI / 180)); 0499 } else { 0500 // Vertical text. 0501 0502 qreal width = 0.0; 0503 for (int i = 0; i < text.length(); i++) 0504 width = qMax(width, fontMetrics.width(text.at(i))); 0505 0506 size.setWidth(width); 0507 size.setHeight((fontMetrics.ascent() + fontMetrics.descent()) 0508 * text.length()); 0509 } 0510 return size; 0511 } 0512 0513 double AdjustColumnRowManipulator::adjustColumnHelper(const Cell& cell) 0514 { 0515 double long_max = 0.0; 0516 const Style style = cell.effectiveStyle(); 0517 const QSizeF size = textSize(cell.displayText(), style); 0518 if (size.width() > long_max) { 0519 double indent = 0.0; 0520 Style::HAlign alignment = style.halign(); 0521 if (alignment == Style::HAlignUndefined) { 0522 if (cell.value().isNumber() || cell.isDate() || cell.isTime()) 0523 alignment = Style::Right; 0524 else 0525 alignment = Style::Left; 0526 } 0527 if (alignment == Style::Left) 0528 indent = cell.style().indentation(); 0529 long_max = indent + size.width() 0530 + style.leftBorderPen().width() + style.rightBorderPen().width(); 0531 // if this cell has others merged into it, we'll subtract the width of those columns 0532 // this is not perfect, but at least should work in 90% of the cases 0533 const int mergedXCount = cell.mergedXCells(); 0534 if (mergedXCount > 0) { 0535 for (int col = 1; col <= mergedXCount; col++) { 0536 double cw = cell.sheet()->columnFormat(cell.column() + col)->width(); 0537 long_max -= cw; 0538 } 0539 } 0540 } 0541 // add 4 because long_max is the length of the text 0542 // but column has borders 0543 if (long_max == 0.0) 0544 return -1.0; 0545 else 0546 return long_max + 4.0; 0547 } 0548 0549 double AdjustColumnRowManipulator::adjustRowHelper(const Cell& cell) 0550 { 0551 double long_max = 0.0; 0552 const Style style = cell.effectiveStyle(); 0553 const QSizeF size = textSize(cell.displayText(), style); 0554 if (size.height() > long_max) 0555 long_max = size.height() + style.topBorderPen().width() + style.bottomBorderPen().width(); 0556 // add 1 because long_max is the height of the text 0557 // but row has borders 0558 if (long_max == 0.0) 0559 return -1.0; 0560 else 0561 return long_max + 1.0; 0562 } 0563 0564 KUndo2MagicString AdjustColumnRowManipulator::name() const 0565 { 0566 if (m_adjustColumn && m_adjustRow) { 0567 return kundo2_i18n("Adjust Columns/Rows"); 0568 } else if (m_adjustColumn) { 0569 return kundo2_i18n("Adjust Columns"); 0570 } else { 0571 return kundo2_i18n("Adjust Rows"); 0572 } 0573 } 0574 0575 /*************************************************************************** 0576 class InsertDeleteColumnManipulator 0577 ****************************************************************************/ 0578 0579 InsertDeleteColumnManipulator::InsertDeleteColumnManipulator(KUndo2Command *parent) 0580 : AbstractRegionCommand(parent) 0581 , m_mode(Insert) 0582 , m_template(0) 0583 { 0584 setText(kundo2_i18n("Insert Columns")); 0585 } 0586 0587 InsertDeleteColumnManipulator::~InsertDeleteColumnManipulator() 0588 { 0589 delete m_template; 0590 } 0591 0592 void InsertDeleteColumnManipulator::setTemplate(const ColumnFormat &columnFormat) 0593 { 0594 delete m_template; 0595 m_template = new ColumnFormat(columnFormat); 0596 } 0597 0598 void InsertDeleteColumnManipulator::setReverse(bool reverse) 0599 { 0600 m_reverse = reverse; 0601 m_mode = reverse ? Delete : Insert; 0602 if (!m_reverse) 0603 setText(kundo2_i18n("Insert Columns")); 0604 else 0605 setText(kundo2_i18n("Remove Columns")); 0606 } 0607 0608 bool InsertDeleteColumnManipulator::process(Element* element) 0609 { 0610 const QRect range = element->rect(); 0611 const int pos = range.left(); 0612 const int num = range.width(); 0613 if (!m_reverse) { // insertion 0614 // insert rows 0615 m_sheet->insertColumns(pos, num); 0616 if (m_template) { 0617 m_template->setSheet(m_sheet); 0618 const int end = pos + num - 1; 0619 for (int col = pos; col <= end; ++col) { 0620 m_template->setColumn(col); 0621 m_sheet->insertColumnFormat(m_template); 0622 } 0623 } 0624 m_sheet->cellStorage()->insertColumns(pos, num); 0625 0626 // undo deletion 0627 if (m_mode == Delete) { 0628 KUndo2Command::undo(); // process child commands (from CellStorage) 0629 } 0630 } else { 0631 // delete rows 0632 m_sheet->removeColumns(pos, num); 0633 m_sheet->cellStorage()->removeColumns(pos, num); 0634 0635 // undo insertion 0636 if (m_mode == Insert) { 0637 KUndo2Command::undo(); // process child commands (from CellStorage) 0638 } 0639 } 0640 return true; 0641 } 0642 0643 bool elementLeftColumnLessThan(const Calligra::Sheets::Region::Element *e1, const Calligra::Sheets::Region::Element *e2) 0644 { 0645 return e1->rect().left() < e2->rect().left(); 0646 } 0647 0648 bool InsertDeleteColumnManipulator::preProcessing() 0649 { 0650 if (m_firstrun) { 0651 // If we have an NCS, create a child command for each element. 0652 if (cells().count() > 1) { // non-contiguous selection 0653 // Sort the elements by their top row. 0654 std::stable_sort(cells().begin(), cells().end(), elementLeftColumnLessThan); 0655 // Create sub-commands. 0656 const Region::ConstIterator end(constEnd()); 0657 for (Region::ConstIterator it = constBegin(); it != end; ++it) { 0658 InsertDeleteColumnManipulator *const command = new InsertDeleteColumnManipulator(this); 0659 command->setSheet(m_sheet); 0660 command->add(Region((*it)->rect(), (*it)->sheet())); 0661 if (m_mode == Delete) { 0662 command->setReverse(true); 0663 } 0664 } 0665 } else { // contiguous selection 0666 m_sheet->cellStorage()->startUndoRecording(); 0667 } 0668 } 0669 return AbstractRegionCommand::preProcessing(); 0670 } 0671 0672 bool InsertDeleteColumnManipulator::mainProcessing() 0673 { 0674 if (cells().count() > 1) { // non-contiguous selection 0675 if ((m_reverse && m_mode == Insert) || (!m_reverse && m_mode == Delete)) { 0676 KUndo2Command::undo(); // process all sub-commands 0677 } else { 0678 KUndo2Command::redo(); // process all sub-commands 0679 } 0680 return true; 0681 } 0682 return AbstractRegionCommand::mainProcessing(); // calls process(Element*) 0683 } 0684 0685 bool InsertDeleteColumnManipulator::postProcessing() 0686 { 0687 if (cells().count() > 1) { // non-contiguous selection 0688 return true; 0689 } 0690 if (m_firstrun) { 0691 m_sheet->cellStorage()->stopUndoRecording(this); 0692 } 0693 const QRect rect(QPoint(boundingRect().left(), 1), QPoint(KS_colMax, KS_rowMax)); 0694 m_sheet->map()->addDamage(new CellDamage(m_sheet, Region(rect, m_sheet), CellDamage::Appearance)); 0695 return true; 0696 } 0697 0698 /*************************************************************************** 0699 class InsertDeleteRowManipulator 0700 ****************************************************************************/ 0701 0702 InsertDeleteRowManipulator::InsertDeleteRowManipulator(KUndo2Command *parent) 0703 : AbstractRegionCommand(parent) 0704 , m_mode(Insert) 0705 , m_template(0) 0706 { 0707 setText(kundo2_i18n("Insert Rows")); 0708 } 0709 0710 InsertDeleteRowManipulator::~InsertDeleteRowManipulator() 0711 { 0712 delete m_template; 0713 } 0714 0715 void InsertDeleteRowManipulator::setTemplate(const RowFormat &rowFormat) 0716 { 0717 delete m_template; 0718 m_template = new RowFormat(rowFormat); 0719 } 0720 0721 void InsertDeleteRowManipulator::setReverse(bool reverse) 0722 { 0723 m_reverse = reverse; 0724 m_mode = reverse ? Delete : Insert; 0725 if (!m_reverse) 0726 setText(kundo2_i18n("Insert Rows")); 0727 else 0728 setText(kundo2_i18n("Remove Rows")); 0729 } 0730 0731 bool InsertDeleteRowManipulator::process(Element* element) 0732 { 0733 const QRect range = element->rect(); 0734 const int pos = range.top(); 0735 const int num = range.height(); 0736 if (!m_reverse) { // insertion 0737 // insert rows 0738 m_sheet->insertRows(pos, num); 0739 if (m_template) { 0740 m_template->setSheet(m_sheet); 0741 const int end = pos + num - 1; 0742 for (int row = pos; row <= end; ++row) { 0743 m_template->setRow(row); 0744 m_sheet->insertRowFormat(m_template); 0745 } 0746 } 0747 m_sheet->cellStorage()->insertRows(pos, num); 0748 0749 // undo deletion 0750 if (m_mode == Delete) { 0751 KUndo2Command::undo(); // process child commands (from CellStorage) 0752 } 0753 } else { 0754 // delete rows 0755 m_sheet->removeRows(pos, num); 0756 m_sheet->cellStorage()->removeRows(pos, num); 0757 0758 // undo insertion 0759 if (m_mode == Insert) { 0760 KUndo2Command::undo(); // process child commands (from CellStorage) 0761 } 0762 } 0763 return true; 0764 } 0765 0766 bool elementTopRowLessThan(const Calligra::Sheets::Region::Element *e1, const Calligra::Sheets::Region::Element *e2) 0767 { 0768 return e1->rect().top() < e2->rect().top(); 0769 } 0770 0771 bool InsertDeleteRowManipulator::preProcessing() 0772 { 0773 if (m_firstrun) { 0774 // If we have an NCS, create a child command for each element. 0775 if (cells().count() > 1) { // non-contiguous selection 0776 // Sort the elements by their top row. 0777 std::stable_sort(cells().begin(), cells().end(), elementTopRowLessThan); 0778 // Create sub-commands. 0779 const Region::ConstIterator end(constEnd()); 0780 for (Region::ConstIterator it = constBegin(); it != end; ++it) { 0781 InsertDeleteRowManipulator *const command = new InsertDeleteRowManipulator(this); 0782 command->setSheet(m_sheet); 0783 command->add(Region((*it)->rect(), (*it)->sheet())); 0784 if (m_mode == Delete) { 0785 command->setReverse(true); 0786 } 0787 } 0788 } else { // contiguous selection 0789 m_sheet->cellStorage()->startUndoRecording(); 0790 } 0791 } 0792 return AbstractRegionCommand::preProcessing(); 0793 } 0794 0795 bool InsertDeleteRowManipulator::mainProcessing() 0796 { 0797 if (cells().count() > 1) { // non-contiguous selection 0798 if ((m_reverse && m_mode == Insert) || (!m_reverse && m_mode == Delete)) { 0799 KUndo2Command::undo(); // process all sub-commands 0800 } else { 0801 KUndo2Command::redo(); // process all sub-commands 0802 } 0803 return true; 0804 } 0805 return AbstractRegionCommand::mainProcessing(); // calls process(Element*) 0806 } 0807 0808 bool InsertDeleteRowManipulator::postProcessing() 0809 { 0810 if (cells().count() > 1) { // non-contiguous selection 0811 return true; 0812 } 0813 if (m_firstrun) { 0814 m_sheet->cellStorage()->stopUndoRecording(this); 0815 } 0816 const QRect rect(QPoint(1, boundingRect().top()), QPoint(KS_colMax, KS_rowMax)); 0817 m_sheet->map()->addDamage(new CellDamage(m_sheet, Region(rect, m_sheet), CellDamage::Appearance)); 0818 return true; 0819 }