#include <glibmm/ustring.h>
#include <glibmm/refptr.h>
+#include <sigc++/functors/mem_fun.h>
+#include <sigc++/signal.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include "board_subject_item.hxx"
#include "thread_idx_cache.hxx"
#include "uri_opener.hxx"
+#include "http_get.hxx"
+#include "board_subject_idx.hxx"
+#include "http_date.hxx"
+#include "misc.hxx"
namespace dialektos {
BoardWindow::BoardWindow(std::auto_ptr<bbs_detail::Base> bbs) :
ApplicationFrameWork(), tree_view_(), bbs_(bbs), scrolled_(),
- tree_model_(ThreadListModel::create()) {
+ tree_model_(ThreadListModel::create()),
+ http_getter_()
+ {
+
+ // additional menuitems for board window
+ action_group_->add(Gtk::Action::create("FileOpen", "_Open Thread"),
+ sigc::mem_fun(*this, &BoardWindow::on_action_file_open));
+
+ Glib::ustring ui =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='FileOpen'/>"
+ " </menu>"
+ " </menubar>"
+ " <popup name='MenuPopup'>"
+ " <menuitem action='FileOpen'/>"
+ " </popup>"
+ "</ui>";
+
+ ui_manager_->add_ui_from_string(ui);
+
tree_view_.signal_row_activated().connect(
sigc::mem_fun(*this, &BoardWindow::on_row_activated));
+ tree_view_.signal_button_press_event().connect_notify(
+ sigc::mem_fun(*this, &BoardWindow::on_child_button_press));
set_default_size(400,300);
std::cout <<
to_simple_string(microsec_clock::local_time() - start) << std::endl;
+ set_title(get_uri());
+
show_all();
}
uri_opener::open(uri);
}
+void BoardWindow::on_action_view_stop() {
+ if (http_getter_) http_getter_->cancel();
+}
+
bool BoardWindow::is_same(const bbs_detail::Base& bbs) const {
return *bbs_ == bbs;
}
+std::string BoardWindow::get_uri() const {
+ return bbs_->get_board_uri();
+}
+
+
void BoardWindow::load() {
boost::unordered_map<model_column::ID::type, ModelColumns> buffer;
std::vector<ThreadIdxCache> caches;
const boost::filesystem::path dir(bbs_->get_board_idx_dir_path());
- if (boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir))
+ if (boost::filesystem::exists(dir) && boost::filesystem::is_directory(dir))
caches = dialektos::ThreadIdxCache::from_directory(dir);
BOOST_FOREACH(const ThreadIdxCache& cache, caches) {
buffer[cache.id_] = cols;
}
- merge_subject_txt(buffer);
+ const boost::filesystem::path sbj(bbs_->get_board_subject_path());
+ if (boost::filesystem::exists(sbj) && boost::filesystem::is_regular(sbj)) {
+ std::vector<SubjectItem> subjects;
+ bbs_->load_subject(subjects);
+
+ idx_ = SubjectIdx::from_xml(
+ boost::filesystem::path(bbs_->get_board_subject_idx_path()));
+
+ merge_subject_txt(subjects, buffer);
+ }
+
tree_model_->set_buffer(buffer);
}
void BoardWindow::merge_subject_txt(
+ const std::vector<SubjectItem>& subjects,
boost::unordered_map<std::string, ModelColumns>& buffer) {
- const boost::filesystem::path sbj(bbs_->get_board_subject_path());
-
- std::vector<SubjectItem> subjects;
- bbs_->load_subject(subjects);
- const std::time_t last_modified = boost::filesystem::last_write_time(sbj);
+ boost::posix_time::ptime last_modified;
+ try {
+ last_modified = rfc1123_to_ptime(idx_.last_modified_);
+ } catch (const HTTPDateError& e) {
+ std::cerr << e.what() << std::endl;
+ last_modified = boost::posix_time::second_clock::universal_time();
+ }
BOOST_FOREACH(const SubjectItem& item, subjects) {
ModelColumns& cols = buffer[item.id_];
model_column::field<model_column::ID>(cols) = item.id_;
model_column::field<model_column::ResNum>(cols) = item.res_num_;
- const std::time_t _start = boost::lexical_cast<std::time_t>(item.id_);
- const boost::posix_time::ptime start = boost::posix_time::from_time_t(_start);
- const boost::posix_time::ptime curr = boost::posix_time::from_time_t(last_modified);
- const boost::posix_time::time_duration dur = curr - start;
- const double sec = dur.total_seconds();
- const double average = item.res_num_ / (sec/60.0/60.0/24.0);
- model_column::field<model_column::Average>(cols) = average;
+ try {
+ const std::time_t _start = boost::lexical_cast<std::time_t>(item.id_);
+ const boost::posix_time::ptime start = boost::posix_time::from_time_t(_start);
+ const boost::posix_time::ptime curr = last_modified;
+ const boost::posix_time::time_duration dur = curr - start;
+ const double sec = dur.total_seconds();
+ const double average = item.res_num_ / (sec/60.0/60.0/24.0);
+ model_column::field<model_column::Average>(cols) = average;
+ } catch(const boost::bad_lexical_cast& e) {
+ std::cerr << e.what() << "(" << item.id_ << ")" << std::endl;
+ }
+ }
+}
+
+void BoardWindow::unmerge_subject_txt(
+ boost::unordered_map<std::string, ModelColumns>& buffer) {
+ // remove thread ids which line_count_ is zero.
+ std::list<std::string> remove_list;
+ typedef boost::unordered_map<std::string, ModelColumns>::value_type PairType;
+ BOOST_FOREACH(PairType& pair, buffer) {
+ ModelColumns& cols = pair.second;
+ if (model_column::field<model_column::LineCount>(cols) == 0)
+ remove_list.push_back(pair.first);
+ else if (model_column::field<model_column::Number>(cols) != 0) {
+ model_column::field<model_column::Number>(cols) = 0;
+ model_column::field<model_column::ResNum>(cols) = 0;
+ model_column::field<model_column::Average>(cols) = 0;
+ }
+ }
+ BOOST_FOREACH(const std::string& id, remove_list)
+ buffer.erase(id);
+}
+
+void BoardWindow::on_action_file_open() {
+ std::cout << "file open activated" << std::endl;
+}
+
+void BoardWindow::on_action_view_refresh() {
+ if (http_getter_) return;
+
+ statusbar_.push("HTTP/1.0 GET...");
+
+ const std::string uri = bbs_->get_board_subject_uri();
+ http::Header request_header = bbs_->get_board_subject_request_header();
+
+ SubjectIdx idx = SubjectIdx::from_xml(
+ boost::filesystem::path(bbs_->get_board_subject_idx_path()));
+ request_header.set_if_modified_since(idx.last_modified_);
+
+ http_getter_.reset(new http::GetInThread(uri, request_header));
+ http_getter_->signal_end().connect(
+ sigc::mem_fun(*this, &BoardWindow::on_http_get_end));
+ http_getter_->run();
+}
+
+void BoardWindow::save_content(const http::Response& response) {
+ // save the content to the file subject.txt
+ if (!misc::create_directories(boost::filesystem::path(
+ bbs_->get_board_subject_path()).parent_path())) return;
+ std::ofstream ofs(bbs_->get_board_subject_path().c_str());
+ ofs << response.get_content();
+ ofs.close();
+
+ // save the metadata to the file subject.xml
+ idx_.last_modified_ = response.get_header().get_last_modified();
+ idx_.to_xml(boost::filesystem::path(bbs_->get_board_subject_idx_path()));
+}
+
+void BoardWindow::on_http_get_end(bool success) {
+// const std::string uri = http_getter_->get_uri();
+// const http::Header request_header = http_getter_->get_request_header();
+ const http::Response response = http_getter_->get_response();
+ const boost::system::error_code err = http_getter_->get_error();
+ http_getter_.reset(0);
+ if (err) {
+ statusbar_.push(err.message());
+ return;
+ }
+ if (!success) {
+ statusbar_.push("Canceled.");
+ return;
}
+
+ on_refresh_end(response.get_status_line(), response.get_header());
+
+ std::vector<SubjectItem> subjects;
+ bbs_->load_subject_from_string(response.get_content(), subjects);
+
+ if (subjects.empty()) return;
+
+ save_content(response);
+
+ boost::unordered_map<std::string, ModelColumns> buffer;
+ tree_model_->get_buffer(buffer);
+ unmerge_subject_txt(buffer);
+ merge_subject_txt(subjects, buffer);
+ tree_model_->set_buffer(buffer);
}
+void BoardWindow::on_refresh_end(const http::StatusLine& status,
+ const http::Header& header) {
+ std::string message = status.get_line();
+ const std::string last_modified = header.get_last_modified();
+ if (!last_modified.empty()) {
+ message += " ";
+ message += last_modified;
+ }
+ statusbar_.push(message);
+}
} // namespace dialektos