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

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006 Robert Knight <robertknight@gmail.com>
0003              (C) 2006 Tomas Mecir <mecirt@gmail.com>
0004              (C) 2002-2003 Norbert Andres <nandres@web.de>
0005              (C) 2002 Ariya Hidayat <ariya@kde.org>
0006              (C) 2002 John Dailey <dailey@vt.edu>
0007              (C) 2002 Werner Trobin <trobin@kde.org>
0008              (C) 2001-2002 Philipp Mueller <philipp.mueller@gmx.de>
0009              (C) 1999-2002 Laurent Montel <montel@kde.org>
0010              (C) 2000 David Faure <faure@kde.org>
0011              (C) 1998-2000 Torben Weis <weis@kde.org>
0012 
0013    This library is free software; you can redistribute it and/or
0014    modify it under the terms of the GNU Library General Public
0015    License as published by the Free Software Foundation; either
0016    version 2 of the License, or (at your option) any later version.
0017 
0018    This library is distributed in the hope that it will be useful,
0019    but WITHOUT ANY WARRANTY; without even the implied warranty of
0020    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0021    Library General Public License for more details.
0022 
0023    You should have received a copy of the GNU Library General Public License
0024    along with this library; see the file COPYING.LIB.  If not, write to
0025    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0026    Boston, MA 02110-1301, USA.
0027 */
0028 
0029 // Local
0030 #include "SortDialog.h"
0031 
0032 // Sheets
0033 #include "Map.h"
0034 #include "ui/Selection.h"
0035 #include "Sheet.h"
0036 #include "ValueConverter.h"
0037 
0038 // commands
0039 #include "commands/SortManipulator.h"
0040 
0041 #include <KoIcon.h>
0042 
0043 // ui
0044 #include "ui_SortWidget.h"
0045 #include "ui_SortDetailsWidget.h"
0046 
0047 #include <KSharedConfig>
0048 
0049 // Qt
0050 #include <QStyledItemDelegate>
0051 
0052 #include <algorithm>
0053 
0054 using namespace Calligra::Sheets;
0055 
0056 Q_DECLARE_METATYPE(Qt::CaseSensitivity)
0057 Q_DECLARE_METATYPE(Qt::SortOrder)
0058 
0059 class SortDialog::Private : public QStyledItemDelegate
0060 {
0061 public:
0062     Private(SortDialog *parent = 0)
0063             : QStyledItemDelegate(parent)
0064     {
0065     }
0066 
0067     QWidget *createEditor(QWidget *parent,
0068                                   const QStyleOptionViewItem &option,
0069                                   const QModelIndex &index) const override
0070     {
0071         Q_UNUSED(index)
0072         Q_UNUSED(option)
0073         if (mainWidget.m_sortHorizontal->isChecked()) /* data grouped in columns; criteria/header per row */ {
0074             if (rows.isEmpty()) {
0075                 return 0;
0076             }
0077         } else if (columns.isEmpty()) {
0078             return 0;
0079         }
0080         KComboBox *const combo = new KComboBox(parent);
0081         return combo;
0082     }
0083 
0084     void setEditorData(QWidget *editor, const QModelIndex &index) const override
0085     {
0086         if (!index.isValid()) {
0087             return;
0088         }
0089         KComboBox *const combo = static_cast<KComboBox*>(editor);
0090         const QAbstractItemModel *const model = index.model();
0091         const QString itemText = model->data(index, Qt::DisplayRole).toString();
0092         const int itemIndex = model->data(index, Qt::UserRole).toInt();
0093         const bool hasHeader = mainWidget.m_useHeader->isChecked();
0094         Sheet *const sheet = selection->lastSheet();
0095         ValueConverter *const converter = sheet->map()->converter();
0096 
0097         if (mainWidget.m_sortVertical->isChecked()) /* data grouped in rows; criteria/header per column */ {
0098             // Put the old item back into the map of available items.
0099             insertIndex(itemIndex, Qt::Horizontal);
0100 
0101             const int row = selection->lastRange().top();
0102             const QList<int> indices = columns;
0103             for (int i = 0; i < indices.count(); ++i) {
0104                 const int col = indices[i];
0105                 const QString columnName = i18n("Column %1", Cell::columnName(col));
0106                 const Value value = Cell(sheet, col, row).value();
0107                 const QString header = converter->asString(value).asString();
0108                 if (hasHeader) {
0109                     if (header.isEmpty()) {
0110                         combo->addItem('(' + columnName + ')', col);
0111                     } else {
0112                         combo->addItem(header, col);
0113                         combo->setItemData(combo->count() - 1, columnName, Qt::ToolTipRole);
0114                     }
0115                 } else {
0116                     combo->addItem(columnName, col);
0117                 }
0118                 if (col == itemIndex) {
0119                     combo->setCurrentIndex(i);
0120                 }
0121             }
0122         } else /* row headers */ {
0123             // Put the old item back into the map of available items.
0124             insertIndex(itemIndex, Qt::Vertical);
0125 
0126             const int col = selection->lastRange().left();
0127             const QList<int> indices = rows;
0128             for (int i = 0; i < indices.count(); ++i) {
0129                 const int row = indices[i];
0130                 const QString rowName = i18n("Row %1", row);
0131                 const Value value = Cell(sheet, col, row).value();
0132                 const QString header = converter->asString(value).asString();
0133                 if (hasHeader) {
0134                     if (header.isEmpty()) {
0135                         combo->addItem('(' + rowName + ')', row);
0136                     } else {
0137                         combo->addItem(header, row);
0138                         combo->setItemData(combo->count() - 1, rowName, Qt::ToolTipRole);
0139                     }
0140                 } else {
0141                     combo->addItem(rowName, row);
0142                 }
0143                 if (row == itemIndex) {
0144                     combo->setCurrentIndex(i);
0145                 }
0146             }
0147         }
0148     }
0149 
0150     void setModelData(QWidget *editor, QAbstractItemModel *model,
0151                               const QModelIndex &index) const override
0152     {
0153         KComboBox *const combo = static_cast<KComboBox*>(editor);
0154         const int currentIndex = combo->currentIndex();
0155         model->setData(index, combo->itemText(currentIndex), Qt::DisplayRole);
0156         model->setData(index, combo->itemData(currentIndex), Qt::UserRole);
0157 
0158         // Remove the current item from the map of available items.
0159         if (mainWidget.m_sortHorizontal->isChecked()) /* data grouped in columns; criteria/header per row */ {
0160             rows.removeAll(combo->itemData(currentIndex).toInt());
0161         } else {
0162             columns.removeAll(combo->itemData(currentIndex).toInt());
0163         }
0164     }
0165 
0166     void updateEditorGeometry(QWidget *editor,
0167                                       const QStyleOptionViewItem &option,
0168                                       const QModelIndex &index) const override
0169     {
0170         Q_UNUSED(index)
0171         editor->setGeometry(option.rect);
0172     }
0173 
0174 public: // data
0175     Selection *selection;
0176     Ui::SortWidget mainWidget;
0177     Ui::SortDetailsWidget detailsWidget;
0178     mutable QList<int> columns;
0179     mutable QList<int> rows;
0180 
0181 public:
0182     /// \return \c true if all columns/rows have text values
0183     bool hasHeader(const Region &region, Qt::Orientation orientation) const;
0184     void createAvailableIndices(const Region &region, Qt::Orientation orientation);
0185     void insertIndex(int index, Qt::Orientation orientation) const;
0186     QString itemText(int index, bool useHeader) const;
0187     void initCriteria(Qt::Orientation orientation, SortDialog *parent);
0188 };
0189 
0190 bool SortDialog::Private::hasHeader(const Region &region, Qt::Orientation orientation) const
0191 {
0192     Sheet *const sheet = region.lastSheet();
0193     const QRect range = region.lastRange();
0194     if (orientation == Qt::Horizontal) /* check for column headers */ {
0195         for (int col = range.left(); col <= range.right(); ++col) {
0196             if (!Cell(sheet, col, range.top()).value().isString())  {
0197                 return false;
0198             }
0199         }
0200     } else /* check for row headers */ {
0201         for (int row = range.top(); row <= range.bottom(); ++row) {
0202             if (!Cell(sheet, range.left(), row).value().isString()) {
0203                 return false;
0204             }
0205         }
0206     }
0207     return true;
0208 }
0209 
0210 void SortDialog::Private::createAvailableIndices(const Region &region, Qt::Orientation orientation)
0211 {
0212     const QRect range = region.lastRange();
0213     if (orientation == Qt::Horizontal) /* available columns */ {
0214         for (int col = range.left(); col <= range.right(); ++col) {
0215             columns.append(col);
0216         }
0217     } else /* available rows */ {
0218         for (int row = range.top(); row <= range.bottom(); ++row) {
0219             rows.append(row);
0220         }
0221     }
0222 }
0223 
0224 void SortDialog::Private::insertIndex(int index, Qt::Orientation orientation) const
0225 {
0226     if (orientation == Qt::Vertical) /* data grouped in columns; criteria/header per row */ {
0227         Q_ASSERT(1 <= index && index <= KS_colMax);
0228         QList<int>::Iterator it = std::lower_bound(rows.begin(), rows.end(), index);
0229         if (*it == index) {
0230             return;
0231         }
0232         rows.insert(it, index);
0233     } else /* data grouped in rows; criteria/header per column */ {
0234         Q_ASSERT(1 <= index && index <= KS_rowMax);
0235         QList<int>::Iterator it = std::lower_bound(columns.begin(), columns.end(), index);
0236         if (*it == index) {
0237             return;
0238         }
0239         columns.insert(it, index);
0240     }
0241 }
0242 
0243 QString SortDialog::Private::itemText(int index, bool useHeader) const
0244 {
0245     Sheet *const sheet = selection->lastSheet();
0246     ValueConverter *const converter = sheet->map()->converter();
0247 
0248     if (mainWidget.m_sortHorizontal->isChecked()) /* data grouped in columns; criteria/header per row */ {
0249         const int col = selection->lastRange().left();
0250         const int row = index;
0251         const QString rowName = i18n("Row %1", row);
0252         if (useHeader) {
0253             const Value value = Cell(sheet, col, row).value();
0254             const QString header = converter->asString(value).asString();
0255             if (header.isEmpty()) {
0256                 return QString('(' + rowName + ')');
0257             } else {
0258                 return header;
0259             }
0260         } else {
0261             return rowName;
0262         }
0263     } else /* data grouped in rows; criteria/header per column */ {
0264         const int col = index;
0265         const int row = selection->lastRange().top();
0266         const QString columnName = i18n("Column %1", Cell::columnName(col));
0267         if (useHeader) {
0268             const Value value = Cell(sheet, col, row).value();
0269             const QString header = converter->asString(value).asString();
0270             if (header.isEmpty()) {
0271                 return QString('(' + columnName + ')');
0272             } else {
0273                 return header;
0274             }
0275         } else {
0276             return columnName;
0277         }
0278     }
0279 }
0280 
0281 void SortDialog::Private::initCriteria(Qt::Orientation orientation, SortDialog *parent)
0282 {
0283     // Put the items back into the map of available items.
0284     for (int row = mainWidget.m_tableWidget->rowCount() - 1; row >= 0; --row) {
0285         QTableWidgetItem *const item = mainWidget.m_tableWidget->item(row, 0);
0286         const int index = item->data(Qt::UserRole).toInt();
0287         insertIndex(index, orientation);
0288         mainWidget.m_tableWidget->removeRow(row);
0289     }
0290 
0291     // (Re-)Insert the criteria.
0292     if (mainWidget.m_sortHorizontal->isChecked()) {
0293         while (rows.count()) {
0294             parent->addCriterion();
0295         }
0296     } else {
0297         while (columns.count()) {
0298             parent->addCriterion();
0299         }
0300     }
0301 
0302     // Setup the buttons.
0303     mainWidget.m_addButton->setEnabled(false);
0304     mainWidget.m_removeButton->setEnabled(false);
0305     mainWidget.m_upButton->setEnabled(false);
0306     mainWidget.m_downButton->setEnabled(false);
0307 
0308     // Adjust the header usage text.
0309     if (mainWidget.m_sortHorizontal->isChecked()) /* Sort horizontally */  {
0310         // data gets sorted horizontally; comparisons per row; columns get exchanged/sorted
0311         mainWidget.m_useHeader->setText(i18n("&First column contains row headers"));
0312     } else /* Sort vertically */ {
0313         // data gets sorted vertically; comparisons per column; rows get exchanged/sorted
0314         mainWidget.m_useHeader->setText(i18n("&First row contains column headers"));
0315     }
0316 }
0317 
0318 
0319 SortDialog::SortDialog(QWidget* parent, Selection* selection)
0320         : KoDialog(parent)
0321         , d(new Private(this))
0322 {
0323     d->selection = selection;
0324 
0325     setCaption(i18n("Sort"));
0326     setButtons(Ok | Cancel | Details | Reset);
0327     setObjectName(QLatin1String("SortDialog"));
0328 
0329     QWidget *widget = new QWidget(this);
0330     d->mainWidget.setupUi(widget);
0331     setMainWidget(widget);
0332 
0333     widget = new QWidget(this);
0334     d->detailsWidget.setupUi(widget);
0335     setDetailsWidget(widget);
0336 
0337     // UI refinements Designer is not capable of
0338     d->mainWidget.m_addButton->setIcon(koIcon("list-add"));
0339     d->mainWidget.m_removeButton->setIcon(koIcon("list-remove"));
0340     d->mainWidget.m_upButton->setIcon(koIcon("go-up"));
0341     d->mainWidget.m_downButton->setIcon(koIcon("go-down"));
0342     QHeaderView *const header = d->mainWidget.m_tableWidget->horizontalHeader();
0343     header->setSectionResizeMode(QHeaderView::ResizeToContents);
0344     header->setSectionResizeMode(0, QHeaderView::Stretch);
0345     d->mainWidget.m_tableWidget->setItemDelegateForColumn(0, d);
0346 
0347     connect(d->mainWidget.m_useHeader, SIGNAL(toggled(bool)),
0348             this, SLOT(useHeaderChanged(bool)));
0349     connect(d->mainWidget.m_sortHorizontal, SIGNAL(toggled(bool)),
0350             this, SLOT(orientationChanged(bool)));
0351 
0352     connect(d->mainWidget.m_tableWidget, SIGNAL(itemActivated(QTableWidgetItem*)),
0353             this, SLOT(itemActivated(QTableWidgetItem*)));
0354     connect(d->mainWidget.m_tableWidget, SIGNAL(itemSelectionChanged()),
0355             this, SLOT(itemSelectionChanged()));
0356 
0357     connect(d->mainWidget.m_addButton, SIGNAL(clicked()),
0358             this, SLOT(addCriterion()));
0359     connect(d->mainWidget.m_removeButton, SIGNAL(clicked()),
0360             this, SLOT(removeCriterion()));
0361     connect(d->mainWidget.m_upButton, SIGNAL(clicked()),
0362             this, SLOT(moveCriterionUp()));
0363     connect(d->mainWidget.m_downButton, SIGNAL(clicked()),
0364             this, SLOT(moveCriterionDown()));
0365 
0366     init();
0367 }
0368 
0369 SortDialog::~SortDialog()
0370 {
0371     delete d;
0372 }
0373 
0374 void SortDialog::init()
0375 {
0376     QStringList lst;
0377     lst << i18n("January") + ',' + i18n("February") + ',' + i18n("March") +
0378     ',' + i18n("April") + ',' + i18n("May") + ',' + i18n("June") +
0379     ',' + i18n("July") + ',' + i18n("August") + ',' + i18n("September") +
0380     ',' + i18n("October") + ',' + i18n("November") +
0381     ',' + i18n("December");
0382 
0383     lst << i18n("Monday") + ',' + i18n("Tuesday") + ',' + i18n("Wednesday") +
0384     ',' + i18n("Thursday") + ',' + i18n("Friday") + ',' + i18n("Saturday") +
0385     ',' + i18n("Sunday");
0386 
0387     KSharedConfigPtr config = KSharedConfig::openConfig();
0388     const QStringList other = config->group("Parameters").readEntry("Other list", QStringList());
0389     QString tmp;
0390     for (QStringList::ConstIterator it = other.begin(); it != other.end(); ++it) {
0391         if ((*it) != "\\")
0392             tmp += (*it) + ", ";
0393         else if (it != other.begin()) {
0394             tmp = tmp.left(tmp.length() - 2);
0395             lst.append(tmp);
0396             tmp.clear();
0397         }
0398     }
0399     d->detailsWidget.m_customList->insertItems(0, lst);
0400 
0401     Sheet *const sheet = d->selection->lastSheet();
0402     const QRect range = d->selection->lastRange();
0403     const Region region(range, sheet);
0404 
0405     if (region.isColumnSelected()) /* entire columns */ {
0406         d->mainWidget.m_sortHorizontal->setEnabled(false);
0407         d->mainWidget.m_sortVertical->setChecked(true);
0408 
0409         const bool hasHeader = d->hasHeader(region, Qt::Horizontal);
0410         d->mainWidget.m_useHeader->setChecked(hasHeader);
0411         d->createAvailableIndices(region, Qt::Horizontal);
0412     }
0413     else if (region.isRowSelected()) /* entire rows */ {
0414         d->mainWidget.m_sortVertical->setEnabled(false);
0415         d->mainWidget.m_sortHorizontal->setChecked(true);
0416 
0417         const bool hasHeader = d->hasHeader(region, Qt::Vertical);
0418         d->mainWidget.m_useHeader->setChecked(hasHeader);
0419         d->createAvailableIndices(region, Qt::Vertical);
0420     } else /* ordinary cell range */ {
0421         if (range.top() == range.bottom()) /* only one row */{
0422             d->mainWidget.m_sortVertical->setEnabled(false);
0423             d->mainWidget.m_sortHorizontal->setChecked(true);
0424         } else if (range.left() == range.right()) /* only one column */ {
0425             d->mainWidget.m_sortHorizontal->setEnabled(false);
0426             d->mainWidget.m_sortVertical->setChecked(true);
0427         } else {
0428             const bool hasColumnHeader = d->hasHeader(region, Qt::Horizontal);
0429             const bool hasRowHeader = d->hasHeader(region, Qt::Vertical);
0430 
0431 #if 0 // TODO
0432             if (hasColumnHeader && range.top() + 1 == range.bottom()) /* only one data row */ {
0433                 d->mainWidget.m_sortVertical->setEnabled(false);
0434             }
0435             if (hasRowHeader && range.left() + 1 == range.right()) /* only one data column */ {
0436                 d->mainWidget.m_sortHorizontal->setEnabled(false);
0437             }
0438 #endif
0439 
0440             if (range.width() >= range.height()) {
0441                 d->mainWidget.m_sortHorizontal->setChecked(true);
0442                 d->mainWidget.m_useHeader->setChecked(hasRowHeader);
0443             } else {
0444                 d->mainWidget.m_sortVertical->setChecked(true);
0445                 d->mainWidget.m_useHeader->setChecked(hasColumnHeader);
0446             }
0447         }
0448 
0449         // create column indices, if data can be sorted vertically
0450         if (d->mainWidget.m_sortVertical->isEnabled()) {
0451             d->createAvailableIndices(region, Qt::Horizontal);
0452         }
0453         // create row indices, if data can be sorted horizontally
0454         if (d->mainWidget.m_sortHorizontal->isEnabled()) {
0455             d->createAvailableIndices(region, Qt::Vertical);
0456         }
0457     }
0458 
0459     // Initialize the criteria.
0460     slotButtonClicked(Reset);
0461 }
0462 
0463 void SortDialog::orientationChanged(bool horizontal)
0464 {
0465     // Take the old, i.e. the reverse orientation.
0466     const Qt::Orientation orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
0467     d->initCriteria(orientation, this);
0468 }
0469 
0470 void SortDialog::accept()
0471 {
0472     Sheet *const sheet = d->selection->activeSheet();
0473 
0474     SortManipulator *const command = new SortManipulator();
0475     command->setSheet(sheet);
0476 
0477     // set parameters
0478     command->setSortRows(d->mainWidget.m_sortVertical->isChecked());
0479     command->setSkipFirst(d->mainWidget.m_useHeader->isChecked());
0480     command->setCopyFormat(d->detailsWidget.m_copyLayout->isChecked());
0481 
0482     const bool horizontal = d->mainWidget.m_sortHorizontal->isChecked();
0483     const QRect range = d->selection->lastRange();
0484     const int offset = horizontal ? range.top() : range.left();
0485 
0486     // retrieve sorting order
0487     QTableWidget *const table = d->mainWidget.m_tableWidget;
0488     for (int i = 0; i < table->rowCount(); ++i) {
0489         const int index = table->item(i, 0)->data(Qt::UserRole).toInt();
0490         const Qt::SortOrder order = table->item(i, 1)->data(Qt::UserRole).value<Qt::SortOrder>();
0491         const Qt::CaseSensitivity caseSensitivity = table->item(i, 2)->data(Qt::UserRole).value<Qt::CaseSensitivity>();
0492         command->addCriterion(index - offset, order, caseSensitivity);
0493     }
0494 
0495     if (d->detailsWidget.m_useCustomLists->isChecked()) {
0496         // add custom list if any
0497         QStringList clist;
0498         QString list = d->detailsWidget.m_customList->currentText();
0499         QString tmp;
0500         int l = list.length();
0501         for (int i = 0; i < l; ++i) {
0502             if (list[i] == ',') {
0503                 clist.append(tmp.trimmed());
0504                 tmp.clear();
0505             } else
0506                 tmp += list[i];
0507         }
0508 
0509         command->setUseCustomList(true);
0510         command->setCustomList(clist);
0511     }
0512     command->add(d->selection->lastRange());
0513     command->execute(d->selection->canvas());
0514 
0515     d->selection->emitModified();
0516     KoDialog::accept();
0517 }
0518 
0519 void SortDialog::slotButtonClicked(int button)
0520 {
0521     if (button == Reset) {
0522         const bool horizontal = d->mainWidget.m_sortHorizontal->isChecked();
0523         const Qt::Orientation orientation = horizontal ? Qt::Vertical : Qt::Horizontal;
0524         d->initCriteria(orientation, this);
0525     }
0526     KoDialog::slotButtonClicked(button);
0527 }
0528 
0529 void SortDialog::useHeaderChanged(bool enable)
0530 {
0531     // Rename the list items.
0532     QTableWidget *const table = d->mainWidget.m_tableWidget;
0533     for (int row = 0; row < table->rowCount(); ++row) {
0534         QTableWidgetItem *const item = table->item(row, 0);
0535         const int index = item->data(Qt::UserRole).toInt();
0536         item->setText(d->itemText(index, enable));
0537     }
0538 }
0539 
0540 void SortDialog::itemActivated(QTableWidgetItem *item)
0541 {
0542     if (item->column() == 1) /* Sort Order */ {
0543         if (item->data(Qt::UserRole).value<Qt::SortOrder>() == Qt::AscendingOrder) {
0544             item->setIcon(koIcon("view-sort-descending"));
0545             item->setText(i18n("Descending"));
0546             item->setData(Qt::UserRole, QVariant::fromValue(Qt::DescendingOrder));
0547         } else {
0548             item->setIcon(koIcon("view-sort-ascending"));
0549             item->setText(i18n("Ascending"));
0550             item->setData(Qt::UserRole, QVariant::fromValue(Qt::AscendingOrder));
0551         }
0552     } else if (item->column() == 2) /* Case Sensitivity */ {
0553         if (item->checkState() == Qt::Checked) {
0554             item->setCheckState(Qt::Unchecked);
0555             item->setText(i18n("Case Insensitive"));
0556             item->setData(Qt::UserRole, QVariant::fromValue(Qt::CaseInsensitive));
0557         } else {
0558             item->setCheckState(Qt::Checked);
0559             item->setText(i18n("Case Sensitive"));
0560             item->setData(Qt::UserRole, QVariant::fromValue(Qt::CaseSensitive));
0561         }
0562     }
0563 }
0564 
0565 void SortDialog::itemSelectionChanged()
0566 {
0567     QTableWidget *const table = d->mainWidget.m_tableWidget;
0568     QList<QTableWidgetSelectionRange> ranges = table->selectedRanges();
0569     if (ranges.count() == 0) {
0570         d->mainWidget.m_removeButton->setEnabled(false);
0571         d->mainWidget.m_upButton->setEnabled(false);
0572         d->mainWidget.m_downButton->setEnabled(false);
0573     } else {
0574         d->mainWidget.m_removeButton->setEnabled(true);
0575         bool first = false;
0576         bool last = false;
0577         for (int i = 0; i < ranges.count(); ++i) {
0578             if (ranges[i].topRow() == 0) {
0579                 first = true;
0580             }
0581             if (ranges[i].bottomRow() == table->rowCount() - 1) {
0582                 last = true;
0583             }
0584             if (first && last) {
0585                 break;
0586             }
0587         }
0588         d->mainWidget.m_upButton->setEnabled(!first);
0589         d->mainWidget.m_downButton->setEnabled(!last);
0590     }
0591 }
0592 
0593 void SortDialog::addCriterion()
0594 {
0595     QTableWidgetItem *item;
0596     const bool useHeader = d->mainWidget.m_useHeader->isChecked();
0597     // Take the first item from the map of available items.
0598     if (d->mainWidget.m_sortVertical->isChecked()) /* data grouped in rows; criteria/header per column */ {
0599         const QList<int> keys = d->columns;
0600         if (keys.isEmpty()) {
0601             return;
0602         } else if (keys.count() == 1) {
0603             d->mainWidget.m_addButton->setEnabled(false);
0604         }
0605         const int col = d->columns.takeFirst();
0606         item = new QTableWidgetItem(d->itemText(col, useHeader));
0607         item->setData(Qt::UserRole, col);
0608     } else {
0609         const QList<int> keys = d->rows;
0610         if (keys.isEmpty()) {
0611             return;
0612         } else if (keys.count() == 1) {
0613             d->mainWidget.m_addButton->setEnabled(false);
0614         }
0615         const int row = d->rows.takeFirst();
0616         item = new QTableWidgetItem(d->itemText(row, useHeader));
0617         item->setData(Qt::UserRole, row);
0618     }
0619     // Insert the item and its default attributes in a new row.
0620     const int row = d->mainWidget.m_tableWidget->rowCount();
0621     d->mainWidget.m_tableWidget->insertRow(row);
0622     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
0623     d->mainWidget.m_tableWidget->setItem(row, 0, item);
0624     item = new QTableWidgetItem(koIcon("view-sort-ascending"), i18n("Ascending"));
0625     item->setData(Qt::UserRole, Qt::AscendingOrder);
0626     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
0627     d->mainWidget.m_tableWidget->setItem(row, 1, item);
0628     item = new QTableWidgetItem(i18n("Case Sensitive"));
0629     item->setCheckState(Qt::Checked);
0630     item->setData(Qt::UserRole, Qt::CaseSensitive);
0631     item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
0632     d->mainWidget.m_tableWidget->setItem(row, 2, item);
0633 }
0634 
0635 bool greaterThan(const QTableWidgetSelectionRange &r1, const QTableWidgetSelectionRange &r2)
0636 {
0637     return r1.topRow() > r2.topRow();
0638 }
0639 
0640 void SortDialog::removeCriterion()
0641 {
0642     QTableWidget *const table = d->mainWidget.m_tableWidget;
0643     QList<QTableWidgetSelectionRange> ranges = table->selectedRanges();
0644     if (ranges.isEmpty()) {
0645         return;
0646     }
0647     std::stable_sort(ranges.begin(), ranges.end(), greaterThan);
0648     for (int i = 0; i < ranges.count(); ++i) {
0649         for (int row = ranges[i].bottomRow(); row >= ranges[i].topRow(); --row) {
0650             // Reinsert the item to be removed into the map of available items.
0651             const int index = table->item(row, 0)->data(Qt::UserRole).toInt();
0652             if (d->mainWidget.m_sortHorizontal->isChecked()) {
0653                 d->insertIndex(index, Qt::Vertical);
0654             } else {
0655                 d->insertIndex(index, Qt::Horizontal);
0656             }
0657             // Remove the item from the list.
0658             table->removeRow(row);
0659         }
0660     }
0661     d->mainWidget.m_addButton->setEnabled(true);
0662 }
0663 
0664 void SortDialog::moveCriterionUp()
0665 {
0666     QTableWidget *const table = d->mainWidget.m_tableWidget;
0667     const QList<QTableWidgetSelectionRange> ranges = table->selectedRanges();
0668     for (int i = 0; i < ranges.count(); ++i) {
0669         if (ranges[i].topRow() > 0) {
0670             const int srcRow = ranges[i].topRow() - 1;
0671             const int dstRow = ranges[i].bottomRow() + 1;
0672             table->insertRow(dstRow);
0673             for (int col = 0; col <= 2; ++col) {
0674                 table->setItem(dstRow, col, table->takeItem(srcRow, col));
0675             }
0676             table->removeRow(srcRow);
0677         }
0678     }
0679     itemSelectionChanged();
0680 }
0681 
0682 void SortDialog::moveCriterionDown()
0683 {
0684     QTableWidget *const table = d->mainWidget.m_tableWidget;
0685     const QList<QTableWidgetSelectionRange> ranges = table->selectedRanges();
0686     for (int i = 0; i < ranges.count(); ++i) {
0687         if (ranges[i].bottomRow() < table->rowCount() - 1) {
0688             const int srcRow = ranges[i].bottomRow() + 2;
0689             const int dstRow = ranges[i].topRow();
0690             table->insertRow(dstRow);
0691             for (int col = 0; col <= 2; ++col) {
0692                 table->setItem(dstRow, col, table->takeItem(srcRow, col));
0693             }
0694             table->removeRow(srcRow);
0695         }
0696     }
0697     itemSelectionChanged();
0698 }