#include <gtkmm/uimanager.h>
#include <gtkmm/actiongroup.h>
#include <iostream>
+#include "bookmark_window.hxx"
namespace dialektos {
action_group_->add(Gtk::Action::create("ViewStatusbar", "Toggle _Statusbar"),
sigc::mem_fun(*this, &ApplicationFrameWork::on_action_view_statusbar));
+ // Bookmark menu
+ action_group_->add(Gtk::Action::create("MenuBookmark", "_Bookmark"));
+ action_group_->add(Gtk::Action::create("BookmarkShow", "_Show Bookmarks"),
+ sigc::mem_fun(*this, &ApplicationFrameWork::on_action_bookmark_show));
+
ui_manager_->insert_action_group(action_group_);
add_accel_group(ui_manager_->get_accel_group());
" <menuitem action='ViewToolbar'/>"
" <menuitem action='ViewStatusbar'/>"
" </menu>"
+ " <menu action='MenuBookmark'>"
+ " <menuitem action='BookmarkShow'/>"
+ " </menu>"
" </menubar>"
" <toolbar name='ToolBar'>"
" <toolitem action='ViewRefresh'/>"
if (statusbar_.is_visible()) statusbar_.hide(); else statusbar_.show();
}
+void ApplicationFrameWork::on_action_bookmark_show() {
+ BookmarkWindow::create();
+}
+
void ApplicationFrameWork::on_child_button_press(GdkEventButton* event) {
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
popupmenu_->popup(event->button, event->time);
virtual void on_action_view_menubar();
virtual void on_action_view_toolbar();
virtual void on_action_view_statusbar();
+ virtual void on_action_bookmark_show();
virtual void on_child_button_press(GdkEventButton*);
--- /dev/null
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bookmark_item.hxx"
+
+#include <boost/archive/xml_iarchive.hpp>
+#include <boost/archive/xml_oarchive.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/vector.hpp>
+#include <boost/xpressive/xpressive.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <sstream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include "convert/cp932.hxx"
+
+
+namespace dialektos {
+
+namespace bookmark {
+
+
+std::vector<Item> from_html(const std::string& html) {
+ using namespace boost::xpressive;
+
+ const sregex regex = as_xpr("<A HREF=")
+ >> (s1=as_xpr("http://") >> +~set[as_xpr('>')|as_xpr(' ')])
+ >> *~as_xpr('>') >> '>'
+ >> (s2=+~as_xpr('<')) >> as_xpr("</A>") >> *_;
+ const sregex regex_category = "<BR><BR><B>"
+ >> (s1=+~as_xpr('<')) >> "</B><BR>" >> *_;
+
+ std::vector<Item> list;
+ std::stringstream ss;
+ ss << html;
+ std::string current_category;
+ std::string line;
+ while (std::getline(ss, line)) {
+ boost::algorithm::trim_right(line);
+ const std::string utf8 = convert::cp932(line);
+
+ if (utf8.empty()) {
+ current_category.clear();
+ continue;
+ }
+
+ smatch what;
+
+ if (regex_match(utf8, what, regex_category)) {
+ current_category = what[1];
+ continue;
+ }
+
+ if (regex_match(utf8, what, regex)) {
+ Item item;
+ item.uri = what[1];
+ item.name = what[2];
+ item.categories.push_back("bbsmenu");
+ if (!current_category.empty())
+ item.categories.push_back(current_category);
+ list.push_back(item);
+ }
+ }
+
+ return list;
+}
+
+std::vector<Item> from_xml(const boost::filesystem::path& xml) {
+ std::vector<Item> items;
+
+ if (!boost::filesystem::exists(xml) || !boost::filesystem::is_regular(xml))
+ return items;
+
+ std::ifstream ifs(xml.file_string().c_str());
+ try {
+ boost::archive::xml_iarchive ia(ifs);
+ ia >> boost::serialization::make_nvp("Bookmarks", items);
+ } catch (const boost::archive::archive_exception& e) {
+ std::cerr << "bookmark::from_xml(): " << e.what() << " "
+ << xml << std::endl;
+ }
+ return items;
+}
+
+void to_xml(const boost::filesystem::path& xml,
+ const std::vector<Item>& items) {
+ std::ofstream ofs(xml.file_string().c_str());
+ try {
+ boost::archive::xml_oarchive oa(ofs);
+ oa << boost::serialization::make_nvp("Bookmarks", items);
+ } catch (const boost::archive::archive_exception& e) {
+ std::cerr << "bookmark::to_xml(): " << e.what() << " " << xml << std::endl;
+ }
+}
+
+
+} // namespace bookmark
+
+} // namespace dialektos
--- /dev/null
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BOOKMARK_ITEM_HXX
+#define BOOKMARK_ITEM_HXX
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/vector.hpp>
+#include <boost/serialization/string.hpp>
+#include <boost/filesystem/path.hpp>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+
+namespace dialektos {
+
+namespace bookmark {
+
+
+struct Item {
+ std::string name;
+ std::string uri;
+ std::vector<std::string> categories;
+
+ bool belongs_to(const std::string& category) const {
+ return std::find(categories.begin(), categories.end(), category) !=
+ categories.end();
+ }
+
+private:
+ friend class boost::serialization::access;
+ template <typename ArchiveType>
+ void serialize(ArchiveType& ar, const unsigned int version) {
+ ar & boost::serialization::make_nvp("Name", name);
+ ar & boost::serialization::make_nvp("URI", uri);
+ ar & boost::serialization::make_nvp("Categories", categories);
+ }
+};
+
+std::vector<Item> from_html(const std::string& html);
+std::vector<Item> from_xml(const boost::filesystem::path&);
+void to_xml(const boost::filesystem::path&, const std::vector<Item>&);
+
+} // namespace bookmark
+
+} // namespace dialektos
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bookmark_window.hxx"
+
+#include <sigc++/functors/mem_fun.h>
+#include <gtkmm/stock.h>
+#include <boost/lambda/lambda.hpp>
+#include <boost/foreach.hpp>
+#include <boost/filesystem/path.hpp>
+#include <iostream>
+#include <vector>
+#include <cstdlib>
+#include "http_get.hxx"
+#include "http_header.hxx"
+#include "http_response.hxx"
+#include "bookmark_item.hxx"
+#include "uri_opener.hxx"
+
+
+namespace dialektos {
+
+
+boost::scoped_ptr<BookmarkWindow> BookmarkWindow::single_window;
+const std::string BookmarkWindow::filename = "bookmarks.xml";
+
+
+void BookmarkWindow::create() {
+ if (single_window) single_window->present();
+ else single_window.reset(new BookmarkWindow);
+}
+
+
+BookmarkWindow::BookmarkWindow() : Gtk::Window(),
+vbox_(), hpaned_(), scrolled1_(), scrolled2_(),
+view_category_(), view_bookmark_(), statusbar_(),
+category_column_record_(), bookmark_column_record_(),
+category_list_store_(Gtk::ListStore::create(category_column_record_)),
+bookmark_list_store_(Gtk::ListStore::create(bookmark_column_record_)),
+ui_manager_(Gtk::UIManager::create()),
+action_group_(Gtk::ActionGroup::create()),
+menubar_(0), toolbar_(0), popupmenu_(0),
+http_getter_(),
+bookmarks_() {
+
+ build_menu();
+
+ set_default_size(250,300);
+
+ add(vbox_);
+ vbox_.pack_start(*menubar_, Gtk::PACK_SHRINK);
+ vbox_.pack_start(*toolbar_, Gtk::PACK_SHRINK);
+ vbox_.pack_start(hpaned_);
+ vbox_.pack_start(statusbar_, Gtk::PACK_SHRINK);
+
+ hpaned_.pack1(scrolled1_, Gtk::SHRINK);
+ hpaned_.pack2(scrolled2_);
+ hpaned_.set_position(100);
+
+ scrolled1_.add(view_category_);
+ scrolled2_.add(view_bookmark_);
+
+ scrolled1_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scrolled2_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ view_category_.set_model(category_list_store_);
+ view_category_.append_column("Category", category_column_record_.category_);
+
+ view_bookmark_.set_model(bookmark_list_store_);
+ view_bookmark_.append_column("Bookmark", bookmark_column_record_.name_);
+
+ view_category_.signal_cursor_changed().connect(
+ sigc::mem_fun(*this, &BookmarkWindow::on_category_cursor_changed));
+ view_category_.signal_row_activated().connect(
+ sigc::mem_fun(*this, &BookmarkWindow::on_category_row_activated));
+ view_bookmark_.signal_row_activated().connect(
+ sigc::mem_fun(*this, &BookmarkWindow::on_bookmark_row_activated));
+
+ bookmarks_ = bookmark::from_xml(get_xml_path());
+ reconstruct_category();
+
+ show_all();
+}
+
+BookmarkWindow::~BookmarkWindow() {
+}
+
+bool BookmarkWindow::on_delete_event(GdkEventAny*) {
+ single_window.reset();
+ return false;
+}
+
+void BookmarkWindow::build_menu() {
+ // File menu
+ action_group_->add(Gtk::Action::create("MenuFile", "_File"));
+
+ // View menu
+ action_group_->add(Gtk::Action::create("MenuView", "_View"));
+ action_group_->add(Gtk::Action::create("ViewRefresh", Gtk::Stock::REFRESH),
+ sigc::mem_fun(*this, &BookmarkWindow::on_action_view_refresh));
+ action_group_->add(Gtk::Action::create("ViewStop", Gtk::Stock::STOP),
+ sigc::mem_fun(*this, &BookmarkWindow::on_action_view_stop));
+ action_group_->add(Gtk::Action::create("ViewMenubar", "Toggle _Menubar"),
+ sigc::mem_fun(*this, &BookmarkWindow::on_action_view_menubar));
+ action_group_->add(Gtk::Action::create("ViewToolbar", "Toggle _Toolbar"),
+ sigc::mem_fun(*this, &BookmarkWindow::on_action_view_toolbar));
+ action_group_->add(Gtk::Action::create("ViewStatusbar", "Toggle _Statusbar"),
+ sigc::mem_fun(*this, &BookmarkWindow::on_action_view_statusbar));
+
+ ui_manager_->insert_action_group(action_group_);
+ add_accel_group(ui_manager_->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " </menu>"
+ " <menu action='MenuView'>"
+ " <menuitem action='ViewRefresh'/>"
+ " <menuitem action='ViewStop'/>"
+ " <separator/>"
+ " <menuitem action='ViewMenubar'/>"
+ " <menuitem action='ViewToolbar'/>"
+ " <menuitem action='ViewStatusbar'/>"
+ " </menu>"
+ " </menubar>"
+ " <toolbar name='ToolBar'>"
+ " <toolitem action='ViewRefresh'/>"
+ " <toolitem action='ViewStop'/>"
+ " </toolbar>"
+ " <popup name='MenuPopup'>"
+ " <menuitem action='ViewRefresh'/>"
+ " <menuitem action='ViewStop'/>"
+ " <separator/>"
+ " <menuitem action='ViewMenubar'/>"
+ " </popup>"
+ "</ui>";
+
+ ui_manager_->add_ui_from_string(ui_info);
+
+ menubar_ = ui_manager_->get_widget("/MenuBar");
+ toolbar_ = ui_manager_->get_widget("/ToolBar");
+ popupmenu_ =
+ dynamic_cast<Gtk::Menu*>(ui_manager_->get_widget("/MenuPopup"));
+}
+
+void BookmarkWindow::on_action_view_refresh() {
+ if (http_getter_) return;
+
+ statusbar_.push("HTTP/1.0 GET...");
+
+ const std::string uri = "http://menu.2ch.net/bbsmenu.html";
+ http::Header request_header;
+ request_header.set_host("menu.2ch.net");
+
+ http_getter_.reset(new http::GetInThread(uri, request_header));
+ http_getter_->signal_end().connect(
+ sigc::mem_fun(*this, &BookmarkWindow::on_http_get_end));
+ http_getter_->run();
+}
+
+void BookmarkWindow::on_action_view_stop() {
+ if (http_getter_) http_getter_->cancel();
+}
+
+void BookmarkWindow::on_action_view_menubar() {
+ if (menubar_->is_visible()) menubar_->hide(); else menubar_->show();
+}
+
+void BookmarkWindow::on_action_view_toolbar() {
+ if (toolbar_->is_visible()) toolbar_->hide(); else toolbar_->show();
+}
+
+void BookmarkWindow::on_action_view_statusbar() {
+ if (statusbar_.is_visible()) statusbar_.hide(); else statusbar_.show();
+}
+
+void BookmarkWindow::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;
+ }
+
+ statusbar_.push(
+ response.get_status_line().get_line() + " " +
+ response.get_header().get_last_modified());
+
+ const int code = response.get_status_line().get_code();
+ if (code != 200) return;
+
+ bookmarks_ = bookmark::from_html(response.get_content());
+ reconstruct_category();
+
+ save_content();
+}
+
+void BookmarkWindow::on_category_cursor_changed() {
+ Glib::RefPtr<const Gtk::TreeSelection> selection =
+ view_category_.get_selection();
+
+ std::vector<Gtk::TreeModel::Path> paths = selection->get_selected_rows();
+ if (paths.empty()) return;
+
+ on_category_row_activated(paths[0], 0);
+}
+
+void BookmarkWindow::on_category_row_activated(
+ const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) {
+
+ Gtk::TreeRow row = *(category_list_store_->get_iter(path));
+ const std::string category = row[category_column_record_.category_];
+
+ bookmark_list_store_->clear();
+
+ BOOST_FOREACH(const bookmark::Item& item, bookmarks_) {
+ if (item.belongs_to(category)) {
+ Gtk::TreeModel::Row row = *(bookmark_list_store_->append());
+ row[bookmark_column_record_.name_] = item.name;
+ row[bookmark_column_record_.uri_] = item.uri;
+ }
+ }
+}
+
+void BookmarkWindow::on_bookmark_row_activated(const Gtk::TreeModel::Path& path,
+ Gtk::TreeViewColumn*) {
+ Gtk::TreeRow row = *(bookmark_list_store_->get_iter(path));
+ const std::string& uri = row[bookmark_column_record_.uri_];
+ uri_opener::open(uri);
+}
+
+void BookmarkWindow::save_content() {
+ bookmark::to_xml(get_xml_path(), bookmarks_);
+}
+
+void BookmarkWindow::reconstruct_category() {
+ std::vector<std::string> categories;
+
+ BOOST_FOREACH(const bookmark::Item& item, bookmarks_) {
+ BOOST_FOREACH(const std::string& category, item.categories) {
+ if (std::find(categories.begin(), categories.end(), category) ==
+ categories.end())
+ categories.push_back(category);
+ }
+ }
+
+ category_list_store_->clear();
+
+ BOOST_FOREACH(const std::string& category, categories) {
+ Gtk::TreeModel::Row row = *(category_list_store_->append());
+ row[category_column_record_.category_] = category;
+ }
+}
+
+boost::filesystem::path BookmarkWindow::get_xml_path() const {
+ const boost::filesystem::path home(std::getenv("HOME"));
+ return home / ".dialektos" / filename;
+}
+
+
+} // namespace dialektos
--- /dev/null
+/*
+ * Copyright (C) 2009 by Aiwota Programmer
+ * aiwotaprog@tetteke.tk
+ *
+ * This file is part of Dialektos.
+ *
+ * Dialektos is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dialektos is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dialektos. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BOOKMARK_WINDOW_HXX
+#define BOOKMARK_WINDOW_HXX
+
+
+#include <gtkmm/window.h>
+#include <gtkmm/box.h>
+#include <gtkmm/paned.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/statusbar.h>
+#include <gtkmm/widget.h>
+#include <gtkmm/uimanager.h>
+#include <gtkmm/actiongroup.h>
+#include <gtkmm/liststore.h>
+#include <glibmm/refptr.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/filesystem/path.hpp>
+#include <string>
+#include <vector>
+#include "bookmark_item.hxx"
+
+
+namespace dialektos {
+
+namespace http {
+ struct GetInThread;
+}
+
+
+class BookmarkWindow : public Gtk::Window {
+
+ struct CategoryColumnRecord : public Gtk::TreeModelColumnRecord {
+ CategoryColumnRecord() : Gtk::TreeModelColumnRecord() {
+ add(category_);
+ }
+ Gtk::TreeModelColumn<std::string> category_;
+ };
+
+ struct BookmakrItemColumnRecord : public Gtk::TreeModelColumnRecord {
+ BookmakrItemColumnRecord() : Gtk::TreeModelColumnRecord() {
+ add(name_);
+ add(uri_);
+ }
+ Gtk::TreeModelColumn<std::string> name_;
+ Gtk::TreeModelColumn<std::string> uri_;
+ };
+
+public:
+ static void create();
+ virtual ~BookmarkWindow();
+protected:
+ BookmarkWindow();
+ virtual bool on_delete_event(GdkEventAny*);
+private:
+ void build_menu();
+
+ void on_action_view_refresh();
+ void on_action_view_stop();
+ void on_action_view_menubar();
+ void on_action_view_toolbar();
+ void on_action_view_statusbar();
+
+ void on_http_get_end(bool);
+
+ void on_category_cursor_changed();
+ void on_category_row_activated(const Gtk::TreeModel::Path&,
+ Gtk::TreeViewColumn*);
+ void on_bookmark_row_activated(const Gtk::TreeModel::Path&,
+ Gtk::TreeViewColumn*);
+
+ void save_content();
+
+ void reconstruct_category();
+ boost::filesystem::path get_xml_path() const;
+
+ Gtk::VBox vbox_;
+ Gtk::HPaned hpaned_;
+ Gtk::ScrolledWindow scrolled1_;
+ Gtk::ScrolledWindow scrolled2_;
+ Gtk::TreeView view_category_;
+ Gtk::TreeView view_bookmark_;
+ Gtk::Statusbar statusbar_;
+
+ CategoryColumnRecord category_column_record_;
+ BookmakrItemColumnRecord bookmark_column_record_;
+ Glib::RefPtr<Gtk::ListStore> category_list_store_;
+ Glib::RefPtr<Gtk::ListStore> bookmark_list_store_;
+
+ Glib::RefPtr<Gtk::UIManager> ui_manager_;
+ Glib::RefPtr<Gtk::ActionGroup> action_group_;
+ Gtk::Widget* menubar_;
+ Gtk::Widget* toolbar_;
+ Gtk::Menu* popupmenu_;
+
+ boost::scoped_ptr<http::GetInThread> http_getter_;
+
+ std::vector<bookmark::Item> bookmarks_;
+
+ static boost::scoped_ptr<BookmarkWindow> single_window;
+ static const std::string filename;
+};
+
+}
+
+
+#endif