From 93a0cd7402e3af3da75881852f6a4fc5b0648911 Mon Sep 17 00:00:00 2001 From: Trigve Date: Wed, 14 Sep 2016 15:04:10 +0200 Subject: [PATCH] Add support for multiple column sort in TableView --- src/Wt/WAbstractItemModel | 10 ++++ src/Wt/WAbstractItemModel.C | 3 + src/Wt/WAbstractItemView | 31 ++++++++++- src/Wt/WAbstractItemView.C | 131 +++++++++++++++++++++++++++++++++++--------- src/Wt/WTableView.C | 11 +++- src/Wt/WTreeView.C | 11 +++- 6 files changed, 166 insertions(+), 31 deletions(-) diff --git a/src/Wt/WAbstractItemModel b/src/Wt/WAbstractItemModel index db1e0e3..c07cc45 100644 --- a/src/Wt/WAbstractItemModel +++ b/src/Wt/WAbstractItemModel @@ -483,6 +483,16 @@ public: */ virtual void sort(int column, SortOrder order = AscendingOrder); + /*! \brief Sorts the model according to a particular columns. + * + * If the model supports sorting, then it should emit the + * layoutAboutToBeChanged() signal, rearrange its items, and + * afterwards emit the layoutChanged() signal. + * + * \sa layoutAboutToBeChanged(), layoutChanged() + */ + virtual void sort(std::vector const &columns, std::vector const &orders); + /*! \brief Expands a column. * * Expands a column. This may only be called by a view when the diff --git a/src/Wt/WAbstractItemModel.C b/src/Wt/WAbstractItemModel.C index 0b2afa6..a1b7465 100644 --- a/src/Wt/WAbstractItemModel.C +++ b/src/Wt/WAbstractItemModel.C @@ -112,6 +112,9 @@ boost::any WAbstractItemModel::headerData(int section, Orientation orientation, void WAbstractItemModel::sort(int column, SortOrder order) { } +void WAbstractItemModel::sort(std::vector const & columns, std::vector const & orders) +{} + void WAbstractItemModel::expandColumn(int column) { } diff --git a/src/Wt/WAbstractItemView b/src/Wt/WAbstractItemView index 60283b2..130c852 100644 --- a/src/Wt/WAbstractItemView +++ b/src/Wt/WAbstractItemView @@ -286,6 +286,26 @@ public: */ void sortByColumn(int column, SortOrder order); + /*! \brief Sorts the data according to a columns. + * + * Sorts the data according to data in columns \p columns and sort + * orders \p orders. + * + * + * \sa WAbstractItemModel::sort() + */ + void sortByColumns(std::vector const & columns, std::vector const &order); + + /*! \brief Add/remove the colum for actual sorting. + * + * Sorts the data according to data in column \p column and sort + * order \p order. + * + * + * \sa WAbstractItemModel::sort() + */ + void modifySortByColumn(int column, SortOrder order, bool append); + /*! \brief Returns the current sorting columm. * * \sa sortByColumn(), sortOrder() @@ -298,6 +318,12 @@ public: */ SortOrder sortOrder() const; + /*! \brief Returns the current sorting order. + * + * \sa sortByColumn(), sortColumn(), sortByColumns() + */ + std::vector sortOrders() const; + /*! \brief Enables or disables sorting for all columns. * * Enable or disable sorting by the user on all columns. @@ -990,7 +1016,8 @@ protected: WSignalMapper *clickedMapper_; mutable std::vector columns_; - int currentSortColumn_; + //int currentSortColumn_; + std::vector currentSortColumns_; bool dragEnabled_, dropsEnabled_; WWidget *dragWidget_; @@ -1150,7 +1177,7 @@ private: void handleHeaderMouseUp(int columnid, WMouseEvent event); void handleHeaderClicked(int columnid, WMouseEvent event); void handleHeaderDblClicked(int columnid, WMouseEvent event); - void toggleSortColumn(int columnid); + void toggleSortColumn(int columnid, bool AppendRemove); void updateColumnWidth(int columnId, int width); virtual WContainerWidget* headerContainer() = 0; diff --git a/src/Wt/WAbstractItemView.C b/src/Wt/WAbstractItemView.C index 3915211..4ef42bd 100644 --- a/src/Wt/WAbstractItemView.C +++ b/src/Wt/WAbstractItemView.C @@ -214,7 +214,6 @@ WAbstractItemView::WAbstractItemView(WContainerWidget *parent) : WCompositeWidget(parent), impl_(new WContainerWidget()), renderState_(NeedRerender), - currentSortColumn_(-1), dragEnabled_(false), dropsEnabled_(false), model_(0), @@ -702,7 +701,7 @@ void WAbstractItemView::handleHeaderClicked(int columnid, WMouseEvent event) ColumnInfo& info = columnInfo(column); if (sortEnabled_ && info.sorting) - toggleSortColumn(columnid); + toggleSortColumn(columnid, event.ctrlKey()); headerClicked_.emit(column, event); } @@ -722,31 +721,95 @@ void WAbstractItemView::handleHeaderMouseUp(int columnid, WMouseEvent event) headerMouseWentUp_.emit(columnById(columnid), event); } -void WAbstractItemView::toggleSortColumn(int columnid) +void WAbstractItemView::toggleSortColumn(int columnid, bool appendRemove) { int column = columnById(columnid); + bool const column_exist = (std::find(currentSortColumns_.cbegin(), currentSortColumns_.cend(), column) != currentSortColumns_.cend()); - if (column != currentSortColumn_) - sortByColumn(column, columnInfo(column).sortOrder); + // Append/Remove should be handled separately + if(appendRemove) + { + SortOrder order = AscendingOrder; + // If the column is sorted, remove it + if(column_exist) + modifySortByColumn(column, (order = AscendingOrder), false); + // Otherwise add it + else + modifySortByColumn(column, (order = columnInfo(column).sortOrder), true); + + // Backward compatibility + if(currentSortColumns_.size() == 1) + model_->sort(currentSortColumns_.front(), order); + + model_->sort(currentSortColumns_, sortOrders()); + } + // Changing sort order or complet new sorting else - sortByColumn(column, columnInfo(column).sortOrder == AscendingOrder - ? DescendingOrder : AscendingOrder); + { + if(!column_exist) + sortByColumn(column, columnInfo(column).sortOrder); + else + sortByColumn(column, columnInfo(column).sortOrder == AscendingOrder + ? DescendingOrder : AscendingOrder); + } +} + +void WAbstractItemView::sortByColumns(std::vector const &columns, std::vector const &orders) +{ + for(auto end = columns.size(), i = 0U; i < end; ++i) + modifySortByColumn(columns.at(i), orders.at(i), true); + + model_->sort(currentSortColumns_, sortOrders()); +} + +void WAbstractItemView::modifySortByColumn(int column, SortOrder order, bool append) +{ + // Add new column for sorting + if(append) + { + currentSortColumns_.push_back(column); + WText* t = headerSortIconWidget(column); + if(t) + { + t->setStyleClass(order == AscendingOrder + ? "Wt-tv-sh Wt-tv-sh-up" : "Wt-tv-sh Wt-tv-sh-down"); + columnInfo(column).sortOrder = order; + } + } + else + { + currentSortColumns_.erase(std::find(currentSortColumns_.begin(), currentSortColumns_.end(), column)); + WText* t = headerSortIconWidget(column); + if(t) + t->setStyleClass("Wt-tv-sh Wt-tv-sh-none"); + } + } int WAbstractItemView::sortColumn() const { - return currentSortColumn_; + return currentSortColumns_.empty() ? -1 : currentSortColumns_.front(); } SortOrder WAbstractItemView::sortOrder() const { - if (currentSortColumn_ >= 0 - && currentSortColumn_ < static_cast(columns_.size())) - return columns_[currentSortColumn_].sortOrder; + if (!currentSortColumns_.empty() + && currentSortColumns_.front() < static_cast(columns_.size())) + return columns_[currentSortColumns_.front()].sortOrder; else return AscendingOrder; } +std::vector WAbstractItemView::sortOrders() const +{ + std::vector ret; + + for(auto it = currentSortColumns_.cbegin(), end = currentSortColumns_.cend(); it != end; ++it) + ret.push_back(columnInfo(*it).sortOrder); + + return ret; +} + int WAbstractItemView::columnById(int columnid) const { for (unsigned i = 0; i < columns_.size(); ++i) @@ -788,23 +851,40 @@ WAbstractItemView::ColumnInfo& WAbstractItemView::columnInfo(int column) const void WAbstractItemView::sortByColumn(int column, SortOrder order) { - if (currentSortColumn_ != -1) { - WText* t = headerSortIconWidget(currentSortColumn_); - if (t) - t->setStyleClass("Wt-tv-sh Wt-tv-sh-none"); - } + auto const it_column = std::find(currentSortColumns_.cbegin(), currentSortColumns_.cend(), column); - currentSortColumn_ = column; - columnInfo(column).sortOrder = order; + // Column isn't in sorted columns, need to reset all the columns and add the new one + if (it_column == currentSortColumns_.cend()) { + for(auto it = currentSortColumns_.cbegin(), end = currentSortColumns_.cend(); it != end; ++it) + { + WText* t = headerSortIconWidget(*it); + if(t) + t->setStyleClass("Wt-tv-sh Wt-tv-sh-none"); + } + currentSortColumns_.clear(); - if (renderState_ != NeedRerender) { - WText* t = headerSortIconWidget(currentSortColumn_); - if (t) - t->setStyleClass(order == AscendingOrder - ? "Wt-tv-sh Wt-tv-sh-up" : "Wt-tv-sh Wt-tv-sh-down"); + // Add new column + currentSortColumns_.push_back(column); + columnInfo(column).sortOrder = order; + } + // Just change order + else + { + columnInfo(*it_column).sortOrder = order; + + if(renderState_ != NeedRerender) + { + WText* t = headerSortIconWidget(*it_column); + if(t) + t->setStyleClass(order == AscendingOrder + ? "Wt-tv-sh Wt-tv-sh-up" : "Wt-tv-sh Wt-tv-sh-down"); + } } - model_->sort(column, order); + if(currentSortColumns_.size() == 1) + model_->sort(column, order); + + model_->sort(currentSortColumns_, sortOrders()); } void WAbstractItemView::setSortingEnabled(bool enabled) @@ -1060,7 +1140,8 @@ WWidget *WAbstractItemView::createHeaderWidget(int column) sortIcon->setObjectName("sort"); sortIcon->setInline(false); sortIcon->setStyleClass("Wt-tv-sh Wt-tv-sh-none"); - if (currentSortColumn_ == column) + + if (std::find(currentSortColumns_.cbegin(), currentSortColumns_.cend() ,column) != currentSortColumns_.cend()) sortIcon->setStyleClass(info.sortOrder == AscendingOrder ? "Wt-tv-sh Wt-tv-sh-up" : "Wt-tv-sh Wt-tv-sh-down"); diff --git a/src/Wt/WTableView.C b/src/Wt/WTableView.C index 2c420b3..41d6291 100644 --- a/src/Wt/WTableView.C +++ b/src/Wt/WTableView.C @@ -1360,9 +1360,16 @@ void WTableView::modelColumnsAboutToBeRemoved(const WModelIndex& parent, if (ajaxMode()) canvas_->setWidth(canvas_->width().toPixels() - width); - if (start <= currentSortColumn_ && currentSortColumn_ <= end) - currentSortColumn_ = -1; + for(auto it = currentSortColumns_.cbegin(), end_it = currentSortColumns_.cend(); it != end_it; ++it) + { + if (start <= *it && *it <= end) + { + currentSortColumns_.clear(); + break; + } + } + if (renderState_ < NeedRerenderHeader) scheduleRerender(NeedRerenderHeader); diff --git a/src/Wt/WTreeView.C b/src/Wt/WTreeView.C index f5532c2..1cf0992 100644 --- a/src/Wt/WTreeView.C +++ b/src/Wt/WTreeView.C @@ -1941,8 +1941,15 @@ void WTreeView::modelColumnsRemoved(const WModelIndex& parent, c->removeColumns(start, count); } - if (start <= currentSortColumn_ && currentSortColumn_ <= end) - currentSortColumn_ = -1; + for(auto it = currentSortColumns_.cbegin(), end_it = currentSortColumns_.cend(); it != end_it; ++it) + { + if(start <= *it && *it <= end) + { + currentSortColumns_.clear(); + break; + + } + } } void WTreeView::modelRowsInserted(const WModelIndex& parent, -- 1.9.5.msysgit.0