OSDN Git Service

HTTP GET method is implemented. It is used in board window.
[fukui-no-namari/dialektos.git] / src / board_window.cxx
1 /*
2  * Copyright (C) 2009 by Aiwota Programmer
3  * aiwotaprog@tetteke.tk
4  *
5  * This file is part of Dialektos.
6  *
7  * Dialektos is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Dialektos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "board_window.hxx"
22
23 #include <glibmm/ustring.h>
24 #include <glibmm/refptr.h>
25 #include <sigc++/functors/mem_fun.h>
26 #include <sigc++/signal.h>
27
28 #include <boost/date_time/posix_time/posix_time.hpp>
29 #include <boost/lexical_cast.hpp>
30 #include <boost/filesystem.hpp>
31 #include <boost/format.hpp>
32 #include <boost/bind.hpp>
33 #include <boost/foreach.hpp>
34 #include <boost/mpl/find.hpp>
35
36 #include <string>
37 #include <fstream>
38 #include <iostream>
39 #include <sstream>
40
41 #include "bbs_detail_base.hxx"
42 #include "thread_list_model.hxx"
43 #include "board_view_column.hxx"
44 #include "board_subject_item.hxx"
45 #include "thread_idx_cache.hxx"
46 #include "uri_opener.hxx"
47 #include "http_get.hxx"
48 #include "board_subject_idx.hxx"
49 #include "http_date.hxx"
50
51
52 namespace dialektos {
53
54
55 void BoardWindow::create(std::auto_ptr<bbs_detail::Base> bbs) {
56   regist(new BoardWindow(bbs));
57 }
58
59 BoardWindow::BoardWindow(std::auto_ptr<bbs_detail::Base> bbs) :
60   ApplicationFrameWork(), tree_view_(), bbs_(bbs), scrolled_(),
61   tree_model_(ThreadListModel::create()),
62   http_getter_()
63   {
64
65   // additional menuitems for board window
66   action_group_->add(Gtk::Action::create("FileOpen", "_Open Thread"),
67       sigc::mem_fun(*this, &BoardWindow::on_action_file_open));
68
69   Glib::ustring ui =
70     "<ui>"
71     "  <menubar name='MenuBar'>"
72     "    <menu action='MenuFile'>"
73     "      <menuitem action='FileOpen'/>"
74     "    </menu>"
75     "  </menubar>"
76     "  <popup name='MenuPopup'>"
77     "    <menuitem action='FileOpen'/>"
78     "  </popup>"
79     "</ui>";
80
81   ui_manager_->add_ui_from_string(ui);
82
83
84   tree_view_.signal_row_activated().connect(
85       sigc::mem_fun(*this, &BoardWindow::on_row_activated));
86   tree_view_.signal_button_press_event().connect_notify(
87       sigc::mem_fun(*this, &BoardWindow::on_child_button_press));
88
89   set_default_size(400,300);
90
91   scrolled_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
92   add(scrolled_);
93   scrolled_.add(tree_view_);
94
95   tree_view_.set_model(tree_model_);
96   tree_view_.set_fixed_height_mode(true);
97   tree_view_.set_rules_hint(true);
98
99   tree_view_.append_column(*Gtk::manage(new view_column::Number));
100   tree_view_.append_column(*Gtk::manage(new view_column::Title));
101   tree_view_.append_column(*Gtk::manage(new view_column::ResNum));
102   tree_view_.append_column(*Gtk::manage(new view_column::LineCount));
103   tree_view_.append_column(*Gtk::manage(new view_column::Average));
104   tree_view_.append_column(*Gtk::manage(new view_column::StartTime));
105
106   using namespace boost::posix_time;
107   ptime start = microsec_clock::local_time();
108
109   load();
110
111   std::cout <<
112   to_simple_string(microsec_clock::local_time() - start) << std::endl;
113
114   show_all();
115 }
116
117 BoardWindow::~BoardWindow() {
118 }
119
120 void BoardWindow::on_row_activated(const Gtk::TreeModel::Path& path,
121     Gtk::TreeViewColumn* /*col*/) {
122   Gtk::TreeIter iter = tree_model_->get_iter(path);
123   std::string id;
124   iter->get_value(boost::mpl::find<model_column::List,
125       model_column::ID>::type::pos::value, id);
126
127   const std::string uri = bbs_->get_another_thread_uri(id);
128   uri_opener::open(uri);
129 }
130
131 bool BoardWindow::is_same(const bbs_detail::Base& bbs) const {
132   return *bbs_ == bbs;
133 }
134
135 void BoardWindow::load() {
136   boost::unordered_map<model_column::ID::type, ModelColumns> buffer;
137
138   std::vector<ThreadIdxCache> caches;
139
140   const boost::filesystem::path dir(bbs_->get_board_idx_dir_path());
141   if (boost::filesystem::exists(dir) && boost::filesystem::is_directory(dir))
142     caches = dialektos::ThreadIdxCache::from_directory(dir);
143
144   BOOST_FOREACH(const ThreadIdxCache& cache, caches) {
145     ModelColumns cols;
146     model_column::field<model_column::Title>(cols) = cache.title_;
147     model_column::field<model_column::ID>(cols) = cache.id_;
148     model_column::field<model_column::LineCount>(cols) = cache.line_count_;
149     buffer[cache.id_] = cols;
150   }
151
152   const boost::filesystem::path sbj(bbs_->get_board_subject_path());
153   if (boost::filesystem::exists(sbj) && boost::filesystem::is_regular(sbj)) {
154     std::vector<SubjectItem> subjects;
155     bbs_->load_subject(subjects);
156
157     idx_ = SubjectIdx::from_xml(
158         boost::filesystem::path(bbs_->get_board_subject_idx_path()));
159
160     merge_subject_txt(subjects, buffer);
161   }
162
163   tree_model_->set_buffer(buffer);
164 }
165
166 void BoardWindow::merge_subject_txt(
167     const std::vector<SubjectItem>& subjects,
168     boost::unordered_map<std::string, ModelColumns>& buffer) {
169
170   boost::posix_time::ptime last_modified;
171   try {
172     last_modified = rfc1123_to_ptime(idx_.last_modified_);
173   } catch (const HTTPDateError& e) {
174     std::cerr << e.what() << std::endl;
175     last_modified = boost::posix_time::second_clock::universal_time();
176   }
177
178   BOOST_FOREACH(const SubjectItem& item, subjects) {
179     ModelColumns& cols = buffer[item.id_];
180     model_column::field<model_column::Number>(cols) = item.number_;
181     model_column::field<model_column::Title>(cols) = item.title_;
182     model_column::field<model_column::ID>(cols) = item.id_;
183     model_column::field<model_column::ResNum>(cols) = item.res_num_;
184
185     try {
186       const std::time_t _start = boost::lexical_cast<std::time_t>(item.id_);
187       const boost::posix_time::ptime start = boost::posix_time::from_time_t(_start);
188       const boost::posix_time::ptime curr = last_modified;
189       const boost::posix_time::time_duration dur = curr - start;
190       const double sec = dur.total_seconds();
191       const double average = item.res_num_ / (sec/60.0/60.0/24.0);
192       model_column::field<model_column::Average>(cols) = average;
193     } catch(const boost::bad_lexical_cast& e) {
194       std::cerr << e.what() << "(" << item.id_ << ")" << std::endl;
195     }
196   }
197 }
198
199 void BoardWindow::unmerge_subject_txt(
200     boost::unordered_map<std::string, ModelColumns>& buffer) {
201   // remove thread ids which line_count_ is zero.
202   std::list<std::string> remove_list;
203   typedef boost::unordered_map<std::string, ModelColumns>::value_type PairType;
204   BOOST_FOREACH(PairType& pair, buffer) {
205     ModelColumns& cols = pair.second;
206     if (model_column::field<model_column::LineCount>(cols) == 0)
207       remove_list.push_back(pair.first);
208     else if (model_column::field<model_column::Number>(cols) != 0) {
209       model_column::field<model_column::Number>(cols) = 0;
210       model_column::field<model_column::ResNum>(cols) = 0;
211       model_column::field<model_column::Average>(cols) = 0;
212     }
213   }
214   BOOST_FOREACH(const std::string& id, remove_list)
215     buffer.erase(id);
216 }
217
218 void BoardWindow::on_action_file_open() {
219   std::cout << "file open activated" << std::endl;
220 }
221
222 void BoardWindow::on_action_view_refresh() {
223   if (http_getter_) return;
224
225   statusbar_.push("HTTP/1.0 GET...");
226
227   const std::string uri = bbs_->get_board_subject_uri();
228   http::Header request_header = bbs_->get_board_subject_request_header();
229
230   SubjectIdx idx = SubjectIdx::from_xml(
231       boost::filesystem::path(bbs_->get_board_subject_idx_path()));
232   if (!idx.last_modified_.empty())
233     request_header.insert(
234         std::make_pair("If-Modified-Since", idx.last_modified_));
235
236   http_getter_.reset(new http::GetInThread(uri, request_header));
237   http_getter_->signal_end().connect(
238       sigc::mem_fun(*this, &BoardWindow::on_http_get_end));
239   http_getter_->run();
240 }
241
242 void BoardWindow::save_content(const http::Response& response) {
243   // save the content to the file subject.txt
244   std::ofstream ofs(bbs_->get_board_subject_path().c_str());
245   ofs << response.get_content();
246   ofs.close();
247
248   // save the metadata to the file subject.xml
249   const http::Header& header = response.get_header();
250   http::Header::const_iterator it = header.find("Last-Modified");
251   if (it != header.end()) {
252     SubjectIdx idx = idx_;
253     idx.last_modified_ = it->second;
254     idx.to_xml(boost::filesystem::path(bbs_->get_board_subject_idx_path()));
255     idx_ = idx;
256   }
257 }
258
259 void BoardWindow::on_http_get_end(bool success) {
260 //  const std::string uri = http_getter_->get_uri();
261 //  const http::Header request_header = http_getter_->get_request_header();
262   const http::Response response = http_getter_->get_response();
263
264   http_getter_.reset(0);
265   on_refresh_end(response.get_status_line(), response.get_header());
266
267   std::vector<SubjectItem> subjects;
268   bbs_->load_subject_from_string(response.get_content(), subjects);
269
270   if (subjects.empty()) return;
271
272   save_content(response);
273
274   boost::unordered_map<std::string, ModelColumns> buffer;
275   tree_model_->get_buffer(buffer);
276   unmerge_subject_txt(buffer);
277   merge_subject_txt(subjects, buffer);
278   tree_model_->set_buffer(buffer);
279 }
280
281 void BoardWindow::on_refresh_end(const http::StatusLine& status,
282     const http::Header& header) {
283   std::string message = status.get_line();
284   http::Header::const_iterator it = header.find("Last-Modified");
285   if (it != header.end()) {
286     message += " ";
287     message += it->second;
288   }
289   statusbar_.push(message);
290 }
291
292 } // namespace dialektos