OSDN Git Service

Add history.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Sat, 4 Jul 2009 18:21:37 +0000 (03:21 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Sat, 4 Jul 2009 18:21:37 +0000 (03:21 +0900)
src/application_framework.cxx
src/application_framework.hxx
src/application_window.cxx
src/application_window.hxx
src/history_data.cxx [new file with mode: 0644]
src/history_data.hxx [new file with mode: 0644]
src/history_item.hxx [new file with mode: 0644]
src/thread_window.cxx

index 305aae0..481dbeb 100644 (file)
@@ -31,6 +31,7 @@
 #include <boost/foreach.hpp>
 #include <iostream>
 #include "bookmark_window.hxx"
+#include "uri_opener.hxx"
 
 
 namespace dialektos {
@@ -44,7 +45,8 @@ ApplicationFrameWork::ApplicationFrameWork() :
   action_group_(Gtk::ActionGroup::create()),
   menubar_(),
   toolbar_(),
-  popupmenu_() {
+  popupmenu_(),
+  history_menu_() {
 
   ApplicationWindow::add(vbox_);
   build_menubar();
@@ -83,6 +85,8 @@ void ApplicationFrameWork::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"),
@@ -108,6 +112,7 @@ void ApplicationFrameWork::build_menubar() {
     "      <menuitem action='ViewToolbar'/>"
     "      <menuitem action='ViewStatusbar'/>"
     "    </menu>"
+    "    <menu action='MenuHistory'/>"
     "    <menu action='MenuBookmark'>"
     "      <menuitem action='BookmarkShow'/>"
     "    </menu>"
@@ -136,6 +141,11 @@ void ApplicationFrameWork::build_menubar() {
   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() {
@@ -180,5 +190,26 @@ void ApplicationFrameWork::on_child_button_press(GdkEventButton* event) {
   }
 }
 
+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
index 73c0b58..dac44d0 100644 (file)
@@ -28,6 +28,7 @@
 #include <gtkmm/uimanager.h>
 #include <gtkmm/actiongroup.h>
 #include <gtkmm/menu.h>
+#include <gtkmm/menuitem.h>
 #include "application_window.hxx"
 
 
@@ -53,6 +54,9 @@ protected:
 
   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_;
@@ -60,6 +64,7 @@ protected:
   Gtk::Widget* menubar_;
   Gtk::Widget* toolbar_;
   Gtk::Menu* popupmenu_;
+  Gtk::MenuItem* history_menu_;
 };
 
 
index 3437fc1..a0341b4 100644 (file)
@@ -39,6 +39,7 @@ namespace dialektos {
 
 /* 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) {
@@ -53,6 +54,7 @@ void ApplicationWindow::load() {
   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) &&
@@ -73,6 +75,8 @@ void ApplicationWindow::load() {
   if (uris.empty()) {
     uri_opener::open("http://dubai.2ch.net/morningcoffee/");
   }
+
+  histories.set_writable();
 }
 
 void ApplicationWindow::save() {
@@ -93,6 +97,8 @@ 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 {
index 0df02b2..ffb3622 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <gtkmm/window.h>
 #include <boost/ptr_container/ptr_vector.hpp>
+#include "history_data.hxx"
 
 
 namespace dialektos {
@@ -48,6 +49,7 @@ private:
   virtual std::string get_uri() const = 0;
 protected:
   static boost::ptr_vector<ApplicationWindow> windows;
+  static history::Data histories;
 };
 
 
diff --git a/src/history_data.cxx b/src/history_data.cxx
new file mode 100644 (file)
index 0000000..89f9bf4
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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
diff --git a/src/history_data.hxx b/src/history_data.hxx
new file mode 100644 (file)
index 0000000..75322be
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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
diff --git a/src/history_item.hxx b/src/history_item.hxx
new file mode 100644 (file)
index 0000000..278b8e3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
index bda9d3d..18c624e 100644 (file)
@@ -114,6 +114,7 @@ bool ThreadWindow::load() {
 
     idx_ = ThreadIdx::from_xml(bbs_->get_thread_idx_path());
     set_title(idx_.title_);
+    histories.push(bbs_->get_thread_uri(), idx_.title_);
   }
 
   return false;
@@ -239,6 +240,7 @@ void ThreadWindow::save_content(const http::Response& response) {
   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();