#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 {
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,
}
-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
#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"
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"; };
};
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());
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()));
#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 {
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_;
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
#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 {
} // 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());
ThreadListModel::ThreadListModel():
Glib::ObjectBase(typeid(ThreadListModel)),
Glib::Object(),
- list_(), order_() {
+ list_(), order_(), sort_function_(Compare<model_column::Number>()) {
}
ThreadListModel::~ThreadListModel() {}
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]));
}
#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>
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:
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();
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