05 recalculate();
06 }
Слот setAutoRecalculate() соответствует пункту меню Options | Auto—Recalculate. Если эта опция включена, мы сразу же пересчитаем всю электронную таблицу и будем уверены, что она показывает обновленные значения; впоследствии функция recalculate() будет автоматически вызываться из somethingChanged().
Нам не нужно реализовывать специальную функцию для пункта меню Options | Show Grid, поскольку в QTableWidget уже содержится слот setShowGrid(), который наследуется от базового класса QTableView. Остается только реализовать функцию Spreadsheet::sort(), которая вызывается из MainWindow::sort():
01 void Spreadsheet::sort(const SpreadsheetCompare &compare)
02 {
03 QList<QStringList> rows;
04 QTableWidgetSelectionRange range = selectedRange();
05 int i;
06 for (i = 0; i < range.rowCount(); ++i) {
07 QStringList row;
08 for (int j = 0; j < range.columnCount(); ++j)
09 row.append(formula(range.topRow() + i,
10 range.leftColumn() + j));
11 rows.append(row);
12 }
13 qStableSort(rows.begin(), rows.end(), compare);
14 for (i = 0; i < range.rowCount(); ++i) {
15 for (int j = 0; j < range.columnCount(); ++j)
16 setFormula(range.topRow() + i, range.leftColumn() + j, rows[i][j]);
17 }
18 clearSelection();
19 somethingChanged();
20 }
Сортировка работает на текущей выделенной области и переупорядочивает строки в соответствии со значениями ключей порядка сортировки, хранящимися в объекте compare. Мы представляем каждую строку данных в QStringList, а выделенную область храним в виде списка строк. Мы используем алгоритм Qt qStableSort() и для простоты сортируем по выражениям формул, а не по их значениям. Стандартные алгоритмы и структуры данных Qt рассматривается в главе 11 («Классы—контейнеры»).
Рис. 4.8. Хранение выделенной области в виде списка строк.
В качестве аргументов функции qStableSort() используются итератор начала, итератор конца и функция сравнения. Функция сравнения имеет два аргумента (оба имеют тип QStringLists), и она возвращает true, когда первый аргумент «больше, чем» второй аргумент, и false в противном случае. Передаваемый как функция сравнения объект compare фактически не является функцией, но он может использоваться и в таком качестве, в чем мы вскоре сможем убедиться.
Рис. 4.9. Помещение данных в таблицу после сортировки.
После выполнения функции qStableSort() мы помещаем данные обратно в таблицу, сбрасываем выделение области и вызываем функцию somethingChanged(). Класс SpreadsheetCompare в spreadsheet.h определен следующим образом:
01 class SpreadsheetCompare
02 {
03 public:
04 bool operator()(const QStringList &row1,
05 const QStringList &row2) const;
06 enum { KeyCount = 3 };
07 int keys[KeyCount];
08 bool ascending[KeyCount];
09 };
Класс SpreadsheetCompare является специальным классом, реализующим оператор (). Это позволяет нам применять этот класс в качестве функции. Такие классы называются объектами функций или функторами (functors).
Для лучшего понимания работы функторов мы сначала разберем простой пример:
01 class Square
02 {
03 public:
04 int operator()(int x) const { return x * x; }
05 }
Класс Square содержит одну функцию operator()(int), которая возвращает квадрат переданного ей значения параметра. Обозначая функцию в виде operator()(int), а не в виде, например, compute(int), мы получаем возможность применения объекта типа Square как функции:
Square square;
int у = square(5);
Теперь рассмотрим пример с применением объекта SpreadsheetCompare:
QStringList row1, row2;
QSpreadsheetCompare compare;
…
if (compare(row1, row2)) {
// строка row1 меньше, чем row2
}
Объект compare можно использовать так же, как если бы он был обычной функцией compare(). Кроме того, он может быть реализован таким образом, что будет осуществлять доступ ко всем ключам сортировки и всем признакам порядка сортировки, которые хранятся в переменных—членах класса.
Можно использовать другой подход, когда ключи сортировки и признаки порядка сортировки хранятся в глобальных переменных и используется функция обычного типа compare(). Однако связь через глобальные переменные выглядит неизящно и может быть причиной тонких ошибок. Функторы представляют собой более мощное средство связи для таких функций—шаблонов, как qStableSort().