File indexing completed on 2024-05-19 16:08:14
0001 /* This file is part of the KDE project 0002 Copyright (C) 2002-2003 Norbert Andres <nandres@web.de> 0003 (C) 2002 Philipp Mueller <philipp.mueller@gmx.de> 0004 (C) 2002 Laurent Montel <montel@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 // Local 0023 #include "SubtotalDialog.h" 0024 0025 #include "ui_SubtotalWidget.h" 0026 #include "ui_SubtotalsDetailsWidget.h" 0027 0028 // Qt 0029 #include <QCheckBox> 0030 #include <QListWidget> 0031 #include <QVector> 0032 0033 // KF5 0034 #include "SheetsDebug.h" 0035 #include <KLocalizedString> 0036 #include <kmessagebox.h> 0037 0038 // Sheets 0039 #include "ui/Selection.h" 0040 #include "Sheet.h" 0041 0042 // commands 0043 #include "commands/DataManipulators.h" 0044 0045 using namespace Calligra::Sheets; 0046 0047 class SubtotalDialog::Private 0048 { 0049 public: 0050 Selection *selection; 0051 Ui::SubtotalsWidget mainWidget; 0052 Ui::SubtotalsDetailsWidget detailsWidget; 0053 }; 0054 0055 SubtotalDialog::SubtotalDialog(QWidget* parent, Selection* selection) 0056 : KoDialog(parent) 0057 , d(new Private) 0058 { 0059 d->selection = selection; 0060 0061 setCaption(i18n("Subtotals")); 0062 setButtons(Ok | Cancel | Details | User1); 0063 setButtonGuiItem(User1, KGuiItem(i18n("Remove All"))); 0064 0065 QWidget* widget = new QWidget(this); 0066 d->mainWidget.setupUi(widget); 0067 setMainWidget(widget); 0068 0069 widget = new QWidget(this); 0070 d->detailsWidget.setupUi(widget); 0071 setDetailsWidget(widget); 0072 0073 fillColumnBoxes(); 0074 fillFunctionBox(); 0075 connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUser1())); 0076 } 0077 0078 SubtotalDialog::~SubtotalDialog() 0079 { 0080 delete d; 0081 } 0082 0083 void SubtotalDialog::accept() 0084 { 0085 Sheet *const sheet = d->selection->lastSheet(); 0086 QRect range = d->selection->lastRange(); 0087 0088 int numOfCols = range.width(); 0089 QVector<int> columns(numOfCols); 0090 0091 bool empty = true; 0092 int left = range.left(); 0093 for (int i = 0; i < d->mainWidget.m_columnList->count(); ++i) { 0094 QListWidgetItem* item = d->mainWidget.m_columnList->item(i); 0095 if (item->checkState() == Qt::Checked) { 0096 columns[i] = left + i; 0097 empty = false; 0098 } else 0099 columns[i] = -1; 0100 } 0101 0102 if (empty) { 0103 KMessageBox::sorry(this, i18n("You need to select at least one column for adding subtotals.")); 0104 return; 0105 } 0106 0107 if (d->detailsWidget.m_replaceSubtotals->isChecked()) 0108 removeSubtotalLines(); 0109 0110 range = d->selection->lastRange(); 0111 0112 int mainCol = left + d->mainWidget.m_columnBox->currentIndex(); 0113 int bottom = range.bottom(); 0114 int top = range.top(); 0115 int newBottom = bottom; 0116 left = range.left(); 0117 QString oldText = Cell(sheet, mainCol, top).displayText(); 0118 QString newText; 0119 QString result(' ' + i18n("Result")); 0120 int lastChangedRow = top; 0121 0122 bool ignoreEmptyCells = d->detailsWidget.m_IgnoreBox->isChecked(); 0123 bool addRow; 0124 if (!d->detailsWidget.m_summaryOnly->isChecked()) { 0125 int y = top + 1; 0126 debugSheets << "Starting in row" << y; 0127 while (y <= bottom) { 0128 addRow = true; 0129 newText = Cell(sheet, mainCol, y).displayText(); 0130 0131 if (ignoreEmptyCells && (newText.length() == 0)) { 0132 ++y; 0133 debugSheets << "Still the same ->" << y; 0134 continue; 0135 } 0136 0137 if (newText != oldText) { 0138 int saveY = y; 0139 for (int x = 0; x < numOfCols; ++x) { 0140 debugSheets << "Column:" << x << "," << columns[x]; 0141 if (columns[x] != -1) { 0142 if (!addSubtotal(mainCol, columns[x], y - 1, lastChangedRow, addRow, oldText + result)) 0143 reject(); 0144 0145 if (addRow) { 0146 ++saveY; 0147 ++bottom; 0148 } 0149 0150 addRow = false; 0151 } 0152 } 0153 y = saveY; 0154 lastChangedRow = y; 0155 } 0156 oldText = newText; 0157 ++y; 0158 } 0159 0160 addRow = true; 0161 for (int x = 0; x < numOfCols; ++x) { 0162 if (columns[x] != -1) { 0163 if (!addSubtotal(mainCol, columns[x], y - 1, lastChangedRow, addRow, oldText + result)) 0164 reject(); 0165 addRow = false; 0166 } 0167 } 0168 newBottom = y; 0169 } 0170 0171 if (d->detailsWidget.m_summaryBelow->isChecked()) { 0172 addRow = true; 0173 int bottom = newBottom; 0174 for (int x = 0; x < numOfCols; ++x) { 0175 if (columns[x] != -1) { 0176 addSubtotal(mainCol, columns[x], bottom, top, addRow, i18n("Grand Total")); 0177 addRow = false; 0178 } 0179 } 0180 } 0181 0182 KoDialog::accept(); 0183 } 0184 0185 void SubtotalDialog::reject() 0186 { 0187 KoDialog::reject(); 0188 } 0189 0190 void SubtotalDialog::slotUser1() 0191 { 0192 removeSubtotalLines(); 0193 KoDialog::accept(); 0194 } 0195 0196 void SubtotalDialog::removeSubtotalLines() 0197 { 0198 debugSheets << "Removing subtotal lines"; 0199 0200 Sheet *const sheet = d->selection->lastSheet(); 0201 QRect range = d->selection->lastRange(); 0202 0203 int r = range.right(); 0204 int l = range.left(); 0205 int t = range.top(); 0206 0207 Cell cell; 0208 QString text; 0209 0210 for (int y = range.bottom(); y >= t; --y) { 0211 debugSheets << "Checking row:" << y; 0212 bool containsSubtotal = false; 0213 for (int x = l; x <= r; ++x) { 0214 cell = Cell(sheet, x, y); 0215 if (!cell.isFormula()) 0216 continue; 0217 0218 text = cell.userInput(); 0219 if (text.indexOf("SUBTOTAL") != -1) { 0220 containsSubtotal = true; 0221 break; 0222 } 0223 } 0224 0225 if (containsSubtotal) { 0226 debugSheets << "Line" << y << " contains a subtotal"; 0227 QRect rect(l, y, range.width(), 1); 0228 0229 ShiftManipulator* manipulator = new ShiftManipulator(); 0230 manipulator->setSheet(sheet); 0231 manipulator->setDirection(ShiftManipulator::ShiftBottom); 0232 manipulator->setReverse(true); 0233 manipulator->add(Region(rect)); 0234 manipulator->execute(d->selection->canvas()); 0235 range.setHeight(range.height() - 1); 0236 } 0237 } 0238 0239 d->selection->initialize(range, sheet); 0240 debugSheets << "Done removing subtotals"; 0241 } 0242 0243 void SubtotalDialog::fillColumnBoxes() 0244 { 0245 Sheet *const sheet = d->selection->lastSheet(); 0246 const QRect range = d->selection->lastRange(); 0247 0248 int r = range.right(); 0249 int row = range.top(); 0250 0251 Cell cell; 0252 QListWidgetItem * item; 0253 0254 QString text; 0255 0256 int index = 0; 0257 for (int i = range.left(); i <= r; ++i) { 0258 cell = Cell(sheet, i, row); 0259 text = cell.displayText(); 0260 0261 //if ( text.length() > 0 ) 0262 { 0263 text = i18n("Column '%1' ", Cell::columnName(i)); 0264 } 0265 0266 d->mainWidget.m_columnBox->insertItem(index++, text); 0267 0268 item = new QListWidgetItem(text); 0269 item->setFlags(item->flags() | Qt::ItemIsUserCheckable); 0270 item->setCheckState(Qt::Unchecked); 0271 d->mainWidget.m_columnList->addItem(item); 0272 } 0273 } 0274 0275 void SubtotalDialog::fillFunctionBox() 0276 { 0277 QStringList lst; 0278 lst << i18n("Average"); 0279 lst << i18n("Count"); 0280 lst << i18n("CountA"); 0281 lst << i18n("Max"); 0282 lst << i18n("Min"); 0283 lst << i18n("Product"); 0284 lst << i18n("StDev"); 0285 lst << i18n("StDevP"); 0286 lst << i18n("Sum"); 0287 lst << i18n("Var"); 0288 lst << i18n("VarP"); 0289 d->mainWidget.m_functionBox->insertItems(0, lst); 0290 } 0291 0292 bool SubtotalDialog::addSubtotal(int mainCol, int column, int row, int topRow, 0293 bool addRow, QString const & text) 0294 { 0295 Sheet *const sheet = d->selection->lastSheet(); 0296 QRect range = d->selection->lastRange(); 0297 0298 debugSheets << "Adding subtotal:" << mainCol << "," << column << ", Rows:" << row << "," << topRow 0299 << ": addRow: " << addRow << ", Text: " << text << endl; 0300 if (addRow) { 0301 QRect rect(range.left(), row + 1, range.width(), 1); 0302 ShiftManipulator* manipulator = new ShiftManipulator(); 0303 manipulator->setSheet(sheet); 0304 manipulator->setDirection(ShiftManipulator::ShiftBottom); 0305 manipulator->add(Region(rect)); 0306 manipulator->execute(d->selection->canvas()); 0307 0308 range.setHeight(range.height() + 1); 0309 0310 Cell cell = Cell(sheet, mainCol, row + 1); 0311 cell.parseUserInput(text); 0312 Style style; 0313 style.setFontBold(true); 0314 style.setFontItalic(true); 0315 style.setFontUnderline(true); 0316 cell.setStyle(style); 0317 } 0318 0319 QString colName = Cell::columnName(column); 0320 0321 QString formula("=SUBTOTAL("); 0322 formula += QString::number(d->mainWidget.m_functionBox->currentIndex() + 1); 0323 formula += "; "; 0324 formula += colName; 0325 formula += QString::number(topRow); 0326 // if ( topRow != row ) 0327 { 0328 formula += ':'; 0329 formula += colName; 0330 formula += QString::number(row); 0331 } 0332 formula += ')'; 0333 0334 Cell cell = Cell(sheet, column, row + 1); 0335 cell.parseUserInput(formula); 0336 Style style; 0337 style.setFontBold(true); 0338 style.setFontItalic(true); 0339 style.setFontUnderline(true); 0340 cell.setStyle(style); 0341 0342 d->selection->initialize(range, sheet); 0343 return true; 0344 }