OSDN Git Service

Board treeview is sortable.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Thu, 2 Jul 2009 19:36:27 +0000 (04:36 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Thu, 2 Jul 2009 19:36:27 +0000 (04:36 +0900)
src/board_view_column.cxx
src/board_view_column.hxx
src/board_window.cxx
src/board_window.hxx
src/thread_list_model.cxx
src/thread_list_model.hxx

index 5bdd0cc..6f412d1 100644 (file)
 
 #include "board_view_column.hxx"
 
-#include <sigc++/functors/mem_fun.h>
-#include <glibmm/ustring.h>
-#include <gtkmm/cellrenderer.h>
-#include <gtkmm/treeviewcolumn.h>
-#include <gtkmm/treemodel.h>
-
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/mpl/find.hpp>
-
-#include "thread_list_model.hxx"
+#include <sstream>
 
 
 namespace dialektos {
@@ -38,34 +30,9 @@ namespace dialektos {
 namespace view_column {
 
 
-Base::Base(const Glib::ustring& title) :
-      Gtk::TreeViewColumn(title) {
-  set_min_width(15);
-  set_reorderable(true);
-  set_resizable(true);
-  set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
-  set_clickable(true);
-//    set_fixed_width(30); // specify in a derived class.
-
-  Gtk::CellRendererText* cell = Gtk::manage(new Gtk::CellRendererText);
-  pack_start(*cell, true);
-  set_cell_data_func(*cell,
-      sigc::mem_fun(*this, &Base::on_cell_data));
-}
-
-Base::~Base() {}
-void Base::on_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  do_cell_data(cell, iter);
-}
-
-StartTime::StartTime() : Base("Since") {
-  set_fixed_width(50);
-}
-StartTime::~StartTime() {}
-
-void StartTime::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
+void RenderCellStartTime::operator()(
+    Gtk::CellRenderer* cell,
+    const Gtk::TreeModel::iterator& iter) const {
 
   model_column::ID::type value;
   iter->get_value(boost::mpl::find<ModelColumns::type,
@@ -88,57 +55,6 @@ void StartTime::do_cell_data(Gtk::CellRenderer* cell,
 }
 
 
-Average::Average() : Base("Average") {
-  set_fixed_width(50);
-}
-Average::~Average() {}
-
-void Average::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  on_cell_data_int<model_column::Average>(cell, iter);
-}
-
-
-Title::Title() : Base("Title") {
-  set_fixed_width(200);
-}
-Title::~Title() {}
-void Title::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  on_cell_data_string<model_column::Title>(cell, iter);
-}
-
-
-ResNum::ResNum() : Base("ResNum") {
-  set_fixed_width(30);
-}
-ResNum::~ResNum() {}
-void ResNum::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  on_cell_data_int<model_column::ResNum>(cell, iter);
-}
-
-
-Number::Number() : Base("No.") {
-  set_fixed_width(30);
-}
-Number::~Number() {}
-void Number::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  on_cell_data_int<model_column::Number>(cell, iter);
-}
-
-
-LineCount::LineCount() : Base("Read") {
-  set_fixed_width(30);
-}
-LineCount::~LineCount() {}
-void LineCount::do_cell_data(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  on_cell_data_int<model_column::LineCount>(cell, iter);
-}
-
-
 } // namespace view_column
 
 } // namespace dialektos
index 26cf9a4..615ed4b 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <gtkmm/treeviewcolumn.h>
 #include <glibmm/ustring.h>
-#include <gtkmm/treemodel.h>
 #include <gtkmm/cellrenderer.h>
 #include <boost/mpl/find.hpp>
 #include "thread_list_model.hxx"
@@ -36,99 +35,161 @@ namespace view_column {
 
 class Base : public Gtk::TreeViewColumn {
 public:
-  Base(const Glib::ustring& title);
-  virtual ~Base();
-protected:
-  template <typename ModelColumnType>
-  void on_cell_data_int(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
-  template <typename ModelColumnType>
-  void on_cell_data_string(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter) = 0;
-  void on_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+  Base(const Glib::ustring& title) : Gtk::TreeViewColumn(title) {
+    set_min_width(15);
+    set_reorderable(true);
+    set_resizable(true);
+    set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
+    set_clickable(true);
+//    set_fixed_width(30); // specify in a derived class.
+  }
+  virtual ~Base() {}
+  virtual std::string get_id() const = 0;
 };
 
-template <typename ModelColumnType>
-void Base::on_cell_data_int(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
-  typename ModelColumnType::type value;
-  iter->get_value(boost::mpl::find<model_column::List,
-      ModelColumnType>::type::pos::value, value);
-  if (value > 0) cell->set_property("text", value);
-  else cell->set_property("text", Glib::ustring(""));
-}
-template <typename ModelColumnType>
-void Base::on_cell_data_string(Gtk::CellRenderer* cell,
-    const Gtk::TreeModel::iterator& iter) {
+template <typename DerivedT>
+class ViewColumnBase : public Base {
+public:
+  ViewColumnBase(const Glib::ustring& title) : Base(title) {
+    Gtk::CellRendererText* cell = Gtk::manage(new Gtk::CellRendererText);
+    pack_start(*cell, true);
+
+    typedef typename DerivedT::render_cell_functor FunctorType;
+    set_cell_data_func(*cell, FunctorType());
+  }
+  virtual ~ViewColumnBase() {}
+};
 
-  typename ModelColumnType::type value;
-  iter->get_value(boost::mpl::find<model_column::List,
-      ModelColumnType>::type::pos::value, value);
 
-  cell->set_property("text", value);
-}
+namespace {
+template <typename TypeHolderT, typename ValueType> struct DoRenderCell;
 
+template <typename TypeHolderT>
+struct DoRenderCell<TypeHolderT, std::string> :
+  public std::binary_function<Gtk::CellRenderer*,
+    Gtk::TreeModel::iterator, void> {
+  void operator()(
+      Gtk::CellRenderer* cell,
+      const Gtk::TreeModel::iterator& iter) const {
+    typename TypeHolderT::type value;
+    iter->get_value(boost::mpl::find<model_column::List,
+        TypeHolderT>::type::pos::value, value);
 
-class StartTime: public Base {
-public:
-  StartTime();
-  virtual ~StartTime();
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+    cell->set_property("text", value);
+  }
+};
+template <typename TypeHolderT>
+struct DoRenderCell<TypeHolderT, int> :
+  public std::binary_function<Gtk::CellRenderer*,
+    Gtk::TreeModel::iterator, void> {
+  void operator()(
+      Gtk::CellRenderer* cell,
+      const Gtk::TreeModel::iterator& iter) const {
+    typename TypeHolderT::type value;
+    iter->get_value(boost::mpl::find<model_column::List,
+        TypeHolderT>::type::pos::value, value);
+    if (value > 0) cell->set_property("text", value);
+    else cell->set_property("text", Glib::ustring(""));
+  }
 };
+template <typename TypeHolderT>
+struct DoRenderCell<TypeHolderT, double> :
+  public std::binary_function<Gtk::CellRenderer*,
+    Gtk::TreeModel::iterator, void> {
+  void operator()(
+      Gtk::CellRenderer* cell,
+      const Gtk::TreeModel::iterator& iter) const {
+    typename TypeHolderT::type value;
+    iter->get_value(boost::mpl::find<model_column::List,
+        TypeHolderT>::type::pos::value, value);
+    if (value > 0) cell->set_property("text", value);
+    else cell->set_property("text", Glib::ustring(""));
+  }
+};
+
+template <typename TypeHolderT>
+struct RenderCell :
+  public DoRenderCell<TypeHolderT, typename TypeHolderT::type> {};
+
+} // anonymous namespace
 
-class Average: public Base {
+
+struct RenderCellStartTime :
+  public std::binary_function<Gtk::CellRenderer*,
+    Gtk::TreeModel::iterator, void> {
+  void operator()(
+      Gtk::CellRenderer* cell,
+      const Gtk::TreeModel::iterator& iter) const;
+};
+
+
+class StartTime: public ViewColumnBase<StartTime> {
 public:
-  Average();
-  virtual ~Average();
+  typedef RenderCellStartTime render_cell_functor;
+  typedef model_column::ID sort_model_column_type;
+  StartTime() : ViewColumnBase<StartTime>("Since") {
+    set_fixed_width(50);
+  }
+  virtual ~StartTime() {}
+  virtual std::string get_id() const { return "start_time"; };
+};
 
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+class Average: public ViewColumnBase<Average> {
+public:
+  typedef RenderCell<model_column::Average> render_cell_functor;
+  typedef model_column::Average sort_model_column_type;
+  Average() : ViewColumnBase<Average>("Average") {
+    set_fixed_width(50);
+  }
+  virtual ~Average() {}
+  virtual std::string get_id() const { return "average"; };
 };
 
 
-class Title: public Base {
+class Title: public ViewColumnBase<Title> {
 public:
-  Title();
-  virtual ~Title();
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+  typedef RenderCell<model_column::Title> render_cell_functor;
+  typedef model_column::Title sort_model_column_type;
+  Title() : ViewColumnBase<Title>("Title") {
+    set_fixed_width(200);
+  }
+  virtual ~Title() {}
+  virtual std::string get_id() const { return "title"; };
 };
 
 
-class ResNum: public Base {
+class ResNum: public ViewColumnBase<ResNum> {
 public:
-  ResNum();
-  virtual ~ResNum();
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+  typedef RenderCell<model_column::ResNum> render_cell_functor;
+  typedef model_column::ResNum sort_model_column_type;
+  ResNum() : ViewColumnBase<ResNum>("Res") {
+    set_fixed_width(30);
+  }
+  virtual ~ResNum() {}
+  virtual std::string get_id() const { return "res_num"; };
 };
 
 
-class Number: public Base {
+class Number: public ViewColumnBase<Number> {
 public:
-  Number();
-  virtual ~Number();
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+  typedef RenderCell<model_column::Number> render_cell_functor;
+  typedef model_column::Number sort_model_column_type;
+  Number() : ViewColumnBase<Number>("No.") {
+    set_fixed_width(30);
+  }
+  virtual ~Number() {}
+  virtual std::string get_id() const { return "number"; };
 };
 
-class LineCount: public Base {
+class LineCount: public ViewColumnBase<LineCount> {
 public:
-  LineCount();
-  virtual ~LineCount();
-private:
-  virtual void do_cell_data(Gtk::CellRenderer* cell,
-      const Gtk::TreeModel::iterator& iter);
+  typedef RenderCell<model_column::LineCount> render_cell_functor;
+  typedef model_column::LineCount sort_model_column_type;
+  LineCount() : ViewColumnBase<LineCount>("Read") {
+    set_fixed_width(30);
+  }
+  virtual ~LineCount() {}
+  virtual std::string get_id() const { return "line_count"; };
 };
 
 
index 283a806..6a7a2ac 100644 (file)
@@ -109,32 +109,40 @@ BoardWindow::BoardWindow(std::auto_ptr<bbs_detail::Base> bbs) :
   tree_view_.set_fixed_height_mode(true);
   tree_view_.set_rules_hint(true);
 
-  tree_view_.append_column(*Gtk::manage(new view_column::Number));
-  tree_view_.append_column(*Gtk::manage(new view_column::Title));
-  tree_view_.append_column(*Gtk::manage(new view_column::ResNum));
-  tree_view_.append_column(*Gtk::manage(new view_column::LineCount));
-  tree_view_.append_column(*Gtk::manage(new view_column::Average));
-  tree_view_.append_column(*Gtk::manage(new view_column::StartTime));
-
   BoardWindowState state;
   state.from_xml(boost::filesystem::path(bbs_->get_board_state_path()));
 
+  add_column<view_column::Number>(state);
+  add_column<view_column::Title>(state);
+  add_column<view_column::ResNum>(state);
+  add_column<view_column::LineCount>(state);
+  add_column<view_column::Average>(state);
+  add_column<view_column::StartTime>(state);
+
   set_default_size(state.width, state.height);
   if (state.menubar) menubar_->show();
   if (state.toolbar) toolbar_->show();
   if (state.statusbar) statusbar_.show();
 
   // set columns widths
-  BOOST_FOREACH(Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
-    BoardWindowState::ColumnInfo info = state.columns[column->get_title()];
+  BOOST_FOREACH(Gtk::TreeViewColumn* kolumn, tree_view_.get_columns()) {
+    view_column::Base* column =
+      dynamic_cast<view_column::Base*>(kolumn);
+
+    assert(column);
+    BoardWindowState::ColumnInfo info = state.columns[column->get_id()];
     if (info.width > 0) column->set_fixed_width(info.width);
   }
 
   // set columns order.
   typedef std::pair<Gtk::TreeViewColumn*, int> PairType;
   std::vector<PairType> column_order;
-  BOOST_FOREACH(Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
-    BoardWindowState::ColumnInfo info = state.columns[column->get_title()];
+  BOOST_FOREACH(Gtk::TreeViewColumn* kolumn, tree_view_.get_columns()) {
+    view_column::Base* column =
+      dynamic_cast<view_column::Base*>(kolumn);
+
+    assert(column);
+    BoardWindowState::ColumnInfo info = state.columns[column->get_id()];
     column_order.push_back(std::make_pair(column, info.order));
   }
   std::sort(column_order.begin(), column_order.end(), LessSecond());
@@ -366,12 +374,22 @@ void BoardWindow::save_state() const{
   state.statusbar = statusbar_.is_visible();
 
   int order = 0;
-  BOOST_FOREACH(const Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
+  BOOST_FOREACH(const Gtk::TreeViewColumn* kolumn, tree_view_.get_columns()) {
+    const view_column::Base* column =
+      dynamic_cast<const view_column::Base*>(kolumn);
+
+    assert(column);
+
     BoardWindowState::ColumnInfo info;
     info.width = column->get_width();
     info.order = order;
-    state.columns[column->get_title()] = info;
+    state.columns[column->get_id()] = info;
     ++order;
+
+    if (column->get_sort_indicator()) {
+      state.sort.column = column->get_id();
+      state.sort.ascendant = column->get_sort_order() == Gtk::SORT_ASCENDING;
+    }
   }
 
   state.to_xml(boost::filesystem::path(bbs_->get_board_state_path()));
index 99e2980..6be2fcc 100644 (file)
 #include <gtkmm/treeview.h>
 #include <gtkmm/scrolledwindow.h>
 #include <boost/scoped_ptr.hpp>
+#include <boost/foreach.hpp>
 #include <memory>
 #include "application_framework.hxx"
 #include "thread_list_model.hxx"
 #include "http_get.hxx"
 #include "board_subject_item.hxx"
 #include "board_subject_idx.hxx"
+#include "board_window_state.hxx"
 
 
 namespace dialektos {
@@ -74,6 +76,12 @@ private:
 
   void save_content(const http::Response&);
 
+  template <typename ColumnType>
+  void on_column_clicked(ColumnType& column);
+
+  template <typename ColumnType>
+  void add_column(const BoardWindowState&);
+
   Gtk::TreeView tree_view_;
   boost::scoped_ptr<bbs_detail::Base> bbs_;
   Gtk::ScrolledWindow scrolled_;
@@ -82,6 +90,46 @@ private:
   SubjectIdx idx_;
 };
 
+template <typename ColumnType>
+void BoardWindow::on_column_clicked(ColumnType& column) {
+  typedef typename ColumnType::sort_model_column_type SortColumn;
+  BOOST_FOREACH(Gtk::TreeViewColumn* kolumn, tree_view_.get_columns()){
+    if (kolumn == &column) {
+      if (kolumn->get_sort_indicator()) {
+        if (kolumn->get_sort_order() == Gtk::SORT_ASCENDING)
+          kolumn->set_sort_order(Gtk::SORT_DESCENDING);
+        else kolumn->set_sort_order(Gtk::SORT_ASCENDING);
+      }
+      kolumn->set_sort_indicator(true);
+    } else {
+      if (kolumn->get_sort_indicator())
+        kolumn->set_sort_order(Gtk::SORT_ASCENDING);
+      kolumn->set_sort_indicator(false);
+    }
+  }
+  tree_model_->set_sort_function<SortColumn>(
+      column.get_sort_order() == Gtk::SORT_ASCENDING);
+  tree_model_->sort();
+}
+
+template <typename ColumnType>
+void BoardWindow::add_column(const BoardWindowState& state) {
+  ColumnType* p = Gtk::manage(new ColumnType);
+  tree_view_.append_column(*p);
+  p->signal_clicked().connect(sigc::bind<ColumnType&>(
+      sigc::mem_fun(*this, &BoardWindow::on_column_clicked<ColumnType>),
+      *p));
+
+  if (state.sort.column == p->get_id()) {
+    typedef typename ColumnType::sort_model_column_type SortColumn;
+    p->set_sort_indicator(true);
+    p->set_sort_order(
+        state.sort.ascendant ? Gtk::SORT_ASCENDING : Gtk::SORT_DESCENDING);
+    tree_model_->set_sort_function<SortColumn>(state.sort.ascendant);
+  }
+
+}
+
 
 } // namespace dialektos
 
index da9b8bb..cb7f8f6 100644 (file)
@@ -27,7 +27,7 @@
 #include <boost/mpl/find.hpp>
 #include <boost/mpl/pop_front.hpp>
 #include <boost/foreach.hpp>
-#include <boost/integer_traits.hpp>
+#include <boost/function.hpp>
 
 
 namespace dialektos {
@@ -80,20 +80,6 @@ struct GetColumnGType {
 
 } // namespace model_column
 
-namespace {
-struct CompareNumber {
-  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
-    int left_num = model_column::field<model_column::Number>(lhs);
-    int right_num = model_column::field<model_column::Number>(rhs);
-
-    if (left_num == 0) left_num = boost::integer_traits<int>::const_max;
-    if (right_num == 0) right_num = boost::integer_traits<int>::const_max;
-
-    return left_num < right_num;
-  }
-};
-}
-
 
 Glib::RefPtr<ThreadListModel> ThreadListModel::create() {
   return Glib::RefPtr<ThreadListModel>(new ThreadListModel());
@@ -102,7 +88,7 @@ Glib::RefPtr<ThreadListModel> ThreadListModel::create() {
 ThreadListModel::ThreadListModel():
   Glib::ObjectBase(typeid(ThreadListModel)),
   Glib::Object(),
-  list_(), order_() {
+  list_(), order_(), sort_function_(Compare<model_column::Number>()) {
 }
 
 ThreadListModel::~ThreadListModel() {}
@@ -148,25 +134,28 @@ void ThreadListModel::set_buffer(
     append(cols);
   }
 
-  OrderType old_order = order_;
-
-  std::sort(list_.begin(), list_.end(), CompareNumber());
+  sort();
+}
 
-  for (size_t i = 0; i != list_.size(); i++) {
-    const model_column::ID::type& id =
-      model_column::field<model_column::ID>(list_[i]);
-    order_[id] = i;
-  }
+void ThreadListModel::sort() {
+  std::sort(list_.begin(), list_.end(), sort_function_);
+  do_after_sort();
+}
 
+void ThreadListModel::do_after_sort() {
+  // for informing to the tree view.
   std::vector<int> new_order;
   new_order.reserve(order_.size());
 
-  BOOST_FOREACH(const ModelColumns& record, list_) {
+  // set new order.
+  for (size_t i = 0; i != list_.size(); i++) {
     const model_column::ID::type& id =
-      model_column::field<model_column::ID>(record);
-    new_order.push_back(old_order[id]);
+      model_column::field<model_column::ID>(list_[i]);
+    new_order.push_back(order_[id]);
+    order_[id] = i;
   }
 
+  // inform new order to the tree view.
   if (!new_order.empty())
     rows_reordered(Gtk::TreeModel::Path(), &(new_order[0]));
 }
index 03ea42b..4c6eead 100644 (file)
@@ -30,6 +30,8 @@
 #include <boost/mpl/inherit.hpp>
 #include <boost/mpl/inherit_linearly.hpp>
 #include <boost/array.hpp>
+#include <boost/integer_traits.hpp>
+#include <boost/function.hpp>
 #include <string>
 #include <vector>
 
@@ -78,6 +80,101 @@ struct ModelColumns : public boost::mpl::inherit_linearly<
   typedef model_column::List type;
 };
 
+namespace {
+template <typename TypeHolderT, typename ValueType> struct DoCompare;
+template <typename TypeHolderT, typename ValueType> struct DoCompareReverse;
+
+template <typename TypeHolderT>
+struct DoCompare<TypeHolderT, std::string> :
+  public std::binary_function<ModelColumns, ModelColumns, bool> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    typedef typename TypeHolderT::type ValueType;
+    const ValueType& left = model_column::field<TypeHolderT>(lhs);
+    const ValueType& right = model_column::field<TypeHolderT>(rhs);
+
+    if (left.empty() && right.empty()) return false;
+    if (left.empty()) return false;
+    if (right.empty()) return true;
+
+    return left < right;
+  }
+};
+
+template <typename TypeHolderT>
+struct DoCompareReverse<TypeHolderT, std::string> :
+  public std::binary_function<ModelColumns, ModelColumns, bool> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    typedef typename TypeHolderT::type ValueType;
+    const ValueType& left = model_column::field<TypeHolderT>(lhs);
+    const ValueType& right = model_column::field<TypeHolderT>(rhs);
+
+    if (left.empty() && right.empty()) return false;
+    if (left.empty()) return false;
+    if (right.empty()) return true;
+
+    return left > right;
+  }
+};
+
+template <typename ColumnType>
+struct DoCompare<ColumnType, int> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    int left_num = model_column::field<ColumnType>(lhs);
+    int right_num = model_column::field<ColumnType>(rhs);
+
+    if (left_num == 0) left_num = boost::integer_traits<int>::const_max;
+    if (right_num == 0) right_num = boost::integer_traits<int>::const_max;
+
+    return left_num < right_num;
+  }
+};
+template <typename ColumnType>
+struct DoCompareReverse<ColumnType, int> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    int left_num = model_column::field<ColumnType>(lhs);
+    int right_num = model_column::field<ColumnType>(rhs);
+
+    if (left_num == 0) left_num = boost::integer_traits<int>::const_min;
+    if (right_num == 0) right_num = boost::integer_traits<int>::const_min;
+
+    return left_num > right_num;
+  }
+};
+
+template <typename ColumnType>
+struct DoCompare<ColumnType, double> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    double left_num = model_column::field<ColumnType>(lhs);
+    double right_num = model_column::field<ColumnType>(rhs);
+
+    if (left_num == 0) left_num = boost::integer_traits<int>::const_max;
+    if (right_num == 0) right_num = boost::integer_traits<int>::const_max;
+
+    return left_num < right_num;
+  }
+};
+template <typename ColumnType>
+struct DoCompareReverse<ColumnType, double> {
+  bool operator()(const ModelColumns& lhs, const ModelColumns& rhs) const {
+    double left_num = model_column::field<ColumnType>(lhs);
+    double right_num = model_column::field<ColumnType>(rhs);
+
+    if (left_num == 0) left_num = boost::integer_traits<int>::const_min;
+    if (right_num == 0) right_num = boost::integer_traits<int>::const_min;
+
+    return left_num > right_num;
+  }
+};
+
+template <typename TypeHolderT>
+struct Compare : public DoCompare<TypeHolderT, typename TypeHolderT::type> {};
+
+template <typename TypeHolderT>
+struct CompareReverse :
+  public DoCompareReverse<TypeHolderT, typename TypeHolderT::type> {};
+
+}
+
 
 class ThreadListModel : public Glib::Object, public Gtk::TreeModel {
 public:
@@ -91,6 +188,8 @@ public:
   void get_buffer(boost::unordered_map<model_column::ID::type, ModelColumns>& buffer) const;
   const ModelColumns& get_model_columns(size_t row_index) const;
   void update_row(const ModelColumns& record);
+  template <typename TypeHolderT> void set_sort_function(bool ascendant);
+  void sort();
 protected:
   ThreadListModel();
 
@@ -114,14 +213,24 @@ protected:
   virtual bool iter_parent_vfunc(const iterator& child, iterator& iter) const;
 
 private:
+  void do_after_sort();
 
   typedef std::vector<ModelColumns> StoreType;
   typedef boost::unordered_map<model_column::ID::type, size_t> OrderType;
 
   StoreType list_;
   OrderType order_;
+
+  boost::function<bool (const ModelColumns&, const ModelColumns&)>
+    sort_function_;
 };
 
+template <typename TypeHolderT>
+void ThreadListModel::set_sort_function(bool ascendant) {
+  if (ascendant) sort_function_ = Compare<TypeHolderT>();
+  else sort_function_ = CompareReverse<TypeHolderT>();
+}
+
 
 } // namespace dialektos