2 * Copyright (C) 2009 by Aiwota Programmer
3 * aiwotaprog@tetteke.tk
5 * This file is part of Dialektos.
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.
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.
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/>.
21 #include "thread_window.hxx"
23 #include <glibmm/convert.h>
24 #include <gtkmm/stock.h>
25 #include <boost/date_time/posix_time/posix_time.hpp>
26 #include <boost/filesystem.hpp>
27 #include <boost/foreach.hpp>
28 #include <boost/format.hpp>
33 #include "bbs_detail_base.hxx"
34 #include "thread_idx.hxx"
35 #include "http_get.hxx"
37 #include "uri_opener.hxx"
38 #include "thread_window_state.hxx"
39 #include "board_window.hxx"
45 void ThreadWindow::create(std::auto_ptr<bbs_detail::Base> bbs) {
46 regist(new ThreadWindow(bbs));
49 ThreadWindow::ThreadWindow(std::auto_ptr<bbs_detail::Base> bbs) :
51 text_view_(new text_view::TextView),
52 scrolled_(text_view_->get_adjustment()),
53 bbs_(bbs), http_getter_(), idx_() {
55 // additional menuitems for board window
56 action_group_->add(Gtk::Action::create("FileDelete", Gtk::Stock::DELETE),
57 sigc::mem_fun(*this, &ThreadWindow::on_action_file_delete));
59 Gtk::Action::create("FileBoard", Gtk::Stock::GO_UP, "Show _Board"),
60 sigc::mem_fun(*this, &ThreadWindow::on_action_file_board));
63 "FileCloseRemoveHistory", Gtk::Stock::CLEAR, "CloseH",
64 "Close and Remove from History"),
66 &ThreadWindow::on_action_file_close_remove_history));
70 " <menubar name='MenuBar'>"
71 " <menu action='MenuFile'>"
72 " <menuitem action='FileDelete'/>"
73 " <menuitem action='FileCloseRemoveHistory'/>"
75 " <menuitem action='FileBoard'/>"
78 " <toolbar name='ToolBar'>"
79 " <toolitem action='FileCloseRemoveHistory'/>"
83 ui_manager_->add_ui_from_string(ui);
86 initialize_text_view();
88 ThreadWindowState state;
89 state.from_xml(boost::filesystem::path(bbs_->get_thread_state_path()));
90 set_default_size(state.width, state.height);
91 if (state.menubar) menubar_->show();
92 if (state.toolbar) toolbar_->show();
93 if (state.statusbar) statusbar_.show();
98 text_view_->jump_to_res_num(state.displayed_res_num);
101 void ThreadWindow::initialize_text_view() {
102 scrolled_.add(*text_view_);
104 text_view_->signal_button_press_event().connect_notify(
105 sigc::mem_fun(*this, &ThreadWindow::on_child_button_press));
106 text_view_->signal_uri_clicked().connect(
107 sigc::mem_fun(*this, &ThreadWindow::on_uri_clicked));
112 bool ThreadWindow::load() {
113 using namespace boost::posix_time;
114 ptime start = microsec_clock::local_time();
118 bbs_->load_thread(*text_view_);
119 text_view_->relayout();
120 text_view_->queue_draw();
123 to_simple_string(microsec_clock::local_time() - start) << std::endl;
125 idx_ = ThreadIdx::from_xml(bbs_->get_thread_idx_path());
126 set_title(idx_.title_);
127 histories.push(bbs_->get_thread_uri(), idx_.title_);
129 if (text_view_->get_res_num() == 0) on_action_view_refresh();
135 bool ThreadWindow::is_same(const bbs_detail::Base& bbs) const {
136 const bbs_detail::Base& lhs = *bbs_;
140 std::string ThreadWindow::get_uri() const {
141 return bbs_->get_thread_uri();
145 void ThreadWindow::on_action_view_refresh() {
146 if (http_getter_) return;
148 statusbar_.push("HTTP/1.0 GET...");
150 const std::string uri = bbs_->get_thread_dat_uri();
151 http::Header request_header = bbs_->get_thread_dat_request_header();
152 if (!request_header.get_range().empty()) {
153 request_header.set_if_modified_since(idx_.last_modified_);
154 request_header.set_if_none_match(idx_.etag_);
157 http_getter_.reset(new http::GetInThread(uri, request_header));
158 http_getter_->signal_end().connect(
159 sigc::mem_fun(*this, &ThreadWindow::on_http_get_end));
163 void ThreadWindow::on_action_view_stop() {
164 if (http_getter_) http_getter_->cancel();
167 void ThreadWindow::on_action_edit_copy() {
168 const Glib::ustring selected = text_view_->get_selected_text();
169 Gtk::Clipboard::get()->set_text(selected);
172 void ThreadWindow::on_action_file_delete() {
173 const boost::filesystem::path dat(bbs_->get_thread_file_path());
175 if (boost::filesystem::exists(dat)) boost::filesystem::remove(dat);
176 } catch (const boost::filesystem::filesystem_error& e) {
177 std::cerr << e.what() << std::endl;
180 const boost::filesystem::path xml(bbs_->get_thread_idx_path());
182 if (boost::filesystem::exists(xml)) boost::filesystem::remove(xml);
183 } catch (const boost::filesystem::filesystem_error& e) {
184 std::cerr << e.what() << std::endl;
188 void ThreadWindow::on_action_file_board() {
189 uri_opener::open(bbs_->get_board_uri());
192 void ThreadWindow::on_action_file_close_remove_history() {
193 histories.erase(bbs_->get_thread_uri());
195 std::vector<ApplicationWindow*> closes;
196 closes.push_back(this);
197 close_windows(closes);
200 void ThreadWindow::on_http_get_end(bool success) {
201 // const std::string uri = http_getter_->get_uri();
202 // const http::Header request_header = http_getter_->get_request_header();
203 const http::Response response = http_getter_->get_response();
204 const boost::system::error_code err = http_getter_->get_error();
205 http_getter_.reset(0);
207 statusbar_.push(err.message());
211 statusbar_.push("Canceled.");
215 on_refresh_end(response.get_status_line(), response.get_header());
217 const int code = response.get_status_line().get_code();
218 if (code != 200 && code != 206) return;
221 // initialize text_view_
222 scrolled_.remove(*text_view_);
223 text_view_.reset(new text_view::TextView);
224 scrolled_.set_adjustment(text_view_->get_adjustment());
226 initialize_text_view();
229 bbs_->load_thread_from_string(response.get_content(), *text_view_);
230 text_view_->relayout();
231 text_view_->queue_draw();
233 save_content(response);
234 inform_to_board(idx_);
237 void ThreadWindow::on_refresh_end(const http::StatusLine& status,
238 const http::Header& header) {
239 std::string message = status.get_line();
240 const std::string last_modified = header.get_last_modified();
241 if (!last_modified.empty()) {
243 message += last_modified;
245 statusbar_.push(message);
248 void ThreadWindow::save_content(const http::Response& response) {
249 const int code = response.get_status_line().get_code();
250 if (code != 200 && code != 206) return;
252 if (!misc::create_directories(boost::filesystem::path(
253 bbs_->get_thread_file_path()).parent_path())) return;
254 std::ofstream ofs(bbs_->get_thread_file_path().c_str(),
256 std::ios::out | std::ios::trunc : std::ios::out | std::ios::app);
257 ofs << response.get_content();
261 idx_.title_ = bbs_->get_title_from_string(response.get_content());
262 set_title(idx_.title_);
263 histories.push(bbs_->get_thread_uri(), idx_.title_);
266 idx_.last_modified_ = response.get_header().get_last_modified();
267 idx_.etag_ = response.get_header().get_etag();
269 idx_.line_count_ = text_view_->get_res_num();
271 idx_.to_xml(boost::filesystem::path(bbs_->get_thread_idx_path()));
274 void ThreadWindow::save_state() const {
275 ThreadWindowState state;
276 state.width = get_width();
277 state.height = get_height();
278 state.menubar = menubar_->is_visible();
279 state.toolbar = toolbar_->is_visible();
280 state.statusbar = statusbar_.is_visible();
281 state.displayed_res_num = text_view_->get_displayed_res_num();
282 state.to_xml(boost::filesystem::path(bbs_->get_thread_state_path()));
285 void ThreadWindow::on_informed_from_board(const int res_num) {
287 statusbar_.push("Dropped.");
288 } else if (res_num > idx_.line_count_) {
289 std::stringstream ss;
290 ss << boost::format("Update. %1% -> %2%") % idx_.line_count_ % res_num;
291 statusbar_.push(ss.str());
296 void ThreadWindow::inform_to_board(const ThreadIdx& idx) const {
297 BOOST_FOREACH(ApplicationWindow& window, windows) {
298 if (BoardWindow* board_window = dynamic_cast<BoardWindow*>(&window)) {
299 board_window->on_informed_from(*bbs_, idx);
304 void ThreadWindow::on_uri_clicked(const std::string& uri) const {
305 if (uri_opener::open(uri)) return;
306 std::stringstream ss;
307 ss << boost::format("firefox %1%") % uri;
308 std::system(ss.str().c_str());
311 } // namespace dialektos