--- /dev/null
+.*
+!.gitignore
+!.gitkeep
+
+*~
+
+# IDE settings
+*.xcodeproj # Xcode
+*.cbp # code::blocks
+*.vcproj # Visual C++
+*.vcxproj # Visual C++
+
+# emacs
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# vim
+.*.s[a-w][a-z]
+*.un~
+Session.vim
+.netrwhist
+
+# build dir
+bin/debug
+bin/release
+
+# OS X hidden/temp files
+.DS_Store
+.AppleDouble
+.LSOverride
+Icon
+
+# OS X Thumbnails
+._*
+
+# files that might appear on external disk (OS X)
+.Spotlight-V100
+.Trashes
+
+# User-specific project settings
+*.mode1v3
+*.mode2v3
+
+# Windows image thumbnail file
+Thumbs.db
+ehthumbs.db
+
+# Windows folder config file
+Desktop.ini
+
+# recycle Bin used on file shares (Windows)
+$RECYCLE.BIN/
+
+*.swp
+*~.nib
+
--- /dev/null
+*ビルド方法
+libcore単体でビルドする場合は、ターミナル上で、
+ cd $(LIBCORE_ROOT)
+ make standalone
+
+pnuts/gikomonaとともにビルドする場合は、
+$(GIKOMONA_ROOT)/build/HOW-TO-BUILD.txt を参照してください。
+
+*テスト方法
+libcore単体でテストするときは、ターミナル上で、
+ cd $(LIBCORE_ROOT)
+ make test
+
+pnuts/gikomonaとともにテストする際には、自動でlibcoreのテストも行われるので、
+いちいち指定してテストする必要はありません。
+
+*ビルド後の pnuts/gikomona のフォルダの構造
+
+$(GIKOMONA_ROOT)/
+ resource/
+ icon/
+ ...
+==== pnuts のみ ====
+ pnuts.xrc
+
+==== gikomona のみ ====
+ gikomona.xrc
+
+ doc/
+ README.txt
+ LICENSE.txt
+
+ thread/
+ board-list/
+
+ history.db
+ config.xml
+ favorite.xml
+
+==== pnuts のみ ====
+ pnuts(.exe)
+
+==== gikomona のみ ====
+ twitter/
+ gikomona(.exe)
+
--- /dev/null
+#ifndef GIKOMONA_CORE_GIKOMONA_HPP
+#define GIKOMONA_CORE_GIKOMONA_HPP
+
+#include <string>
+
+#include <boost/utility/string_ref.hpp>
+
+namespace monazilla { namespace GikoMona {
+
+typedef char u8_char;
+typedef std::basic_string<u8_char> u8_string;
+
+typedef u8_string mona_string;
+typedef boost::basic_string_ref<u8_char, std::char_traits<u8_char>> mona_string_ref;
+
+typedef std::basic_string<char> sjis_string;
+
+namespace core {
+
+bool init_core();
+void done_core() noexcept ;
+
+}
+
+} }
+
+/// @breif libbbs で string に関わる typedef を抑制する。@link libbbs/settings.hpp @endlink を見よ。
+#define LIBBBS_PRAGMA_USED_BY_GIKOMONA
+
+#define PROJECT_NAME "GikoMona project, powered by monazilla.org."
+#define PROJECT_URL "http://sourceforge.jp/projects/gikomona/"
+
+#endif
--- /dev/null
+#ifndef GIKOMONA_CORE_CONFIG_HPP
+#define GIKOMONA_CORE_CONFIG_HPP
+
+#include <tuple>
+
+#include <boost/mpl/bool.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+
+class config final {
+public:
+ typedef config self_type;
+
+ config() noexcept {}
+ ~config() {}
+
+ template <typename ...T>
+ std::tuple<T...> post_query(const mona_string_ref src) {
+ query conf_query(src);
+ return post_query(conf_query);
+ }
+
+ template <typename ...T>
+ std::tuple<T...> post_query(const query& query) {
+ switch(query.type) {
+ case query_type::IN:
+ config_table[query.var] = query.value;
+ return std::tuple<T...>();
+
+ case query_type::OUT:
+ // = config_table[query.var];
+
+ }
+ }
+};
+
+template <>
+struct is_responsible_to_query<config> : public boost::mpl::true_ {};
+
+} } }
+
+#endif
--- /dev/null
+#ifndef GIKOMONA_CORE_DATABASE_HPP
+#define GIKOMONA_CORE_DATABASE_HPP
+
+#include <memory>
+
+#include <sqlite3.h>
+
+#include <boost/filesystem.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class database final {
+public:
+ template <typename T>
+ class sql_executer final {
+ friend database;
+
+ sql_executer(const mona_string& sql) {}
+ ~sql_executer() {}
+
+ public:
+ void operator()() {}
+ };
+
+public:
+ typedef ::sqlite3 *db_object_type;
+ typedef database self_type;
+
+ database() : database("") {}
+
+ /// @breif すでに存在するデータベースファイルを開く。まだファイルが存在しない場合は、新たに作成する。
+ database(const boost::filesystem::path& db_path) {
+ if(db_path.empty()) {
+ is_opened_db = false;
+ } else {
+ if(!boost::filesystem::exists(db_path)) {
+ create(db_path);
+ } else {
+ open(db_path);
+ }
+ }
+ }
+
+ database(const self_type& other) = delete;
+
+ ~database() { close(); }
+
+ /// @breif 受け取った SQL を実行する。ただし、実行できる文は返り値の無いものに限られる。
+ /**
+ * @return SQL が正常に実行された場合は true、それ以外の場合は false を返す。
+ * @note この関数は SQL の実行結果を返さない。したがって、実行結果が必要な場合は、
+ * compile_sql() を用いるべきである。
+ */
+ bool run_sql(const mona_string& sql) {
+ return (::sqlite3_exec(db, sql.c_str(), NULL, NULL, NULL) != SQLITE_OK)
+ ? false : true;
+ }
+
+ /// @breif 受け取った SQL を sql_executer へと変換して返す。
+ /**
+ * @note 返された sql_executer を実行することで、ここで渡した SQL の実行結果を得ることができる。
+ * 実行した SQL の実行結果が不要である (あるいはそもそも実行結果が無い) 場合は、
+ * run_sql() を用いるべきである。
+ */
+ template <typename T>
+ sql_executer<T> compile_sql(const mona_string& sql) {
+ return sql_executer<T>(sql);
+ }
+
+ /// @brief すでに存在するデータベースファイルを開く。
+ bool open(const boost::filesystem::path& db_path) {
+ if (is_opened_db) {
+ close();
+ }
+
+ is_opened_db =
+ (::sqlite3_open(db_path.c_str(), &db) != SQLITE_OK)
+ ? false : true;
+
+ return is_opened_db;
+ }
+
+ /// @breif まだ存在しないデータベースファイルを新たに作成する。
+ /**
+ * @return true: ファイルが存在せず、かつ新たにデータベースファイルを作成することに成功した
+ * false: ファイルが存在する、あるいは存在しないが新規データベースファイルの作成に失敗した
+ */
+ bool create(const boost::filesystem::path& db_path) {
+ if(boost::filesystem::exists(db_path)) {
+ return false;
+ }
+
+ return open(db_path);
+ }
+
+ void close() noexcept {
+ ::sqlite3_close(db);
+ }
+
+private:
+ db_object_type db;
+ bool is_opened_db;
+
+ void begin_sql_statement() { run_sql("begin;"); }
+ void end_sql_statement() { run_sql("end"); }
+};
+
+template <>
+struct is_responsible_to_query<database> : public boost::mpl::true_ {};
+
+} } }
+
+#endif
--- /dev/null
+#ifndef GIKOMONA_CORE_MODEL_HPP
+#define GIKOMONA_CORE_MODEL_HPP
+
+#include <unistd.h>
+
+#include <boost/lockfree/queue.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+#include "database.hpp"
+#include "config.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class model {
+public:
+ typedef model self_type;
+
+ model() noexcept {
+ instance = this;
+ }
+
+ ~model() {}
+
+ static
+ model *get_instance() {
+ return instance;
+ }
+
+ bool push_query_in_queue(const query& src) {
+ return query_queue.push(src);
+ }
+
+ template <typename ResultType>
+ ResultType pop_query() {
+ exec_all_query();
+ }
+
+ bool load_file(const boost::filesystem::path& file_path);
+
+private:
+ static self_type *instance;
+ database db;
+ config app_config;
+ boost::lockfree::queue<query> query_queue;
+
+ void exec_all_query() {
+ query val("");
+ while(!query_queue.empty()) {
+ if(!query_queue.pop(val)) { continue; }
+ // valの処理
+ }
+ }
+};
+
+void post_query_to_model(const query& src) {
+ model *obj = model::get_instance();
+ while(!obj->push_query_in_queue(src)) {
+ ::sleep(10);
+ }
+}
+
+} } }
+
+#endif
--- /dev/null
+#ifndef GIKOMONA_CORE_QUERY_HPP_INCLUDED
+#define GIKOMONA_CORE_QUERY_HPP_INCLUDED
+
+#include <vector>
+
+#include <boost/mpl/bool.hpp>
+
+#include "GikoMona.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+/**
+ * @note このクラスは boost::lockfree::queue<> の制約から
+ * trivially copyable の要件を満たす必要がある。
+ * (参考:http://d.hatena.ne.jp/faith_and_brave/20130213/1360737911 )
+ */
+
+template <typename ValueType>
+class query final {
+public:
+ // query order
+ class select_ {
+ public:
+ struct select_all_column {} all_column;
+
+ select_& distinct() {}
+ select_& group_by() {}
+ select_& where() {}
+ select_& having() {}
+ };
+
+public:
+ typedef std::vector<mona_string> column_name_list;
+
+ query(mona_string) {}
+ ~query() = default;
+
+ query& define() { return (*this); }
+ select_& select(const column_name_list& columnes,
+ const mona_string& from) {
+ return (*this);
+ }
+ select_& select(select_::select_all_column /* dummy */,
+ const mona_string& from) {
+ return (*this);
+ }
+ query& insert(const mona_string& into) {}
+};
+
+template <typename T>
+struct is_responsible_to_query : public boost::mpl::false_ {};
+
+} } }
+
+#endif
\ No newline at end of file
--- /dev/null
+#include <wx/xrc/xmlres.h>
+
+#include "GikoMona.hpp"
+#include "database.hpp"
+#include "model.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+namespace {
+
+model *app_model;
+
+}
+
+bool init_core() {
+ wxXmlResource::Get()->InitAllHandlers();
+ wxXmlResource::Get()->LoadAllFiles(wxT(""));
+
+ database db;
+ db.run_sql("");
+
+ app_model = new model;
+ app_model->load_file("./config.xml");
+ app_model->load_file("./history.db");
+ app_model->load_file("./saved-tab-session.xml");
+}
+
+void done_core() noexcept {
+ delete app_model;
+}
+
+} } }
--- /dev/null
+
+#include "model.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+bool model::load_file(const boost::filesystem::path& file_path) {
+ if(boost::filesystem::exists(file_path)) {
+ return false;
+ }
+ return true;
+}
+
+} } }
--- /dev/null
+#!/bin/sh
+
+# bootstrap
+
+echo "now loading..."
+
+BUILD_DIR=`pwd`
+ROOT=${BUILD_DIR}/../
+LIB_DIR=${ROOT}/lib
+
+function check_dir() {
+ if [ -d $1 ] ; then
+ echo "\$1 does not find."
+ exit 1
+ fi
+}
+
+check_dir ${LIB_DIR}/core
+check_dir ${LIB_DIR}/libbbs
+check_dir ${LIB_DIR}/lua
+check_dir ${LIB_DIR}/sqlite3
+
+LIBCORE_DIR=${LIB_DIR}/core
+LIBBBS_DIR=${LIB_DIR}/libbbs
+LIBLUA_DIR=${LIB_DIR}/lua
+LIBSQLITE3_DIR=${LIB_DIR}/sqlite3
+
+cd LIBLUA_DIR
+if [ ! -f LIBLUA_DIR/bin/lua ] ; then
+ if [ ! -f LIBLUA_DIR/bin/lua.exe ] ; then
+ make
+ fi
+fi
+
+LUA=LIBLUA_DIR/bin/lua
+
+echo "bootstrap stage finished."
+
+cd BUILD_DIR
+
+LUA ./lake.lua lake-build
--- /dev/null
+#ifndef GIKOMONA_PNUTS_HPP
+#define GIKOMONA_PNUTS_HPP
+
+#include <wx/xrc.h>
+
+#include "core/GikoMona.hpp"
+
+namespace GikoMona {
+namespace pnuts {}
+}
+
+#define PNUTS_VERSION_MAJOR 1
+#define PNUTS_VERSION_MINOR 0
+#define PNUTS_VERSION_STATE 0
+
+#define PNUTS_VERSION PNUTS_VERSION_MAJOR . \
+ PNUTS_VERSION_MINOR . \
+ PNUTS_VERSION_STATE
+
+#define PNUTS_VERSION_STR BOOST_STRINGNIZE(PNUTS_VERSION)
+
+// wxrc generate.
+extern void InitXmlResource();
+
+namespace GikoMona {
+namespace pnuts {
+
+class application : public wxApp {
+ bool OnInit();
+ int OnExit();
+};
+
+}
+}
+
+#endif
--- /dev/null
+
+#include "pnuts.hpp"
+
+namespace GikoMona {
+namespace pnuts {
+
+bool application::OnInit() {
+ wxXmlResource::Get()->InitAllHandlers();
+ // init embedded resources.
+ InitXmlResources();
+ // init changeable resources.
+ wxXmlResource::Get()->LoadAllFiles("resource");
+
+ config = new config;
+#ifdef ENABLE_TWITTER
+ auto res = config.post_query<bool>("?:twitter/service=enable");
+ if(res) {
+ auto id_password
+ = config.post_query<mona_string, mona_string>(
+ ">:twitter/user/{id & password}"
+ );
+ twitter::login(id_password<0>, id_password<1>);
+ }
+#endif
+
+ controller = new controller;
+ model = new model;
+ main_window = new main_window;
+ model.main_window = main_window;
+ main_window->PushEventHandler(controller);
+
+ auto is_first_executation
+ = config.post_query<bool>(
+ "?:app/last-executation-time=inavailable"
+ );
+
+ if(is_first_executation) {
+ show_welcome();
+ main_window->Show();
+ return true;
+ } else {
+ thread_window = new threaad_window;
+ model.thread_window = thread_window;
+ thread_window->PushEventHandler(controller);
+
+ thread_window->Show();
+ return true;
+ }
+}
+
+int application::OnExit() {
+ config.post_query<void>(
+ "<:app/last-executation-time", "$:NOW"
+ );
+ return 0;
+}
+
+}
+}