Feature #4380 » 0001-Add-support-for-multiple-column-sort-for-WStandardIt.patch
src/Wt/WSortFilterProxyModel | ||
---|---|---|
*
|
||
* \sa sort()
|
||
*/
|
||
int sortColumn() const { return sortKeyColumn_; }
|
||
int sortColumn() const { return sortKeyColumns_.empty() ? -1 : sortKeyColumns_.front(); }
|
||
/*! \brief Returns the all sort columns.
|
||
*
|
||
* When sort() has not been called, the model is unsorted, and this method returns empty container.
|
||
*
|
||
* \sa sort()
|
||
*/
|
||
std::vector<int> const &sortColumnsAll() const { return sortKeyColumns_; }
|
||
/*! \brief Returns the current sort order.
|
||
*
|
||
* \sa sort()
|
||
*/
|
||
SortOrder sortOrder() const { return sortOrder_; }
|
||
SortOrder sortOrder() const { return sortOrders_.empty() ? SortOrder::AscendingOrder : sortOrders_.front(); }
|
||
/*! \brief Returns the sort order for all sorted columns.
|
||
*
|
||
* \sa sort()
|
||
*/
|
||
std::vector<SortOrder> const & sortOrdersAll() const { return sortOrders_; }
|
||
/*! \brief Configure the proxy to dynamically track changes in the
|
||
* source model.
|
||
... | ... | |
virtual void sort(int column, SortOrder order = AscendingOrder);
|
||
virtual void sort(std::vector<int> const &columns, std::vector<SortOrder> const &orders);
|
||
protected:
|
||
/*! \brief Returns whether a source row is accepted by the filter.
|
||
*
|
||
... | ... | |
WRegExp *regex_;
|
||
int filterKeyColumn_, filterRole_;
|
||
int sortKeyColumn_, sortRole_;
|
||
SortOrder sortOrder_;
|
||
int sortRole_;
|
||
std::vector<int> sortKeyColumns_;
|
||
std::vector<SortOrder> sortOrders_;
|
||
bool dynamic_, inserting_;
|
||
std::vector<Wt::Signals::connection> modelConnections_;
|
src/Wt/WSortFilterProxyModel.C | ||
---|---|---|
bool WSortFilterProxyModel::Compare::operator()(int sourceRow1,
|
||
int sourceRow2) const
|
||
{
|
||
if (model->sortOrder_ == AscendingOrder)
|
||
return lessThan(sourceRow1, sourceRow2);
|
||
else
|
||
return lessThan(sourceRow2, sourceRow1);
|
||
return lessThan(sourceRow1, sourceRow2);
|
||
}
|
||
bool WSortFilterProxyModel::Compare::lessThan(int sourceRow1, int sourceRow2)
|
||
const
|
||
{
|
||
if (model->sortKeyColumn_ == -1)
|
||
return sourceRow1 < sourceRow2;
|
||
bool result;
|
||
WModelIndex lhs
|
||
= model->sourceModel()->index(sourceRow1, model->sortKeyColumn_,
|
||
if (model->sortKeyColumns_.empty())
|
||
result = sourceRow1 < sourceRow2;
|
||
else
|
||
{
|
||
// Process columns by one
|
||
for(auto i = 0U; i < model->sortKeyColumns_.size(); ++i)
|
||
{
|
||
auto const column = model->sortKeyColumns_[i];
|
||
auto const order = model->sortOrders_[i];
|
||
WModelIndex lhs
|
||
= model->sourceModel()->index(sourceRow1, column,
|
||
item->sourceIndex_);
|
||
WModelIndex rhs
|
||
= model->sourceModel()->index(sourceRow2, model->sortKeyColumn_,
|
||
WModelIndex rhs
|
||
= model->sourceModel()->index(sourceRow2, column,
|
||
item->sourceIndex_);
|
||
return model->lessThan(lhs, rhs);
|
||
// Save result, if we're at the last column
|
||
result = (order == SortOrder::AscendingOrder ? model->lessThan(lhs, rhs) : model->lessThan(rhs, lhs));
|
||
// Are the same, go to the next column comparison
|
||
if(!result && !(order == SortOrder::AscendingOrder ? model->lessThan(rhs, lhs) : model->lessThan(lhs, rhs)))
|
||
continue;
|
||
// Otherwise, we can stop here
|
||
else
|
||
break;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
#endif // WT_TARGET_JAVA
|
||
int WSortFilterProxyModel::Compare::compare(int sourceRow1, int sourceRow2)
|
||
const
|
||
{
|
||
int factor = (model->sortOrder_ == AscendingOrder) ? 1 : -1;
|
||
int result;
|
||
if (model->sortKeyColumn_ == -1)
|
||
return factor * (sourceRow1 - sourceRow2);
|
||
if(model->sortKeyColumns_.empty())
|
||
result = (sourceRow1 - sourceRow2);
|
||
else
|
||
{
|
||
// Process columns by one
|
||
for(auto i = 0U; i < model->sortKeyColumns_.size(); ++i)
|
||
{
|
||
auto const column = model->sortKeyColumns_[i];
|
||
auto const order = model->sortOrders_[i];
|
||
int factor = (order == AscendingOrder) ? 1 : -1;
|
||
WModelIndex lhs
|
||
= model->sourceModel()->index(sourceRow1, model->sortKeyColumn_,
|
||
WModelIndex lhs
|
||
= model->sourceModel()->index(sourceRow1, column,
|
||
item->sourceIndex_);
|
||
WModelIndex rhs
|
||
= model->sourceModel()->index(sourceRow2, model->sortKeyColumn_,
|
||
WModelIndex rhs
|
||
= model->sourceModel()->index(sourceRow2, column,
|
||
item->sourceIndex_);
|
||
return factor * model->compare(lhs, rhs);
|
||
result = factor * model->compare(lhs, rhs);
|
||
// Aren't same, we're done
|
||
if(result != 0)
|
||
break;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
#endif // DOXYGEN_ONLY
|
||
... | ... | |
regex_(0),
|
||
filterKeyColumn_(0),
|
||
filterRole_(DisplayRole),
|
||
sortKeyColumn_(-1),
|
||
sortRole_(DisplayRole),
|
||
sortOrder_(AscendingOrder),
|
||
dynamic_(false),
|
||
inserting_(false),
|
||
mappedRootItem_(0)
|
||
... | ... | |
void WSortFilterProxyModel::sort(int column, SortOrder order)
|
||
{
|
||
sortKeyColumn_ = column;
|
||
sortOrder_ = order;
|
||
sortKeyColumns_.clear();
|
||
sortOrders_.clear();
|
||
sortKeyColumns_.push_back(column);
|
||
sortOrders_.push_back(order);
|
||
invalidate();
|
||
}
|
||
void WSortFilterProxyModel::sort(std::vector<int> const &columns, std::vector<SortOrder> const &orders)
|
||
{
|
||
sortKeyColumns_ = columns;
|
||
sortOrders_ = orders;
|
||
}
|
||
void WSortFilterProxyModel::invalidate()
|
||
{
|
||
if (sourceModel()) {
|
||
... | ... | |
/*
|
||
* Sort...
|
||
*/
|
||
if (sortKeyColumn_ != -1) {
|
||
if (!sortKeyColumns_.empty()) {
|
||
Utils::stable_sort(item->proxyRowMap_, Compare(this, item));
|
||
rebuildSourceRowMap(item);
|
||
... | ... | |
= dynamic_ && (filterKeyColumn_ >= topLeft.column()
|
||
&& filterKeyColumn_ <= bottomRight.column());
|
||
bool resort
|
||
= dynamic_ && (sortKeyColumn_ >= topLeft.column()
|
||
&& sortKeyColumn_ <= bottomRight.column());
|
||
bool resort = dynamic_;
|
||
if(dynamic_)
|
||
{
|
||
for(auto const &column : sortKeyColumns_)
|
||
{
|
||
resort = (column >= topLeft.column()
|
||
&& column <= bottomRight.column());
|
||
// At least 1 column in range, no need to continue
|
||
if(resort)
|
||
break;
|
||
}
|
||
}
|
||
WModelIndex parent = mapFromSource(topLeft.parent());
|
||
// distinguish between invalid parent being root item or being filtered out
|
src/Wt/WStandardItem | ||
---|---|---|
*/
|
||
virtual void sortChildren(int column, SortOrder order);
|
||
/*! \brief Sorts the children according to a given column and sort order.
|
||
*/
|
||
virtual void sortChildren(std::vector<int> const &columns, std::vector<SortOrder> const &orders);
|
||
protected:
|
||
/*! \brief Set the model for this WStandardItem and its children.
|
||
*
|
||
... | ... | |
void adoptChild(int row, int column, WStandardItem *item);
|
||
void orphanChild(WStandardItem *item);
|
||
void recursiveSortChildren(int column, SortOrder order);
|
||
void recursiveSortChildren(std::vector<int> const &columns, std::vector<SortOrder> const &orders);
|
||
void renumberColumns(int column);
|
||
void renumberRows(int row);
|
||
src/Wt/WStandardItem.C | ||
---|---|---|
SortOrder order;
|
||
};
|
||
// Item comparer for multiple columns
|
||
struct WStandardItemCompareMulti W_JAVA_COMPARATOR(int)
|
||
{
|
||
WStandardItemCompareMulti(WStandardItem *anItem, std::vector<int> const &aColumns, std::vector<SortOrder> const &anOrders)
|
||
: item(anItem),
|
||
columns(aColumns),
|
||
orders(anOrders)
|
||
{ }
|
||
#ifndef WT_TARGET_JAVA
|
||
bool operator()(int r1, int r2) const {
|
||
return compare(r1, r2);
|
||
}
|
||
bool compare(int r1, int r2) const {
|
||
// Always should have valid value at return
|
||
bool result;
|
||
// Process columns by one
|
||
for(auto i = 0U; i < columns.size(); ++i)
|
||
{
|
||
auto const column = columns[i];
|
||
auto const order = orders[i];
|
||
WStandardItem *item1 = item->child(r1, column);
|
||
WStandardItem *item2 = item->child(r2, column);
|
||
// First item valid
|
||
if(item1) {
|
||
// Second also valid
|
||
if(item2) {
|
||
// Save result, if we're at the last column
|
||
result = (order == SortOrder::AscendingOrder ? *item1 < *item2 : *item2 < *item1);
|
||
// Are the same, go to the next column comparison
|
||
if(!result && !(order == SortOrder::AscendingOrder ? *item2 < *item1 : *item1 < *item2))
|
||
continue;
|
||
// Otherwise, we can stop here
|
||
else
|
||
break;
|
||
}
|
||
// Second not valid
|
||
else {
|
||
result = (order == SortOrder::AscendingOrder ? UNSPECIFIED_RESULT == -1 : UNSPECIFIED_RESULT != -1);
|
||
break;
|
||
}
|
||
}
|
||
// First not valid
|
||
else {
|
||
// Second valid
|
||
if(item2) {
|
||
result = (order == SortOrder::AscendingOrder ? UNSPECIFIED_RESULT != -1 : UNSPECIFIED_RESULT == -1);
|
||
break;
|
||
}
|
||
// Second also not valid, move to the next column
|
||
else {
|
||
result = (order == SortOrder::AscendingOrder ? false : true);
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
#else
|
||
int compare(int r1, int r2) const {
|
||
// Always should have valid value at return
|
||
int result;
|
||
// Process columns by one
|
||
for(auto i = 0U; i < columns.size(); ++i)
|
||
{
|
||
auto const column = columns[i];
|
||
auto const order = orders[i];
|
||
WStandardItem *item1 = item->child(r1, column);
|
||
WStandardItem *item2 = item->child(r2, column);
|
||
// First item valid
|
||
if(item1)
|
||
{
|
||
// Second also valid
|
||
if(item2)
|
||
{
|
||
// Save result, if we're at the last column
|
||
result = (order == SortOrder::AscendingOrder ? item1->compare(*item2) : item2->compare(*item1));
|
||
// Are the same, go to the next column comparison
|
||
if(result == 0)
|
||
continue;
|
||
// Otherwise, we can stop here
|
||
else
|
||
break;
|
||
}
|
||
// Second not valid
|
||
else
|
||
{
|
||
result = (order == SortOrder::AscendingOrder ? -UNSPECIFIED_RESULT : UNSPECIFIED_RESULT);
|
||
break;
|
||
}
|
||
}
|
||
// First not valid
|
||
else
|
||
{
|
||
// Second valid
|
||
if(item2)
|
||
{
|
||
result = (order == SortOrder::AscendingOrder ? UNSPECIFIED_RESULT : -UNSPECIFIED_RESULT);
|
||
break;
|
||
}
|
||
// Second also not valid, move to the next column
|
||
else
|
||
{
|
||
result = 0;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
#endif // WT_TARGET_JAVA
|
||
WStandardItem *item;
|
||
std::vector<int> const &columns;
|
||
std::vector<SortOrder> const &orders;
|
||
};
|
||
}
|
||
namespace Wt {
|
||
... | ... | |
model_->layoutChanged().emit();
|
||
}
|
||
void WStandardItem::sortChildren(std::vector<int> const &columns, std::vector<SortOrder> const &orders)
|
||
{
|
||
if(model_)
|
||
model_->layoutAboutToBeChanged().emit();
|
||
recursiveSortChildren(columns, orders);
|
||
if(model_)
|
||
model_->layoutChanged().emit();
|
||
}
|
||
bool WStandardItem::operator< (const WStandardItem& other) const
|
||
{
|
||
return compare(other) < 0;
|
||
... | ... | |
}
|
||
}
|
||
void WStandardItem::recursiveSortChildren(std::vector<int> const &columns, std::vector<SortOrder> const &orders)
|
||
{
|
||
bool all_columns_valid = true;
|
||
for(auto const col : columns)
|
||
{
|
||
if((all_columns_valid = (col < columnCount())) == false)
|
||
break;
|
||
}
|
||
if (all_columns_valid) {
|
||
#ifndef WT_TARGET_JAVA
|
||
std::vector<int> permutation(rowCount());
|
||
for (unsigned i = 0; i < permutation.size(); ++i)
|
||
permutation[i] = i;
|
||
#else
|
||
std::vector<int> permutation;
|
||
for (unsigned i = 0; i < rowCount(); ++i)
|
||
permutation.push_back(i);
|
||
#endif // WT_TARGET_JAVA
|
||
Utils::stable_sort(permutation, WStandardItemCompareMulti(this, columns, orders));
|
||
#ifndef WT_TARGET_JAVA
|
||
Column temp(rowCount());
|
||
#endif
|
||
for (int c = 0; c < columnCount(); ++c) {
|
||
#ifdef WT_TARGET_JAVA
|
||
Column temp;
|
||
#endif // WT_TARGET_JAVA
|
||
Column& cc = (*columns_)[c];
|
||
for (int r = 0; r < rowCount(); ++r) {
|
||
#ifndef WT_TARGET_JAVA
|
||
temp[r] = cc[permutation[r]];
|
||
#else
|
||
temp.push_back(cc[permutation[r]]);
|
||
#endif // WT_TARGET_JAVA
|
||
if (temp[r])
|
||
temp[r]->row_ = r;
|
||
}
|
||
(*columns_)[c] = temp;
|
||
}
|
||
}
|
||
for (int c = 0; c < columnCount(); ++c)
|
||
for (int r = 0; r < rowCount(); ++r) {
|
||
WStandardItem *ch = child(r, c);
|
||
if (ch)
|
||
ch->recursiveSortChildren(columns, orders);
|
||
}
|
||
}
|
||
void WStandardItem::signalModelDataChange()
|
||
{
|
||
if (model_) {
|
src/Wt/WStandardItemModel | ||
---|---|---|
virtual void sort(int column, SortOrder order = AscendingOrder);
|
||
virtual void sort(std::vector<int> const &columns, std::vector<SortOrder> const &orders);
|
||
/*! \brief %Signal emitted when an item is changed.
|
||
*
|
||
* This signal is emitted whenever data for an item has changed. The
|
src/Wt/WStandardItemModel.C | ||
---|---|---|
invisibleRootItem_->sortChildren(column, order);
|
||
}
|
||
void WStandardItemModel::sort(std::vector<int> const &columns, std::vector<SortOrder> const &orders)
|
||
{
|
||
invisibleRootItem_->sortChildren(columns, orders);
|
||
}
|
||
}
|
||
#endif // DOXYGEN_ONLY
|
- « Previous
- 1
- 2
- Next »