OSDN Git Service

States of windows are saved and restored.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Sun, 28 Jun 2009 23:38:34 +0000 (08:38 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Sun, 28 Jun 2009 23:38:34 +0000 (08:38 +0900)
15 files changed:
src/application_framework.cxx
src/application_window.cxx
src/application_window.hxx
src/bbs_detail_base.cxx
src/bbs_detail_base.hxx
src/board_window.cxx
src/board_window.hxx
src/board_window_state.cxx [new file with mode: 0644]
src/board_window_state.hxx [new file with mode: 0644]
src/text_view.cxx
src/text_view.hxx
src/thread_window.cxx
src/thread_window.hxx
src/thread_window_state.cxx [new file with mode: 0644]
src/thread_window_state.hxx [new file with mode: 0644]

index abc4f09..efded30 100644 (file)
@@ -28,6 +28,7 @@
 #include <gtkmm/menu.h>
 #include <gtkmm/uimanager.h>
 #include <gtkmm/actiongroup.h>
+#include <boost/foreach.hpp>
 #include <iostream>
 #include "bookmark_window.hxx"
 
@@ -47,6 +48,7 @@ ApplicationFrameWork::ApplicationFrameWork() :
 
   ApplicationWindow::add(vbox_);
   build_menubar();
+  statusbar_.set_no_show_all(true);
 }
 
 ApplicationFrameWork::~ApplicationFrameWork() {}
@@ -117,7 +119,11 @@ void ApplicationFrameWork::build_menubar() {
   ui_manager_->add_ui_from_string(ui_info);
 
   menubar_ = ui_manager_->get_widget("/MenuBar");
+  menubar_->set_no_show_all(true);
+  menubar_->hide();
   toolbar_ = ui_manager_->get_widget("/ToolBar");
+  toolbar_->set_no_show_all(true);
+  toolbar_->hide();
   popupmenu_ =
     dynamic_cast<Gtk::Menu*>(ui_manager_->get_widget("/MenuPopup"));
 
@@ -125,6 +131,8 @@ void ApplicationFrameWork::build_menubar() {
 
 void ApplicationFrameWork::on_action_file_quit() {
   save();
+  BOOST_FOREACH(const ApplicationWindow& window, windows)
+    window.save_state();
   windows.clear();
   Gtk::Main::quit();
 }
index 7546b0c..3437fc1 100644 (file)
@@ -111,6 +111,7 @@ void ApplicationWindow::regist(ApplicationWindow* window) {
 bool ApplicationWindow::on_delete_event(GdkEventAny* /*event*/) {
   using namespace boost::lambda;
 //  using boost::lambda::_1;
+  save_state();
   windows.erase_if(&_1 == this);
   if (!windows.empty()) save();
   if (windows.empty()) Gtk::Main::quit();
index 0564551..0df02b2 100644 (file)
@@ -37,6 +37,7 @@ public:
   virtual ~ApplicationWindow();
   static bool is_opened(const bbs_detail::Base& bbs);
   static void load();
+  virtual void save_state() const = 0;
 protected:
   ApplicationWindow();
   static void regist(ApplicationWindow* window);
index 0eeba68..feada1e 100644 (file)
@@ -98,6 +98,11 @@ std::string Base::get_thread_idx_path() const {
   return xml.file_string();
 }
 
+std::string Base::get_thread_state_path() const {
+  const boost::filesystem::path dir(get_board_dir_path());
+  const boost::filesystem::path xml = dir / "states" / (thread_ + ".xml");
+  return xml.file_string();
+}
 
 std::string Base::get_thread_dat_uri() const {
   return "http://" + host_ + "/" + board_ + "/dat/" + thread_ + ".dat";
@@ -121,6 +126,12 @@ std::string Base::get_board_subject_path() const {
   return sbj.file_string();
 }
 
+std::string Base::get_board_state_path() const {
+  boost::filesystem::path xml(get_board_dir_path());
+  xml = xml / "boardstate.xml";
+  return xml.file_string();
+}
+
 std::string Base::get_board_subject_uri() const {
   std::stringstream ss;
   ss << boost::format("http://%1%/%2%/%3%")
index c77bbbc..940939d 100644 (file)
@@ -45,10 +45,12 @@ public:
   virtual std::string get_board_dir_path() const;
   virtual std::string get_thread_file_path() const;
   virtual std::string get_thread_idx_path() const;
+  virtual std::string get_thread_state_path() const;
   virtual std::string get_thread_dat_uri() const;
   virtual std::string get_thread_uri() const;
   virtual std::string get_another_thread_uri(const std::string& thread) const;
   virtual std::string get_board_subject_path() const;
+  virtual std::string get_board_state_path() const;
   virtual std::string get_board_subject_uri() const;
   virtual std::string get_board_uri() const;
   virtual std::string get_board_subject_idx_path() const;
index acd4095..c1c6d90 100644 (file)
@@ -37,6 +37,8 @@
 #include <fstream>
 #include <iostream>
 #include <sstream>
+#include <vector>
+#include <algorithm>
 
 #include "bbs_detail_base.hxx"
 #include "thread_list_model.hxx"
 #include "board_subject_idx.hxx"
 #include "http_date.hxx"
 #include "misc.hxx"
+#include "board_window_state.hxx"
 
 
 namespace dialektos {
 
+namespace {
+struct LessSecond {
+  template <typename PairType>
+  bool operator()(const PairType& lhs, const PairType& rhs) const {
+    return lhs.second < rhs.second;
+  }
+};
+}
+
 
 void BoardWindow::create(std::auto_ptr<bbs_detail::Base> bbs) {
   regist(new BoardWindow(bbs));
@@ -87,7 +99,6 @@ BoardWindow::BoardWindow(std::auto_ptr<bbs_detail::Base> bbs) :
   tree_view_.signal_button_press_event().connect_notify(
       sigc::mem_fun(*this, &BoardWindow::on_child_button_press));
 
-  set_default_size(400,300);
 
   scrolled_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
   add(scrolled_);
@@ -104,6 +115,36 @@ BoardWindow::BoardWindow(std::auto_ptr<bbs_detail::Base> bbs) :
   tree_view_.append_column(*Gtk::manage(new view_column::Average));
   tree_view_.append_column(*Gtk::manage(new view_column::StartTime));
 
+  BoardWindowState state;
+  state.from_xml(boost::filesystem::path(bbs_->get_board_state_path()));
+
+  set_default_size(state.width, state.height);
+  if (state.menubar) menubar_->show();
+  if (state.toolbar) toolbar_->show();
+  if (state.statusbar) statusbar_.show();
+
+  // set columns widths
+  BOOST_FOREACH(Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
+    BoardWindowState::ColumnInfo info = state.columns[column->get_title()];
+    if (info.width > 0) column->set_fixed_width(info.width);
+  }
+
+  // set columns order.
+  typedef std::pair<Gtk::TreeViewColumn*, int> PairType;
+  std::vector<PairType> column_order;
+  BOOST_FOREACH(Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
+    BoardWindowState::ColumnInfo info = state.columns[column->get_title()];
+    column_order.push_back(std::make_pair(column, info.order));
+  }
+  std::sort(column_order.begin(), column_order.end(), LessSecond());
+
+  Gtk::TreeViewColumn* previous_column = 0;
+  BOOST_FOREACH(PairType& pair, column_order) {
+    if (!previous_column) tree_view_.move_column_to_start(*pair.first);
+    else tree_view_.move_column_after(*pair.first, *previous_column);
+    previous_column = pair.first;
+  }
+
   using namespace boost::posix_time;
   ptime start = microsec_clock::local_time();
 
@@ -304,4 +345,25 @@ void BoardWindow::on_refresh_end(const http::StatusLine& status,
   statusbar_.push(message);
 }
 
+void BoardWindow::save_state() const{
+  BoardWindowState state;
+  state.width = get_width();
+  state.height = get_height();
+  state.menubar = menubar_->is_visible();
+  state.toolbar = toolbar_->is_visible();
+  state.statusbar = statusbar_.is_visible();
+
+  int order = 0;
+  BOOST_FOREACH(const Gtk::TreeViewColumn* column, tree_view_.get_columns()) {
+    BoardWindowState::ColumnInfo info;
+    info.width = column->get_width();
+    info.order = order;
+    state.columns[column->get_title()] = info;
+    ++order;
+  }
+
+  state.to_xml(boost::filesystem::path(bbs_->get_board_state_path()));
+}
+
+
 } // namespace dialektos
index 5a1c3f1..605eb7f 100644 (file)
@@ -44,6 +44,7 @@ class BoardWindow: public dialektos::ApplicationFrameWork {
 public:
   static void create(std::auto_ptr<bbs_detail::Base> bbs);
   virtual ~BoardWindow();
+  virtual void save_state() const;
 protected:
   BoardWindow(std::auto_ptr<bbs_detail::Base> bbs);
 
diff --git a/src/board_window_state.cxx b/src/board_window_state.cxx
new file mode 100644 (file)
index 0000000..5acba65
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 "board_window_state.hxx"
+
+#include <boost/archive/xml_iarchive.hpp>
+#include <boost/archive/xml_oarchive.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/filesystem.hpp>
+#include <fstream>
+#include <iostream>
+
+
+namespace dialektos {
+
+
+BoardWindowState::BoardWindowState() :
+  width(400), height(300), menubar(true), toolbar(true), statusbar(true) {
+}
+
+void BoardWindowState::from_xml(const boost::filesystem::path& xml) {
+  if (!boost::filesystem::exists(xml) ||
+      !boost::filesystem::is_regular_file(xml))
+    return;
+
+  std::ifstream ifs(xml.file_string().c_str());
+  try {
+    boost::archive::xml_iarchive ia(ifs);
+    ia >> boost::serialization::make_nvp("BoardWindowState", *this);
+  } catch (const boost::archive::archive_exception& e) {
+    std::cerr << e.what() << " " << xml << std::endl;
+  }
+}
+
+void BoardWindowState::to_xml(const boost::filesystem::path& xml) {
+  // Do not save if subject.txt does not exist.
+  if (!boost::filesystem::exists(xml.parent_path() / "subject.txt")) return;
+  std::ofstream ofs(xml.file_string().c_str());
+  try {
+    boost::archive::xml_oarchive oa(ofs);
+    oa << boost::serialization::make_nvp("BoardWindowState", *this);
+  } catch (const boost::archive::archive_exception& e) {
+    std::cerr << e.what() << " " << xml << std::endl;
+  }
+}
+
+
+} // namespace dialektos
diff --git a/src/board_window_state.hxx b/src/board_window_state.hxx
new file mode 100644 (file)
index 0000000..ed1bb32
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 BOARD_WINDOW_STATE_HXX
+#define BOARD_WINDOW_STATE_HX_
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/map.hpp>
+#include <boost/serialization/string.hpp>
+#include <boost/filesystem/path.hpp>
+#include <map>
+#include <string>
+
+
+namespace dialektos {
+
+
+struct BoardWindowState {
+  struct ColumnInfo {
+    ColumnInfo() : width(0), order(-1) {}
+    int width;
+    unsigned int order;
+  private:
+    friend class boost::serialization::access;
+    template <typename ArchiveType>
+    void serialize(ArchiveType& ar, const unsigned int version) {
+      ar & boost::serialization::make_nvp("ColumnWidth", width);
+      ar & boost::serialization::make_nvp("ColumnOrder", order);
+    }
+  };
+
+  struct SortInfo {
+    SortInfo() : column("No."), ascendant(true) {}
+    std::string column;
+    bool ascendant;
+  private:
+    friend class boost::serialization::access;
+    template <typename ArchiveType>
+    void serialize(ArchiveType& ar, const unsigned int version) {
+      ar & boost::serialization::make_nvp("SortColumn", column);
+      ar & boost::serialization::make_nvp("SortOrder", ascendant);
+    }
+  };
+
+  BoardWindowState();
+
+  int width;
+  int height;
+  bool menubar;
+  bool toolbar;
+  bool statusbar;
+  std::map<std::string, ColumnInfo> columns;
+  SortInfo sort;
+
+  void from_xml(const boost::filesystem::path& xml);
+  void to_xml(const boost::filesystem::path& xml);
+
+private:
+  friend class boost::serialization::access;
+  template <typename ArchiveType>
+  void serialize(ArchiveType& ar, const unsigned int version) {
+    ar & boost::serialization::make_nvp("WindowWidth", width);
+    ar & boost::serialization::make_nvp("WindowHeight", height);
+    ar & boost::serialization::make_nvp("MenubarVisible", menubar);
+    ar & boost::serialization::make_nvp("ToolbarVisible", toolbar);
+    ar & boost::serialization::make_nvp("StatusbarVisible", statusbar);
+    ar & boost::serialization::make_nvp("Columns", columns);
+    ar & boost::serialization::make_nvp("SortColumn", sort);
+  }
+
+};
+
+
+} // namespace dialektos
+
+
+#endif
index b7f1780..5efd00d 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "text_view.hxx"
 
+#include <boost/foreach.hpp>
 #include <iostream>
 
 #include "text_line.hxx"
@@ -43,6 +44,18 @@ int TextView::get_displayed_res_num() const {
   return 0;
 }
 
+void TextView::jump_to_res_num(const int res_num) {
+  if (res_num < 1 || res_num > get_res_num()) return;
+
+  BOOST_FOREACH(TextLine& line, line_list_) {
+    const int res = line.get_res_num();
+    if (res_num == res) {
+      adjustment_.set_value(line.get_y());
+      break;
+    }
+  }
+}
+
 void TextView::on_anchor_click_event(const text_element::Anchor& elem) {
   std::cout << elem.get_href() << std::endl;
 }
index 5fc1f9d..5273b09 100644 (file)
@@ -39,6 +39,7 @@ public:
 
   /*! @brief Gets the first number of res on the top of the widget. */
   int get_displayed_res_num() const;
+  void jump_to_res_num(int res_num);
 
 protected:
   virtual void on_anchor_click_event(const text_element::Anchor& elem);
index a428975..878b769 100644 (file)
@@ -31,6 +31,7 @@
 #include "http_get.hxx"
 #include "misc.hxx"
 #include "uri_opener.hxx"
+#include "thread_window_state.hxx"
 
 
 namespace dialektos {
@@ -66,17 +67,23 @@ ThreadWindow::ThreadWindow(std::auto_ptr<bbs_detail::Base> bbs) :
 
   ui_manager_->add_ui_from_string(ui);
 
-  set_default_size(400,300);
-
   scrolled_.add(*text_view_);
   add(scrolled_);
 
   text_view_->signal_button_press_event().connect_notify(
       sigc::mem_fun(*this, &ThreadWindow::on_child_button_press));
 
+  ThreadWindowState state;
+  state.from_xml(boost::filesystem::path(bbs_->get_thread_state_path()));
+  set_default_size(state.width, state.height);
+  if (state.menubar) menubar_->show();
+  if (state.toolbar) toolbar_->show();
+  if (state.statusbar) statusbar_.show();
+
   show_all();
 
   load();
+  text_view_->jump_to_res_num(state.displayed_res_num);
 }
 
 bool ThreadWindow::load() {
@@ -226,5 +233,15 @@ void ThreadWindow::save_content(const http::Response& response) {
   idx_.to_xml(boost::filesystem::path(bbs_->get_thread_idx_path()));
 }
 
+void ThreadWindow::save_state() const {
+  ThreadWindowState state;
+  state.width = get_width();
+  state.height = get_height();
+  state.menubar = menubar_->is_visible();
+  state.toolbar = toolbar_->is_visible();
+  state.statusbar = statusbar_.is_visible();
+  state.displayed_res_num = text_view_->get_displayed_res_num();
+  state.to_xml(boost::filesystem::path(bbs_->get_thread_state_path()));
+}
 
 } // namespace dialektos
index b130642..1c56c44 100644 (file)
@@ -44,6 +44,7 @@ public:
   static void create(std::auto_ptr<bbs_detail::Base> bbs);
   bool load();
   virtual ~ThreadWindow(){}
+  virtual void save_state() const;
 protected:
   ThreadWindow(std::auto_ptr<bbs_detail::Base> bbs);
 
diff --git a/src/thread_window_state.cxx b/src/thread_window_state.cxx
new file mode 100644 (file)
index 0000000..1a762bb
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 "thread_window_state.hxx"
+
+#include <boost/archive/xml_iarchive.hpp>
+#include <boost/archive/xml_oarchive.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/filesystem.hpp>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include "misc.hxx"
+
+
+namespace dialektos {
+
+
+ThreadWindowState::ThreadWindowState() :
+  width(400), height(300), displayed_res_num(0),
+  menubar(true), toolbar(true), statusbar(true) {
+}
+
+void ThreadWindowState::from_xml(const boost::filesystem::path& xml) {
+  if (!boost::filesystem::exists(xml) ||
+      !boost::filesystem::is_regular_file(xml))
+    return;
+
+  std::ifstream ifs(xml.file_string().c_str());
+  try {
+    boost::archive::xml_iarchive ia(ifs);
+    ia >> boost::serialization::make_nvp("ThreadWindowState", *this);
+  } catch (const boost::archive::archive_exception& e) {
+    std::cerr << e.what() << " " << xml << std::endl;
+  }
+}
+
+void ThreadWindowState::to_xml(const boost::filesystem::path& xml) {
+  // do not save if corresponding dat file does not exist.
+  std::string leaf = xml.leaf();
+  boost::filesystem::path dat =
+    xml.parent_path().parent_path() /
+    "dat" / (leaf.substr(0, leaf.size() - 3) + "dat");
+  if (!boost::filesystem::exists(dat)) return;
+  if (!misc::create_directories(xml.parent_path())) return;
+  std::ofstream ofs(xml.file_string().c_str());
+  try {
+    boost::archive::xml_oarchive oa(ofs);
+    oa << boost::serialization::make_nvp("ThreadWindowState", *this);
+  } catch (const boost::archive::archive_exception& e) {
+    std::cerr << e.what() << " " << xml << std::endl;
+  }
+}
+
+
+} // namespace dialektos
diff --git a/src/thread_window_state.hxx b/src/thread_window_state.hxx
new file mode 100644 (file)
index 0000000..5db9b50
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 THREAD_WINDOW_STATE_HXX
+#define THREAD_WINDOW_STATE_HXX
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/filesystem/path.hpp>
+
+
+namespace dialektos {
+
+
+struct ThreadWindowState {
+  ThreadWindowState();
+
+  int width;
+  int height;
+  int displayed_res_num;
+  bool menubar;
+  bool toolbar;
+  bool statusbar;
+
+  void from_xml(const boost::filesystem::path& xml);
+  void to_xml(const boost::filesystem::path& xml);
+
+private:
+  friend class boost::serialization::access;
+  template <typename ArchiveType>
+  void serialize(ArchiveType& ar, const unsigned int version) {
+    ar & boost::serialization::make_nvp("WindowWidth", width);
+    ar & boost::serialization::make_nvp("WindowHeight", height);
+    ar & boost::serialization::make_nvp("DisplayedResNum", displayed_res_num);
+    ar & boost::serialization::make_nvp("MenubarVisible", menubar);
+    ar & boost::serialization::make_nvp("ToolbarVisible", toolbar);
+    ar & boost::serialization::make_nvp("StatusbarVisible", statusbar);
+  }
+};
+
+
+} // namespace dialektos
+
+
+#endif