#include <boost/foreach.hpp>
#include <iostream>
#include "bookmark_window.hxx"
+#include "uri_opener.hxx"
namespace dialektos {
action_group_(Gtk::ActionGroup::create()),
menubar_(),
toolbar_(),
- popupmenu_() {
+ popupmenu_(),
+ history_menu_() {
ApplicationWindow::add(vbox_);
build_menubar();
action_group_->add(Gtk::Action::create("ViewStatusbar", "Toggle _Statusbar"),
sigc::mem_fun(*this, &ApplicationFrameWork::on_action_view_statusbar));
+ action_group_->add(Gtk::Action::create("MenuHistory", "Hi_story"));
+
// Bookmark menu
action_group_->add(Gtk::Action::create("MenuBookmark", "_Bookmark"));
action_group_->add(Gtk::Action::create("BookmarkShow", "_Show Bookmarks"),
" <menuitem action='ViewToolbar'/>"
" <menuitem action='ViewStatusbar'/>"
" </menu>"
+ " <menu action='MenuHistory'/>"
" <menu action='MenuBookmark'>"
" <menuitem action='BookmarkShow'/>"
" </menu>"
popupmenu_ =
dynamic_cast<Gtk::Menu*>(ui_manager_->get_widget("/MenuPopup"));
+ history_menu_ = dynamic_cast<Gtk::MenuItem*>(
+ ui_manager_->get_widget("/MenuBar/MenuHistory"));
+ history_menu_->show();
+ history_menu_->get_submenu()->signal_show().connect(
+ sigc::mem_fun(*this, &ApplicationFrameWork::set_history_menus));
}
void ApplicationFrameWork::on_action_file_quit() {
}
}
+void ApplicationFrameWork::on_history_menu_item_activate(
+ const std::string& uri) {
+ uri_opener::open(uri);
+}
+
+
+void ApplicationFrameWork::set_history_menus() {
+ Gtk::Menu* sub = history_menu_->get_submenu();
+ sub->items().clear();
+ std::vector<history::Item> recents = histories.get_recent();
+ BOOST_FOREACH(const history::Item& hitem, recents) {
+ Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem(hitem.name_));
+ item->show();
+ item->signal_activate().connect(sigc::bind(
+ sigc::mem_fun(*this,
+ &ApplicationFrameWork::on_history_menu_item_activate),
+ hitem.uri_));
+ sub->append(*item);
+ }
+}
+
} // namespace dialektos
#include <gtkmm/uimanager.h>
#include <gtkmm/actiongroup.h>
#include <gtkmm/menu.h>
+#include <gtkmm/menuitem.h>
#include "application_window.hxx"
virtual void on_child_button_press(GdkEventButton*);
+ void on_history_menu_item_activate(const std::string&);
+ void set_history_menus();
+
Gtk::VBox vbox_;
Gtk::Statusbar statusbar_;
Glib::RefPtr<Gtk::UIManager> ui_manager_;
Gtk::Widget* menubar_;
Gtk::Widget* toolbar_;
Gtk::Menu* popupmenu_;
+ Gtk::MenuItem* history_menu_;
};
/* static data members */
boost::ptr_vector<ApplicationWindow> ApplicationWindow::windows;
+history::Data ApplicationWindow::histories;
/* static member function */
bool ApplicationWindow::is_opened(const bbs_detail::Base& bbs) {
boost::filesystem::path dir(homedir);
boost::filesystem::path session = dir / ".dialektos" / "session.xml";
+ histories.from_xml(dir / ".dialektos" / "history.xml");
std::vector<std::string> uris;
if (boost::filesystem::exists(session) &&
if (uris.empty()) {
uri_opener::open("http://dubai.2ch.net/morningcoffee/");
}
+
+ histories.set_writable();
}
void ApplicationWindow::save() {
} catch (const boost::archive::archive_exception& e) {
std::cerr << "save(): " << e.what() << std::endl;
}
+
+ histories.to_xml(dir / ".dialektos" / "history.xml");
}
//bool ApplicationWindow::is_same(const bbs_detail::Base& bbs) const {
#include <gtkmm/window.h>
#include <boost/ptr_container/ptr_vector.hpp>
+#include "history_data.hxx"
namespace dialektos {
virtual std::string get_uri() const = 0;
protected:
static boost::ptr_vector<ApplicationWindow> windows;
+ static history::Data histories;
};
--- /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 "history_data.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/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <algorithm>
+#include <functional>
+#include <fstream>
+#include <string>
+#include <iostream>
+#include <vector>
+#include <map>
+#include "history_item.hxx"
+#include "misc.hxx"
+
+
+namespace dialektos {
+
+namespace history {
+
+
+namespace {
+struct CompareTime :
+ public std::binary_function<const Item&, const Item&, bool> {
+ bool operator()(const Item& lhs, const Item& rhs) const {
+ return lhs.time_ < rhs.time_;
+ }
+};
+} // anonymous namespace
+
+
+Data::Data() : map_(), writable_(false) {
+}
+
+Data::~Data() {
+}
+
+void Data::from_xml(const boost::filesystem::path& xml) {
+ if (!boost::filesystem::exists(xml) ||
+ !boost::filesystem::is_regular_file(xml))
+ return;
+
+ std::vector<Item> vec;
+ std::ifstream ifs(xml.file_string().c_str());
+ try {
+ boost::archive::xml_iarchive ia(ifs);
+ ia >> boost::serialization::make_nvp("History", vec);
+ } catch (const boost::archive::archive_exception& e) {
+ std::cerr << e.what() << " " << xml.file_string() << std::endl;
+ return;
+ }
+
+ map_.clear();
+ BOOST_FOREACH(const Item& item, vec) map_[item.uri_] = item;
+}
+
+void Data::to_xml(const boost::filesystem::path& xml) const {
+ if (!writable_) return;
+ if (!misc::create_directories(xml.parent_path())) return;
+
+ std::vector<Item> _vec;
+ _vec.reserve(map_.size());
+ BOOST_FOREACH(const MapType::value_type& pair, map_)
+ _vec.push_back(pair.second);
+ std::sort(_vec.begin(), _vec.end(), std::not2(CompareTime()));
+ std::vector<Item> vec(_vec.begin(),
+ _vec.size() > 100 ? _vec.begin()+100 : _vec.end());
+
+ std::ofstream ofs(xml.file_string().c_str());
+ try {
+ boost::archive::xml_oarchive oa(ofs);
+ oa << boost::serialization::make_nvp("History", vec);
+ boost::filesystem::last_write_time(xml.parent_path(),
+ boost::filesystem::last_write_time(xml));
+ } catch (const boost::archive::archive_exception& e) {
+ std::cerr << e.what() << " " << xml.file_string() << std::endl;
+ }
+}
+
+void Data::push(const Item::UriType& uri, const Item::NameType& name) {
+ if (!writable_) return;
+ if (uri.empty() || name.empty()) return;
+
+ Item item;
+ item.uri_ = uri;
+ item.name_ = name;
+ item.time_ = boost::posix_time::second_clock::universal_time();
+ map_[uri] = item;
+}
+
+std::vector<Item> Data::get_recent() const {
+ std::vector<Item> order;
+ order.reserve(map_.size());
+ BOOST_FOREACH(const MapType::value_type& pair, map_)
+ order.push_back(pair.second);
+
+ std::sort(order.begin(), order.end(), std::not2(CompareTime()));
+ return std::vector<Item>(order.begin(),
+ order.size() > 10 ? order.begin()+10 : order.end());
+}
+
+
+} // namespace history
+
+} // 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 HISTORY_DATA_HXX
+#define HISTORY_DATA_HXX
+
+#include <boost/unordered_map.hpp>
+#include <boost/filesystem/path.hpp>
+#include <string>
+#include <map>
+#include "history_item.hxx"
+
+
+namespace dialektos {
+
+namespace history {
+
+
+class Data {
+ typedef std::string UriType;
+ typedef boost::unordered_map<UriType, Item> MapType;
+public:
+ Data();
+ ~Data();
+ void set_writable() { writable_ = true; }
+
+ void from_xml(const boost::filesystem::path& xml);
+ void to_xml(const boost::filesystem::path& xml) const;
+
+ void push(const Item::UriType& uri, const Item::NameType& name);
+
+ std::vector<Item> get_recent() const;
+
+private:
+ MapType map_;
+ bool writable_;
+};
+
+
+} // namespace history
+
+} // 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/>.
+ */
+
+#ifndef HISTORY_ITEM_HXX
+#define HISTORY_ITEM_HXX
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <string>
+
+
+namespace dialektos {
+
+namespace history {
+
+
+struct Item {
+ typedef std::string UriType;
+ typedef std::string NameType;
+ typedef boost::posix_time::ptime TimeType;
+
+ UriType uri_;
+ NameType name_;
+ TimeType time_;
+private:
+ friend class boost::serialization::access;
+ BOOST_SERIALIZATION_SPLIT_MEMBER();
+
+ template <typename ArchiveType>
+ void load(ArchiveType& ar, const unsigned int version) {
+ ar & boost::serialization::make_nvp("Name", name_);
+ ar & boost::serialization::make_nvp("URI", uri_);
+ std::string iso;
+ ar & boost::serialization::make_nvp("Time", iso);
+ time_ = boost::posix_time::from_iso_string(iso);
+ }
+ template <typename ArchiveType>
+ void save(ArchiveType& ar, const unsigned int version) const {
+ ar & boost::serialization::make_nvp("Name", name_);
+ ar & boost::serialization::make_nvp("URI", uri_);
+ const std::string iso = boost::posix_time::to_iso_string(time_);
+ ar & boost::serialization::make_nvp("Time", iso);
+ }
+
+};
+
+
+} // namespace history
+
+} // namespace dialektos
+
+#endif
idx_ = ThreadIdx::from_xml(bbs_->get_thread_idx_path());
set_title(idx_.title_);
+ histories.push(bbs_->get_thread_uri(), idx_.title_);
}
return false;
if (code == 200) {
idx_.title_ = bbs_->get_title_from_string(response.get_content());
set_title(idx_.title_);
+ histories.push(bbs_->get_thread_uri(), idx_.title_);
}
idx_.last_modified_ = response.get_header().get_last_modified();