/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "StdAfx.h" #include "ColumnSortProxyModel.h" #include "AbstractSortModel.h" class ColumnSortProxyModelLessThan { public: inline ColumnSortProxyModelLessThan(int column, const AbstractSortModel* source) : sort_column(column) , source_model(source) {} inline bool operator()(int r1, int r2) const { QModelIndex i1 = source_model->index(r1, sort_column); QModelIndex i2 = source_model->index(r2, sort_column); return source_model->LessThan(i1, i2); } private: int sort_column; const AbstractSortModel* source_model; }; class ColumnSortProxyModelGreaterThan { public: inline ColumnSortProxyModelGreaterThan(int column, const AbstractSortModel* source) : sort_column(column) , source_model(source) {} inline bool operator()(int r1, int r2) const { QModelIndex i1 = source_model->index(r1, sort_column); QModelIndex i2 = source_model->index(r2, sort_column); return source_model->LessThan(i2, i1); } private: int sort_column; const AbstractSortModel* source_model; }; ColumnSortProxyModel::ColumnSortProxyModel(QObject* parent) : QAbstractProxyModel(parent) { } ColumnSortProxyModel::~ColumnSortProxyModel() { } QVariant ColumnSortProxyModel::data(const QModelIndex& index, int role) const { Q_ASSERT(index.isValid() && index.model() == this); return sourceModel()->data(mapToSource(index), role); } QVariant ColumnSortProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { return sourceModel()->headerData(section, orientation, role); } int ColumnSortProxyModel::rowCount(const QModelIndex& index) const { if (!sourceModel() || index.isValid()) { return 0; } return sourceModel()->rowCount(index); } int ColumnSortProxyModel::columnCount(const QModelIndex& index) const { if (!sourceModel() || index.isValid()) { return 0; } return sourceModel()->columnCount(index); } QModelIndex ColumnSortProxyModel::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent); Q_ASSERT(!parent.isValid()); return createIndex(row, column); } QModelIndex ColumnSortProxyModel::parent(const QModelIndex& index) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex ColumnSortProxyModel::mapFromSource(const QModelIndex& sourceIndex) const { Q_ASSERT(!sourceIndex.isValid() || sourceIndex.model() == sourceModel()); if (!sourceIndex.isValid()) { return QModelIndex(); } int row = m_mappingToSource.indexOf(sourceIndex.row()); return createIndex(row, sourceIndex.column()); } QModelIndex ColumnSortProxyModel::mapToSource(const QModelIndex& proxyIndex) const { Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this); if (!proxyIndex.isValid()) { return QModelIndex(); } int row = m_mappingToSource.at(proxyIndex.row()); return sourceModel()->index(row, proxyIndex.column()); } void ColumnSortProxyModel::setSourceModel(QAbstractItemModel* sourceModel) { Q_ASSERT(qobject_cast(sourceModel)); QAbstractProxyModel::setSourceModel(sourceModel); connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &ColumnSortProxyModel::SortModel); connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &ColumnSortProxyModel::SortModel); connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &ColumnSortProxyModel::beginResetModel); connect(sourceModel, &QAbstractItemModel::modelReset, this, [=]() { { QSignalBlocker sb(this); SortModel(); } endResetModel(); }); connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &ColumnSortProxyModel::SortModel); connect(sourceModel, &QAbstractItemModel::dataChanged, this, &ColumnSortProxyModel::SourceDataChanged); SortModel(); } void ColumnSortProxyModel::sort(int column, Qt::SortOrder order) { int id = ColumnContains(column); if (id == -1) { AddColumn(column, order); } else { if (m_columns.at(id).sortOrder != order) { m_columns[id].sortOrder = order; SortModel(); } } } void ColumnSortProxyModel::AddColumn(int column, Qt::SortOrder order) { if (ColumnContains(column) == -1) { m_columns.push_front({ column, order }); SortModel(); } } void ColumnSortProxyModel::AddColumnWithoutSorting(int column, Qt::SortOrder order) { if (ColumnContains(column) == -1) { m_columns.push_front({ column, order }); } } void ColumnSortProxyModel::RemoveColumn(int column) { int id = ColumnContains(column); if (id != -1) { m_columns.remove(id); SortModel(); } } void ColumnSortProxyModel::RemoveColumnWithoutSorting(int column) { int id = ColumnContains(column); if (id != -1) { m_columns.remove(id); } } void ColumnSortProxyModel::SetColumns(const QVector& columns) { m_columns.clear(); foreach(int col, columns) { m_columns.push_back({ col, Qt::AscendingOrder }); } SortModel(); } void ColumnSortProxyModel::ClearColumns() { SortModel(); } bool ColumnSortProxyModel::IsColumnSorted(int col) const { int id = ColumnContains(col); return (id != -1); } Qt::SortOrder ColumnSortProxyModel::SortOrder(int col) const { int id = ColumnContains(col); if (id != -1) { return m_columns.at(id).sortOrder; } return Qt::AscendingOrder; } void ColumnSortProxyModel::SortModel() { emit layoutAboutToBeChanged(); int size = sourceModel() ? sourceModel()->rowCount() : 0; m_mappingToSource.resize(size); for (int i = 0; i < size; ++i) { m_mappingToSource[i] = i; } foreach(Column col, m_columns) { if (col.sortOrder == Qt::AscendingOrder) { ColumnSortProxyModelLessThan lt(col.column, qobject_cast(sourceModel())); std::stable_sort(m_mappingToSource.begin(), m_mappingToSource.end(), lt); } else { ColumnSortProxyModelGreaterThan gt(col.column, qobject_cast(sourceModel())); std::stable_sort(m_mappingToSource.begin(), m_mappingToSource.end(), gt); } } emit layoutChanged(); emit SortChanged(); } void ColumnSortProxyModel::SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { for (int col = topLeft.column(); col <= bottomRight.column(); ++col) { if (ColumnContains(col) != -1) { SortModel(); return; } } } int ColumnSortProxyModel::ColumnContains(int col) const { for (int i = 0; i < m_columns.count(); ++i) { if (m_columns.at(i).column == col) { return i; } } return -1; } #include