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 }