HtmlNode HtmlNode::parent()
{
pointer self_ = this->lself();
- if(self_) return HtmlNode(self_->lparent());
+ if(self_ && self_->lparent()) return HtmlNode(self_->lparent());
return HtmlNode();
}
*/
Html::pointer HtmlPrivate::lparent()
{
- assert(!m_parent.expired());
+ if(m_parent.expired()) return pointer();
return m_parent.lock();
}
QObject::connect(&w, SIGNAL(closing()), socket, SLOT(abort()));
w.show();
int result = a.exec();
+ delete service;
return result;
}
window.wordring = {
socket: null, // TMとの通信用ソケット。
port: 0, // ソケットのポート番号。
+ url: '',
edit_mode: false, // 編集モード判別フラグ。
slanguage: '',
tlanguage: '',
segments: null, // パラグラフの連想配列。
// ページのセットアップを行う。
- setup: function(){
- wordring.socket = new WebSocket('ws://localhost:' + window.wordring.port + '/');
- wordring.socket.onopen = wordring.onopen;
- wordring.socket.onmessage = wordring.onmessage;
- wordring.socket.onerror = wordring.onerror;
- wordring.socket.onclose = wordring.onclose;
-
+ setup: function() {
// 原文のコピーを取る。
- wordring.segments = {};
+ wordring.segments = Array();
var elements = document.getElementsByTagName('span');
var element;
var i = 0;
if(!element.hasAttribute('data-wordring-segment')) continue;
var id = element.getAttribute('data-wordring-segment');
- wordring.segments[id] = {};
- wordring.segments[id].element = element;
- wordring.segments[id].html = element.innerHTML;
+ var segment = {};
+ segment.segment_id = id;
+ segment.loaded = false;
+ segment.element = element;
+ segment.html = element.innerHTML;
+ wordring.segments[id] = segment;
}
+
+ wordring.socket = new WebSocket('ws://localhost:' + window.wordring.port + '/');
+ wordring.socket.onopen = wordring.onopen;
+ wordring.socket.onmessage = wordring.onmessage;
+ wordring.socket.onerror = wordring.onerror;
+ wordring.socket.onclose = wordring.onclose;
},
onopen: function(ev) {
wordring.socket.send(JSON.stringify({
'cmd': 'load',
- 'edit_mode': wordring.edit_mode,
+ 'url': window.wordring.url,
}));
-
- //if(document.hasFocus) wordring.socket.send(JSON.stringify({
- // 'cmd': 'load',
- // 'edit_mode': wordring.edit_mode,
- //}));
- //wordring.socket.send(JSON.stringify({
- // 'cmd': 'sethtml',
- // 'html': document.body.innerHTML,
- //}));
+
+ // 全てのセグメントの対して、セグメントの情報を送信する。
+ var length = wordring.segments.length;
+ var i = 0;
+ for(; i < length; i++) {
+ var segment = wordring.segments[i];
+ wordring.socket.send(JSON.stringify({
+ 'cmd': 'load_segment',
+ 'segment_id': segment.segment_id,
+ 'html': segment.html,
+ }));
+ }
},
onmessage: function(ev) {
{
case 'set_segment': wordring.set_segment(json); break;
case 'set_edit_mode': wordring.set_edit_mode(json); break;
+ case 'segment_loaded': wordring.segment_loaded(json); break;
}
},
onfocus: function(ev) {
wordring.socket.send(JSON.stringify({
'cmd': 'focus',
- 'edit_mode': wordring.edit_mode,
}));
},
for(node = ev.target; node.nodeType == Node.ELEMENT_NODE; node = node.parentNode) {
if(node.hasAttribute('data-wordring-segment'))
{
- var segment_id = node.getAttribute('data-wordring-segment');
+ var id = node.getAttribute('data-wordring-segment');
+ var segment = wordring.segments[id];
+ if(segment.loaded) break;
wordring.socket.send(JSON.stringify({
- 'cmd': 'edit',
- 'segment_id': segment_id,
- 'source': wordring.segments[segment_id].html,
- 'target': node.innerHtml,
+ 'cmd': 'edit_segment',
+ 'segment_id': segment.segment_id,
}));
break;
}
},
set_segment: function(json) {
- var id = json.id;
- wordring.segments[id].element.innerHTML = json.html;
+ var segment_id = json.segment_id;
+ wordring.segments[segment_id].element.innerHTML = json.html;
},
set_edit_mode: function(json) {
wordring.edit_mode = json.edit_mode;
},
+
+ segment_loaded: function(json) {
+ var segment_id = json.segment_id;
+ var segment = wordring.segments[segment_id];
+ segment.loaded = true;
+ segment.element.className = '';
+ },
};
window.addEventListener('load', function(ev){
-#include "tmdatabase.h"
-#include "settings.h"
+#include "settings.h"
+#include "tmdatabase.h"
+#include "tmservice.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDir>
#include <QLocale>
+#include <QDateTime>
#include "debug.h"
+// DatabaseBase ---------------------------------------------------------------
+
+bool TM::DatabaseBase::open(char const *message)
+{
+ assert(!m_database_name.isEmpty());
+ m_database = QSqlDatabase::addDatabase("QSQLITE", m_database_name);
+ m_database.setDatabaseName(m_database_name);
+ bool ret = m_database.open();
+ if(!ret)
+ {
+ error(message);
+ assert(false);
+ }
+ return ret;
+}
+
+QSqlQuery TM::DatabaseBase::prepare(char const *sql, char const *message)
+{
+ QSqlQuery result(m_database);
+ bool ret = result.prepare(sql);
+ if(!ret)
+ {
+ error(message, &result);
+ assert(false);
+ }
+ return result;
+}
+
+bool TM::DatabaseBase::exec(QSqlQuery &query, char const *message)
+{
+ bool ret = query.exec();
+ if(!ret)
+ {
+ error(message, &query);
+ assert(false);
+ }
+ return ret;
+}
+
+QSqlQuery TM::DatabaseBase::exec(char const *sql, char const *message)
+{
+ QSqlQuery result = m_database.exec(sql);
+ if(result.lastError().isValid())
+ {
+ error(message, &result);
+ assert(false);
+ }
+ return result;
+}
+
+void TM::DatabaseBase::error(QString message, QSqlQuery *query)
+{
+ QString s;
+ if(query) s = query->lastError().text();
+ else s = m_database.lastError().text();
+
+ qCritical() << message << s;
+}
+
// SiteDatabase ---------------------------------------------------------------
TM::SiteDatabase::SiteDatabase(Settings *settings)
"SiteDatabase/cache-limit", QVariant::fromValue(1024)).toInt();
// データベースを開く。
- m_database_name = settings->value(Database::root_key()).toString() + "/site.db";
- m_database = QSqlDatabase::addDatabase("QSQLITE", m_database_name);
- m_database.setDatabaseName(m_database_name);
- bool ret = m_database.open();
- if(!ret) qFatal("An error occured while open database in SiteDatabase().");
+ m_database_name = settings->value(TMDATABASE_ROOT_PATH_KEY).toString() + "/site.db";
+ open(Q_FUNC_INFO);
// テーブル作成。
- QSqlQuery q = m_database.exec(
- "CREATE TABLE IF NOT EXISTS sites("
+ exec("CREATE TABLE IF NOT EXISTS sites("
"site_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "domain TEXT UNIQUE);");
- if(q.lastError().isValid()) qFatal("An error occured while create table in SiteDatabase().");
- q = m_database.exec(
- "CREATE INDEX IF NOT EXISTS domain_index ON sites(domain);");
- if(q.lastError().isValid()) qFatal("An error occured while create index in SiteDatabase().");
+ "host_name TEXT UNIQUE);", Q_FUNC_INFO);
- // クエリ作成。
- m_find_site_id.reset(new QSqlQuery(m_database));
- ret = m_find_site_id->prepare("SELECT site_id FROM sites WHERE domain=?;");
- if(!ret) qFatal("An error occured while prepare select statement in SiteDatabase().");
+ exec("CREATE INDEX IF NOT EXISTS domain_index ON sites(host_name);", Q_FUNC_INFO);
- m_insert_site.reset(new QSqlQuery(m_database));
- ret = m_insert_site->prepare("INSERT INTO sites(domain) VALUES(?);");
- if(!ret) qFatal("An error occured while prepare insert statement in SiteDatabase().");
+ // クエリ作成。
+ m_find_site_id = prepare("SELECT site_id FROM sites WHERE host_name=?;", Q_FUNC_INFO);
+ m_insert_site = prepare("INSERT INTO sites(host_name) VALUES(?);", Q_FUNC_INFO);
}
TM::SiteDatabase::~SiteDatabase()
{
if(m_database.isOpen()) m_database.close();
- QSqlDatabase::removeDatabase(m_database_name);
}
/*!
/*!
* \brief 引数として与えられたドメイン名に対して一意な番号を返します。
+ *
+ * 登録されていない場合、0を返します。
*/
-int TM::SiteDatabase::find_site_id(QString domain)
+int TM::SiteDatabase::find_site_id(QString host_name)
{
- QSqlQuery *q = m_find_site_id.get();
- q->bindValue(0, domain);
- bool ret = q->exec();
- if(!ret) qFatal("An error occured in SiteDatabase::find_site_id().");
- if(q->next()) return q->value(0).toInt();
+ m_find_site_id.bindValue(0, host_name);
+ exec(m_find_site_id, Q_FUNC_INFO);
+ if(m_find_site_id.next()) return m_find_site_id.value(0).toInt();
return 0;
}
-void TM::SiteDatabase::insert_site(QString domain)
+void TM::SiteDatabase::insert_site(QString host_name)
{
- QSqlQuery *q = m_insert_site.get();
- q->bindValue(0, domain);
- bool ret = q->exec();
- if(!ret)
- {
- qDebug() << q->lastError();
- qFatal("An error occured in SiteDatabase::insert_site().");
- }
+ m_insert_site.bindValue(0, host_name);
+ exec(m_insert_site, Q_FUNC_INFO);
}
TM::SiteDatabase::pointer TM::SiteDatabase::create(Settings *settings)
// データベースを開く。
m_database_name = settings->value(
- Database::root_key()).toString() + "/word-" + name.toLower() + ".db";
- m_database = QSqlDatabase::addDatabase("QSQLITE", m_database_name);
- m_database.setDatabaseName(m_database_name);
- bool ret = m_database.open();
- if(!ret) qFatal("An error occured while open database in WordDatabase().");
+ TMDATABASE_ROOT_PATH_KEY).toString() + "/word-" + name.toLower() + ".db";
+ open(Q_FUNC_INFO);
// テーブル作成。
- QSqlQuery q = m_database.exec(
- "CREATE TABLE IF NOT EXISTS words("
+ exec("CREATE TABLE IF NOT EXISTS words("
"word_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "word TEXT UNIQUE);");
- if(q.lastError().isValid()) qFatal("An error occured while create table in WordDatabase().");
- q = m_database.exec(
- "CREATE INDEX IF NOT EXISTS word_index ON words(word);");
- if(q.lastError().isValid()) qFatal("An error occured while create index in WordDatabase().");
+ "word TEXT UNIQUE);", Q_FUNC_INFO);
+
+ exec("CREATE INDEX IF NOT EXISTS word_index ON words(word);", Q_FUNC_INFO);
// クエリ作成。
- m_find_word_id.reset(new QSqlQuery(m_database));
- ret = m_find_word_id->prepare("SELECT word_id FROM words WHERE word=?;");
- if(!ret) qFatal("An error occured while prepare select statement in WordDatabase().");
+ m_find_word_id = prepare(
+ "SELECT word_id FROM words WHERE word=?;", Q_FUNC_INFO);
- m_insert_word.reset(new QSqlQuery(m_database));
- ret = m_insert_word->prepare("INSERT INTO words(word) VALUES(?);");
- if(!ret) qFatal("An error occured while prepare insert statement in WordDatabase().");
+ m_insert_word = prepare("INSERT INTO words(word) VALUES(?);", Q_FUNC_INFO);
}
TM::WordDatabase::~WordDatabase()
{
if(m_database.isOpen()) m_database.close();
- QSqlDatabase::removeDatabase(m_database_name);
}
int TM::WordDatabase::word_id(QString word)
int TM::WordDatabase::find_word_id(QString word)
{
- QSqlQuery *q = m_find_word_id.get();
- q->bindValue(0, word);
- bool ret = q->exec();
- if(!ret) qFatal("An error occured in WordDatabase::find_word_id().");
- if(q->next()) return q->value(0).toInt();
+ m_find_word_id.bindValue(0, word);
+ exec(m_find_word_id, Q_FUNC_INFO);
+ if(m_find_word_id.next()) return m_find_word_id.value(0).toInt();
return 0;
}
void TM::WordDatabase::insert_word(QString word)
{
- QSqlQuery *q = m_insert_word.get();
- q->bindValue(0, word);
- bool ret = q->exec();
- if(!ret) qFatal("An error occured in WordDatabase::insert_word().");
+ m_insert_word.bindValue(0, word);
+ exec(m_insert_word, Q_FUNC_INFO);
}
TM::WordDatabase::pointer TM::WordDatabase::create(Settings *settings, QString name)
return pointer(new WordDatabase(settings, name));
}
+// sentence_data_type ---------------------------------------------------------
+
+TM::sentence_data_type::sentence_data_type()
+ : sentence_id(0)
+ , source_id(0)
+ , crc(0)
+ , previous_crc(0)
+ , next_crc(0)
+ , user_id(0)
+ , time(0)
+{
+
+}
+
+TM::sentence_data_type::pointer TM::sentence_data_type::create()
+{
+ return pointer(new sentence_data_type());
+}
+
// SentenceDatabase -----------------------------------------------------------
TM::SentenceDatabase::SentenceDatabase(Settings *settings, int site_id, QString name)
{
// データベースを開く。
m_database_name =
- settings->value(Database::root_key()).toString()
+ settings->value(TMDATABASE_ROOT_PATH_KEY).toString()
+ "/sentence-" + QString::number(site_id) + "-" + name.toLower() + ".db";
- m_database = QSqlDatabase::addDatabase("QSQLITE", m_database_name);
- m_database.setDatabaseName(m_database_name);
- bool ret = m_database.open();
- if(!ret) qFatal("An error occured while open database in SentenceDatabase().");
+ open(Q_FUNC_INFO);
// テーブル作成。
- QSqlQuery q = m_database.exec(
- "CREATE TABLE IF NOT EXISTS sentences("
+ exec("CREATE TABLE IF NOT EXISTS sentences("
"sentence_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "crc INTEGER,"
- "UNIQUE(source_id INTEGER, sentence TEXT),"
+ "source_id INTEGER,"
+ "sentence TEXT,"
"json TEXT,"
+ "crc INTEGER,"
+ "previous_crc INTEGER,"
+ "next_crc INTEGER,"
"user_id INTEGER,"
- "time TIMESTAMP);");
- if(q.lastError().isValid())
- qFatal("An error occured while create table in SentenceDatabase().");
+ "time INTEGER);");
// インデックス作成。
- q = m_database.exec("CREATE INDEX IF NOT EXISTS crc_index ON sentences(crc);");
- if(q.lastError().isValid())
- qFatal("An error occured while create index in SentenceDatabase().");
-
- q = m_database.exec("CREATE INDEX IF NOT EXISTS source_id_index ON sentences(source_id);");
- if(q.lastError().isValid())
- qFatal("An error occured while create index in SentenceDatabase().");
-
- q = m_database.exec("CREATE INDEX IF NOT EXISTS sentence_index ON sentences(sentence);");
- if(q.lastError().isValid())
- qFatal("An error occured while create index in SentenceDatabase().");
+ exec("CREATE INDEX IF NOT EXISTS source_id_index ON sentences(source_id);", Q_FUNC_INFO);
+ exec("CREATE INDEX IF NOT EXISTS sentence_index ON sentences(sentence);", Q_FUNC_INFO);
// クエリ作成。
- m_find_sentence_id.reset(new QSqlQuery(m_database));
- ret = m_find_sentence_id->prepare("SELECT sentence_id FROM sentences WHERE sentence=?;");
- if(!ret) qFatal("An error occured while prepare find_sentence_id statement in SentenceDatabase().");
+ m_find_sentence_id = prepare(
+ "SELECT sentence_id FROM sentences WHERE sentence=?;", Q_FUNC_INFO);
+
+ m_find_sentence_id_with_context = prepare(
+ "SELECT sentence_id FROM sentences "
+ "WHERE sentence=? "
+ "AND previous_crc=? "
+ "AND next_crc=?;", Q_FUNC_INFO);
+
+ m_find_sentence_by_crc = prepare("SELECT * FROM sentences WHERE crc=?;", Q_FUNC_INFO);
+ m_find_sentence_by_source_id = prepare("SELECT * FROM sentences WHERE source_id=?;", Q_FUNC_INFO);
+ m_insert_sentence = prepare(
+ "INSERT OR REPLACE INTO sentences("
+ "source_id, sentence, json, crc, previous_crc, next_crc, user_id, time) "
+ "VALUES(?, ?, ?, ?, ?, ?, ?, ?);", Q_FUNC_INFO);
+ m_update_sentence = prepare(
+ "UPDATE sentences SET "
+ "source_id=?,"
+ "sentence=?,"
+ "json=?,"
+ "crc=?,"
+ "previous_crc=?,"
+ "next_crc=?,"
+ "user_id=?,"
+ "time=? "
+ "WHERE sentence_id=?;", Q_FUNC_INFO);
+}
- m_find_sentence_by_crc.reset(new QSqlQuery(m_database));
- ret = m_find_sentence->prepare("SELECT * FROM sentences WHERE crc=?;");
- if(!ret) qFatal("An error occured while prepare find_sentence_by_crc statement in SentenceDatabase().");
+TM::SentenceDatabase::~SentenceDatabase()
+{
+ if(m_database.isOpen()) m_database.close();
+}
- m_find_sentence_by_source_id.reset(new QSqlQuery(m_database));
- ret = m_find_sentence->prepare("SELECT * FROM sentences WHERE source_id=?;");
- if(!ret) qFatal("An error occured while prepare select_by_crc statement in SentenceDatabase().");
+quint32 TM::SentenceDatabase::find_sentence_id(QString sentence)
+{
+ m_find_sentence_id.bindValue(0, sentence);
+ exec(m_find_sentence_id, Q_FUNC_INFO);
- m_insert_sentence.reset(new QSqlQuery(m_database));
- ret = m_insert_sentence->prepare(
- "INSERT OR REPLACE INTO sentences(crc, source_id, sentence, json, user_id, time) "
- "VALUES(?, ?, ?, ?, ?, ?);");
- if(!ret) qFatal("An error occured while prepare insert statement in SentenceDatabase().");
+ if(!m_find_sentence_id.next()) return 0;
+ return m_find_sentence_id.value(0).toUInt();
}
-TM::SentenceDatabase::~SentenceDatabase()
+quint32 TM::SentenceDatabase::find_sentence_id_with_context(
+ QString sentence, quint32 previous_crc, quint32 next_crc)
{
- if(m_database.isOpen()) m_database.close();
- QSqlDatabase::removeDatabase(m_database_name);
+ m_find_sentence_id_with_context.bindValue(0, sentence);
+ m_find_sentence_id_with_context.bindValue(1, previous_crc);
+ m_find_sentence_id_with_context.bindValue(2, next_crc);
+ exec(m_find_sentence_id_with_context, Q_FUNC_INFO);
+
+ if(!m_find_sentence_id_with_context.next()) return 0;
+ return m_find_sentence_id_with_context.value(0).toUInt();
}
-int TM::SentenceDatabase::sentence_id(QString sentence)
+TM::sentence_data_type::pointer
+TM::SentenceDatabase::find_sentence_by_source_id(int source_id)
{
- QSqlQuery *q = m_find_sentence_id.get();
- q->bindValue(0, sentence);
- bool ret = q->exec();
- if(!ret) qFatal("An error occured in SentenceDatabase::sentence_id().");
- if(q->next()) return q->value(0).toInt();
+ assert(source_id);
- return 0;
+ sentence_data_type::pointer result;
+
+ m_find_sentence_by_source_id.bindValue(0, source_id);
+ exec(m_find_sentence_by_source_id, Q_FUNC_INFO);
+
+ if(!m_find_sentence_by_source_id.next()) return result;
+
+ return stuff_value(&m_find_sentence_by_source_id);
}
-/*bool TM::SentenceDatabase::find_sentence(QString sentence,
- QString *tsentence, QJsonArray *json)
+void TM::SentenceDatabase::insert(sentence_data_type::pointer sentence_data)
{
+ assert(m_database.isValid());
+ assert(m_database.isOpen());
+
+ m_insert_sentence.bindValue(0, sentence_data->source_id);
+ m_insert_sentence.bindValue(1, sentence_data->sentence);
+ m_insert_sentence.bindValue(2, sentence_data->json);
+ m_insert_sentence.bindValue(3, sentence_data->crc);
+ m_insert_sentence.bindValue(4, sentence_data->previous_crc);
+ m_insert_sentence.bindValue(5, sentence_data->next_crc);
+ m_insert_sentence.bindValue(6, sentence_data->user_id);
+ m_insert_sentence.bindValue(7, sentence_data->time);
+
+ exec(m_insert_sentence, Q_FUNC_INFO);
+}
+void TM::SentenceDatabase::update(sentence_data_type::pointer sentence_data)
+{
+ assert(m_database.isValid());
+ assert(m_database.isOpen());
+
+ m_update_sentence.bindValue(0, sentence_data->source_id);
+ m_update_sentence.bindValue(1, sentence_data->sentence);
+ m_update_sentence.bindValue(2, sentence_data->json);
+ m_update_sentence.bindValue(3, sentence_data->crc);
+ m_update_sentence.bindValue(4, sentence_data->previous_crc);
+ m_update_sentence.bindValue(5, sentence_data->next_crc);
+ m_update_sentence.bindValue(6, sentence_data->user_id);
+ m_update_sentence.bindValue(7, sentence_data->time);
+ m_update_sentence.bindValue(8, sentence_data->sentence_id);
+
+ exec(m_update_sentence, Q_FUNC_INFO);
}
-*/
-void TM::SentenceDatabase::insert(int source_id, QString sentence, QString json, int user_id)
+
+TM::sentence_data_type::pointer
+TM::SentenceDatabase::stuff_value(QSqlQuery *query)
{
- QSqlQuery *q = m_insert_sentence.get();
- q->bindValue(0, 0); // crc
- q->bindValue(1, source_id);
- q->bindValue(2, sentence);
- q->bindValue(3, json);
- q->bindValue(4, user_id);
- q->bindValue(5, 0); // time
- bool ret = q->exec();
- if(!ret) qFatal("An error occured in WordDatabase::insert_word().");
+ assert(query->isValid());
+ sentence_data_type::pointer result = sentence_data_type::create();
+
+ result->sentence_id = query->value(0).toUInt();
+ result->source_id = query->value(1).toUInt();
+ result->sentence = query->value(2).toString();
+ result->json = query->value(3).toByteArray();
+ result->crc = query->value(4).toUInt();
+ result->previous_crc = query->value(5).toUInt();
+ result->next_crc = query->value(6).toUInt();
+ result->user_id = query->value(7).toUInt();
+ result->time = query->value(8).toUInt();
+
+ return result;
}
TM::SentenceDatabase::pointer TM::SentenceDatabase::create(
// Database -------------------------------------------------------------------
-TM::Database::Database(Settings *settings, QObject *parent)
- : QObject(parent)
+TM::Database::Database(Settings *settings)
+ : QObject(0)
, m_settings(settings)
{
+ qRegisterMetaType<sentence_data_type::pointer>();
+}
+
+TM::Database::~Database()
+{
+}
+
+/*!
+ * \brief データベースのセットアップを行います。
+ *
+ * データベース接続を開いたスレッド内でしか使えない制限のため、スレッドの
+ * イベントループから処理します。
+ */
+void TM::Database::setup()
+{
// キャッシュの最大値を設定。
- m_sentence_cache_limit = settings->value(
+ m_sentence_cache_limit = m_settings->value(
"Database/sentence-cache-limit", QVariant::fromValue(20)).toInt();
// データベース用のフォルダが無ければ作成する。
- QString key("Database/root");
- if(!settings->contains(key)) qFatal("An error occured while find settings in Database().");
- QString path = settings->value(key).toString();
+ if(!m_settings->contains(TMDATABASE_ROOT_PATH_KEY)) qFatal("An error occured while find settings in Database().");
+ QString path = m_settings->value(TMDATABASE_ROOT_PATH_KEY).toString();
QDir dir(path);
if(!dir.exists()) dir.mkpath(path);
// データベースを開く。
- m_site_database = SiteDatabase::create(settings);
-}
-
-QString TM::Database::find_language_name(int code) const
-{
- QString result;
- QMap<int, QString>::const_iterator it = m_language_map.find(code);
- if(it != m_language_map.end()) result = it.value();
- return result;
-}
-
-int TM::Database::find_site_id(QString domain)
-{
- return m_site_database->site_id(domain);
+ m_site_database = SiteDatabase::create(m_settings);
}
/*!
m_word_databases.insert(code, WordDatabase::create(m_settings, name));
}
+quint32 TM::Database::find_site_id(QString host_name)
+{
+ quint32 result = m_site_database->site_id(host_name);
+ return result;
+}
+
+void TM::Database::find_sentence(quint32 site_id, int scode, int tcode,
+ QString sstring, sentence_callback callback)
+{
+ QByteArray json;
+
+ SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
+ assert(sdb);
+
+ SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
+ assert(tdb);
+
+ int source_id = sdb->find_sentence_id(sstring);
+ if(!source_id)
+ {
+ //tdb->
+ }
+}
+
+void TM::Database::insert_sentence(quint32 site_id,
+ quint32 scode, sentence_data_type::pointer source,
+ quint32 tcode, sentence_data_type::pointer target)
+{
+ assert(site_id);
+ assert(scode);
+ assert(tcode);
+
+ assert(source->source_id == 0);
+ assert(source->json.isEmpty());
+ assert(target->previous_crc == 0);
+ assert(target->next_crc == 0);
+
+ SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
+ assert(sdb);
+ SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
+ assert(tdb);
+
+ quint32 source_id = source->sentence_id;
+ if(!source_id) source_id = sdb->find_sentence_id_with_context(
+ source->sentence, source->previous_crc, source->next_crc);
+ if(!source_id)
+ {
+ source->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ sdb->insert(source);
+ source_id = sdb->find_sentence_id_with_context(
+ source->sentence, source->previous_crc, source->next_crc);
+ }
+ assert(source_id);
+
+ target->source_id = source_id;
+ target->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
+
+ sentence_data_type::pointer target_sentence =
+ tdb->find_sentence_by_source_id(source_id);
+ if(target_sentence)
+ {
+ target->sentence_id = target_sentence->sentence_id;
+ tdb->update(target);
+ }
+ else tdb->insert(target);
+}
+
+QString TM::Database::find_language_name(int code) const
+{
+ QString result;
+ QMap<int, QString>::const_iterator it = m_language_map.find(code);
+ if(it != m_language_map.end()) result = it.value();
+ return result;
+}
+
/*!
* \brief 引数として与えられた単語に対して一意な番号を返します。
* \param code 言語コード。
* センテンス・データベースは、設定によって決められた数まで接続をキャッシュします。
* このメンバは、キャッシュ内にデータベース接続があればそれを返し、無ければ接続します。
*/
-TM::SentenceDatabase::pointer TM::Database::find_sentence_database(int site_id, int code)
+TM::SentenceDatabase::pointer
+TM::Database::find_sentence_database(quint32 site_id, int code)
{
- typedef QMap<QPair<int, int>, SentenceDatabase::pointer>::iterator iterator;
+ typedef QMap<QPair<quint32, int>, SentenceDatabase::pointer>::iterator iterator;
+ assert(site_id);
+ assert(code);
// キャッシュが大きすぎる場合、消去する。
if(m_sentence_cache_limit < m_sentence_databases.size()) m_sentence_databases.clear();
// キャッシュにデータベースが無い場合、作成する。
- QPair<int,int> key(site_id, code);
+ QPair<quint32, int> key(site_id, code);
iterator it = m_sentence_databases.find(key);
if(it == m_sentence_databases.end())
{
QString name = find_language_name(code);
+ assert(!name.isEmpty());
it = m_sentence_databases.insert(
key, SentenceDatabase::create(m_settings, site_id, name));
}
+ assert(it.value());
+
return it.value();
}
-/*!
- * \brief 引数として与えられたセンテンスに対して一意な番号を返します。
- * \param site_id サイトを識別する番号。
- * \param code 言語コード。
- * \param sentence センテンス(文)。
- */
-int TM::Database::find_sentence_id(int site_id, int code, QString sentence)
-{
- SentenceDatabase::pointer p = find_sentence_database(site_id, code);
- return p->sentence_id(sentence);
-}
-bool TM::Database::find_sentence(int site_id, int scode, QString ssentence, int tcode,
- QString *tsentence, QJsonArray *json)
-{
- SentenceDatabase::pointer p = find_sentence_database(site_id, scode);
- //p->;
- return true;
-}
-void TM::Database::insert_sentence(int site_id, int code, int source_id,
- QString sentence, QString json, int user_id)
-{
-}
-QString TM::Database::root_key() { return "Database/root"; }
+
+
+
+
+
+
+
+
#include <QMap>
#include <QPair>
+#include <QJsonArray>
+#include <QTime>
+
+#include <QThread>
#include <memory>
+#include <functional>
+
+#define TMDATABASE_ROOT_PATH_KEY "Database/root"
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
namespace TM
{
+class Service;
+
+class DatabaseBase
+{
+protected:
+ bool open(char const *message);
+ QSqlQuery prepare(char const *sql, char const *message = nullptr);
+ bool exec(QSqlQuery &query, char const *message = nullptr);
+ QSqlQuery exec(char const *sql, char const *message = nullptr);
+ void error(QString message, QSqlQuery *query = nullptr);
+
+protected:
+ QSqlDatabase m_database;
+ QString m_database_name;
+};
+
/*!
* \brief サイトに番号を振るためのクラスです。
*/
-class SiteDatabase
+class SiteDatabase : public DatabaseBase
{
public:
typedef std::unique_ptr<SiteDatabase> pointer;
- typedef std::unique_ptr<QSqlQuery> query_pointer;
private:
SiteDatabase(Settings *settings);
public:
~SiteDatabase();
-
int site_id(QString domain);
+
private:
- int find_site_id(QString domain);
- void insert_site(QString domain);
+ int find_site_id(QString host_name);
+ void insert_site(QString host_name);
+
public:
static pointer create(Settings *settings);
private:
- QString m_database_name;
-
- QSqlDatabase m_database;
- query_pointer m_find_site_id;
- query_pointer m_insert_site;
+ QSqlQuery m_find_site_id;
+ QSqlQuery m_insert_site;
QMap<QString, int> m_cache;
int m_cache_limit;
/*!
* \brief 単語に番号を振るためのクラスです。
*/
-class WordDatabase
+class WordDatabase : public DatabaseBase
{
public:
typedef std::shared_ptr<WordDatabase> pointer;
- typedef std::unique_ptr<QSqlQuery> query_pointer;
private:
WordDatabase(Settings *settings, QString name);
static pointer create(Settings *settings, QString name);
private:
- QString m_database_name;
-
- QSqlDatabase m_database;
- query_pointer m_find_word_id;
- query_pointer m_insert_word;
+ QSqlQuery m_find_word_id;
+ QSqlQuery m_insert_word;
QMap<QString, int> m_cache;
int m_cache_limit;
};
-class SentenceDatabase
+class sentence_data_type
+{
+public:
+ typedef std::shared_ptr<sentence_data_type> pointer;
+
+ sentence_data_type();
+
+ static pointer create();
+
+ quint32 sentence_id; /*!< DBによって自動的に付与される文ID */
+ quint32 source_id; /*!< 原文は0固定、訳文は原文のsentence_id */
+ QString sentence; /*!< 文本体の文字列 */
+ QByteArray json; /*!< 原文は空、訳文はリンク情報のJSON */
+ quint32 crc; /*!< 検索に使う文のCRC */
+ quint32 previous_crc;/*!< 原文は文脈一致に使う前方文のCRC、訳文は0固定 */
+ quint32 next_crc; /*!< 原文は文脈一致に使う後方文のCRC、訳文は0固定 */
+ quint32 user_id; /*!< レコードを更新したユーザーのID */
+ quint64 time; /*!< 更新時刻 */
+};
+
+class SentenceDatabase : public DatabaseBase
{
public:
typedef std::shared_ptr<SentenceDatabase> pointer;
- typedef std::unique_ptr<QSqlQuery> query_pointer;
+
+
+ typedef QList<sentence_data_type> sentences_data_type;
private:
SentenceDatabase(Settings *settings, int site_id, QString name);
public:
~SentenceDatabase();
- int sentence_id(QString sentence);
+ quint32 find_sentence_id(QString sentence);
+ quint32 find_sentence_id_with_context(
+ QString sentence, quint32 previous_crc, quint32 next_crc);
-// bool find_sentence_by_source_sentence(
-// QString sentence, QString *tsentence, QJsonArray *json = nullptr);
bool find_sentence_by_crc(
quint32 crc, QString *tsentence, QJsonArray *json = nullptr);
- bool find_sentence_by_source_id(
- int source_id, QString *tsentence, QJsonArray *json = nullptr);
- void insert(int source_id, QString sentence, QString json, int user_id);
+ sentence_data_type::pointer find_sentence_by_source_id(int source_id);
-private:
+ void insert(sentence_data_type::pointer sentence_data);
+ void update(sentence_data_type::pointer sentence_data);
+private:
+ sentence_data_type::pointer stuff_value(QSqlQuery *query);
public:
static pointer create(Settings *settings, int site_id, QString name);
private:
- QString m_database_name;
-
- QSqlDatabase m_database;
-
- query_pointer m_find_sentence_id;
- query_pointer m_find_sentence;
- query_pointer m_find_sentence_by_crc;
- query_pointer m_find_sentence_by_source_id;
- query_pointer m_insert_sentence;
+ QSqlQuery m_find_sentence_id;
+ QSqlQuery m_find_sentence_id_with_context;
+ QSqlQuery m_find_sentence;
+ QSqlQuery m_find_sentence_by_crc;
+ QSqlQuery m_find_sentence_by_source_id;
+ QSqlQuery m_insert_sentence;
+ QSqlQuery m_update_sentence;
};
class IndexDatabase
class Database : public QObject
{
Q_OBJECT
+public:
+ typedef std::function<void(sentence_data_type)> sentence_callback;
public:
- Database(Settings *settings, QObject *parent = 0);
+ Database(Settings *settings);
+ ~Database();
- QString find_language_name(int code) const;
+signals:
- int find_site_id(QString domain);
+public slots:
+ void setup();
void open_word_database(int code, QString name);
- int find_word_id(int code, QString word);
+
+ quint32 find_site_id(QString host_name);
+
+ void find_sentence(quint32 site_id, int scode, int tcode, QString sstring,
+ sentence_callback callback);
+ void insert_sentence(quint32 site_id,
+ quint32 scode, sentence_data_type::pointer source,
+ quint32 tcode, sentence_data_type::pointer target);
+
private:
- SentenceDatabase::pointer find_sentence_database(int site_id, int code);
-public:
- int find_sentence_id(int site_id, int code, QString sentence);
- bool find_sentence(int site_id, int scode, QString ssentence, int tcode,
- QString *tsentence, QJsonArray *json);
- void insert_sentence(int site_id, int code, int source_id,
- QString sentence, QString json, int user_id);
+ QString find_language_name(int code) const;
- static QString root_key();
-signals:
+ int find_word_id(int code, QString word);
-public slots:
+ SentenceDatabase::pointer find_sentence_database(quint32 site_id, int code);
private:
Settings *m_settings;
- QMap<int, QString> m_language_map; /*!< è¨\80èª\9eã\82³ã\83¼ã\83\89ã\81¨è¨\80èª\9eå\90\8dã\81®ã\83\9eã\83\83ã\83\97 */
+ QMap<int, QString> m_language_map; /*!< è¨\80èª\9eã\82³ã\83¼ã\83\89ã\80\81è¨\80èª\9eå\90\8d */
SiteDatabase::pointer m_site_database;
- QMap<int, WordDatabase::pointer> m_word_databases;
- QMap<QPair<int, int>, SentenceDatabase::pointer> m_sentence_databases;
- int m_sentence_cache_limit;
+ QMap<int, WordDatabase::pointer> m_word_databases; /*!< 言語コード, 単語データベース */
+ /*! QPair<サイトID、言語コード>、文データベース */
+ QMap<QPair<quint32, int>, SentenceDatabase::pointer> m_sentence_databases;
+ int m_sentence_cache_limit; /*!< 文データベース接続の最大値 */
};
+Q_DECLARE_METATYPE(sentence_data_type::pointer)
+//Q_DECLARE_METATYPE(quint32)
+
} // namespace TM
#endif // TMDATABASE_H
m_edit->set_segment(segment);
}
-void TM::EditorWidget::save_sentence(int segment_id, int index,
- Text::pointer target_sentence, QJsonArray link)
+void TM::EditorWidget::save_sentence(int segment_id, int index)
{
- qDebug() << "save: " << link;
- m_socket->save_sentence(segment_id, index, target_sentence, link);
+ m_socket->save_sentence(segment_id, index);
}
/*!
TM::EditorPanel::EditorPanel(QWidget *parent)
: TextPanel(parent)
- , m_editor(nullptr)
+ , m_parent_editor(nullptr)
{
}
+TM::Editor* TM::EditorPanel::parent_editor() { return m_parent_editor; }
+
/*!
* \brief 親となるエディタを設定します。
*/
-void TM::EditorPanel::set_editor(Editor *editor)
+void TM::EditorPanel::set_parent_editor(Editor *editor)
{
- assert(!m_editor);
- m_editor = editor;
+ assert(!m_parent_editor);
+ m_parent_editor = editor;
}
/*!
- * \brief 保持している文を返します。
- */
-Text::pointer TM::EditorPanel::sentence() { return m_sentence; }
-
-/*!
- * \brief 文を設定します。
+ * \brief 文を再表示します。
*/
-void TM::EditorPanel::set_sentence(Text::pointer sentence)
+void TM::EditorPanel::ensure_sentence()
{
- assert(sentence);
- m_sentence = sentence;
+ if(!sentence()) return;
clear();
QTextCursor c = textCursor();
- for(Text::pointer p = sentence->begin(); p; p = p->next()) // p=word
+ for(Text::pointer p = sentence()->begin(); p; p = p->next()) // p=word
{
QTextCharFormat cf;
QVariant v = QVariant::fromValue(Text::weak_pointer(p));
for(Text::pointer p : *link) highlight(p, color);
}
-void TM::EditorPanel::clear_highlight()
-{
- if(m_sentence) set_sentence(m_sentence);
-}
+void TM::EditorPanel::clear_highlight() { ensure_sentence(); }
/*!
* \brief 引数として与えられた整数値から色を作成します。
void TM::SourcePanel::set_index(int index) { m_index = index; }
+Text::pointer TM::SourcePanel::sentence() { return source_sentence(); }
+
+Text::pointer TM::SourcePanel::source_sentence()
+{
+ return m_text_sentence->source_sentence();
+}
+
+Text::pointer TM::SourcePanel::target_sentence()
+{
+ return m_text_sentence->target_sentence();
+}
+
+void TM::SourcePanel::set_target_sentence(Text::pointer sentence)
+{
+ m_text_sentence->set_target_sentence(sentence);
+}
+
+TM::TextSentence::pointer TM::SourcePanel::text_sentence()
+{
+ return m_text_sentence;
+}
+
+void TM::SourcePanel::set_text_sentence(TextSentence::pointer text_sentence)
+{
+ m_text_sentence = text_sentence;
+ ensure_sentence();
+ assert(target_panel());
+ target_panel()->ensure_sentence();
+}
+
TM::TargetPanel* TM::SourcePanel::target_panel() { return m_target_panel; }
void TM::SourcePanel::set_target_panel(TargetPanel *target)
void TM::SourcePanel::commit_link()
{
- m_linker.commit();
+ linker()->commit();
clear_highlight();
m_target_panel->clear_highlight();
}
TM::WordLinker* TM::SourcePanel::linker()
{
- return &m_linker;
+ return m_text_sentence->linker();
}
/*!
clear_highlight();
m_target_panel->clear_highlight();
- WordLink::pointer wl = m_linker.current();
+ WordLink::pointer wl = linker()->current();
if(wl)
{
highlight(wl->sources(), Qt::cyan);
target_panel()->highlight(wl->targets(), Qt::cyan);
}
- for(WordLink::pointer wl : m_linker)
+ for(WordLink::pointer wl : *linker())
{
- int index = m_linker.index_of(wl);
+ int index = linker()->index_of(wl);
QColor c = color(index);
highlight(wl->sources(), c);
target_panel()->highlight(wl->targets(), c);
void TM::SourcePanel::keyPressEvent(QKeyEvent *ev)
{
+ Editor *editor = parent_editor();
int key = ev->key();
switch(key)
{
case Qt::Key_Enter:
case Qt::Key_Return:
- if(m_editor->link_mode()) m_editor->set_link_mode(false);
+ if(editor->link_mode()) editor->set_link_mode(false);
break;
case Qt::Key_C:
if(!ev->modifiers().testFlag(Qt::ControlModifier)) break;
void TM::SourcePanel::do_click(QPoint const &pos)
{
- if(m_editor->link_mode()) do_click_in_link_mode(pos);
+ Editor *editor = parent_editor();
+ if(editor->link_mode()) do_click_in_link_mode(pos);
}
void TM::SourcePanel::do_click_in_link_mode(QPoint const &pos)
Text::pointer w = select_word(pos);
if(!w) return;
- m_linker.toggle(WordLink::Source, w);
+ linker()->toggle(WordLink::Source, w);
ensure_highlight();
setTextCursor(cursorForPosition(pos));
}
-void TM::SourcePanel::do_focusin()
-{
- //if(m_target_panel) m_target_panel->show();
-}
-
-void TM::SourcePanel::do_focusout()
-{
-}
-
// TargetPanel ----------------------------------------------------------------
TM::TargetPanel::TargetPanel(QWidget *parent)
m_source_panel = source;
}
-void TM::TargetPanel::set_sentence(Text::pointer sentence)
+Text::pointer TM::TargetPanel::sentence()
+{
+ return source_panel()->target_sentence();
+}
+
+void TM::TargetPanel::save_sentence()
+{
+
+}
+
+void TM::TargetPanel::ensure_sentence()
{
- EditorPanel::set_sentence(sentence);
+ EditorPanel::ensure_sentence();
set_text_dirty(false);
set_text_saved(false);
}
*/
void TM::TargetPanel::set_text_dirty(bool dirty)
{
+ EditorWidget *editor_widget = parent_editor()->parent_editor_widget();
m_text_dirty = dirty;
- if(dirty) m_editor->parent_editor_widget()->set_link_mode_disabled(false);
+ if(dirty) editor_widget->set_link_mode_disabled(false);
}
bool TM::TargetPanel::canInsertFromMimeData(QMimeData const *source) const
void TM::TargetPanel::inputMethodEvent(QInputMethodEvent *ev)
{
- if(m_editor->link_mode()) ev->setCommitString("");
+ Editor *editor = parent_editor();
+ if(editor->link_mode()) ev->setCommitString("");
if(!ev->commitString().isEmpty())
{
set_text_dirty(true);
void TM::TargetPanel::keyPressEvent(QKeyEvent *ev)
{
- if(m_editor->link_mode()) do_key_press_in_link_mode(ev);
+ Editor *editor = parent_editor();
+ if(editor->link_mode()) do_key_press_in_link_mode(ev);
else
{
TextPanel::keyPressEvent(ev);
void TM::TargetPanel::do_click(QPoint const &pos)
{
- if(m_editor->link_mode()) do_click_in_link_mode(pos);
+ Editor *editor = parent_editor();
+ if(editor->link_mode()) do_click_in_link_mode(pos);
}
void TM::TargetPanel::do_click_in_link_mode(QPoint const &pos)
setTextCursor(cursorForPosition(pos));
}
-void TM::TargetPanel::do_focusin()
-{
- //show();
-}
-
-void TM::TargetPanel::do_focusout()
-{
- //hide();
-}
-
void TM::TargetPanel::do_key_press_in_link_mode(QKeyEvent *ev)
{
+ Editor *editor = parent_editor();
int key = ev->key();
switch(key)
{
case Qt::Key_Enter:
case Qt::Key_Return:
- m_editor->set_link_mode(false);
+ editor->set_link_mode(false);
break;
case Qt::Key_Left:
case Qt::Key_Right:
int i = 0;
for(TextSentence::pointer p : *segment)
{
- Text::pointer s = p->source_sentence();
- Text::pointer t = p->target_sentence();
-
SourcePanel *sp = ta->append<SourcePanel>();
TargetPanel *tp = ta->append<TargetPanel>();
sp->set_index(i++);
sp->set_target_panel(tp);
- sp->set_editor(this);
+ sp->set_parent_editor(this);
tp->set_source_panel(sp);
- tp->set_editor(this);
+ tp->set_parent_editor(this);
- sp->set_sentence(s);
- if(t) tp->set_sentence(t);
+ sp->set_text_sentence(p);
sp->show();
}
if(!tp->is_text_saved())
{
- int tcode = parent_editor_widget()->target_language();
-
- if(tp->is_text_dirty())
- {
- panel->linker()->clear();
- QString string = tp->toPlainText();
- Text::pointer sentences = m_service->divide_into_sentences(tcode, string);
- if(sentences->size())
- {
- Text::pointer words = m_service->divide_into_words(tcode, sentences->begin());
- tp->set_sentence(words); // ココで、text_dirtyがfalseになる。
- }
- }
-
- parent_editor_widget()->save_sentence(m_segment_id, panel->index(),
- tp->sentence(), panel->linker()->to_json_array());
+ if(tp->is_text_dirty()) divide_target_sentence(panel);
+ int index = panel->index();
+ parent_editor_widget()->save_sentence(m_segment_id, index);
tp->set_text_saved(true);
}
}
assert(panel);
TargetPanel *tp = panel->target_panel();
assert(tp);
- int tcode = parent_editor_widget()->target_language();
-
- if(tp->is_text_dirty())
- {
- panel->linker()->clear();
- QString string = tp->toPlainText();
- Text::pointer sentences = m_service->divide_into_sentences(tcode, string);
- if(sentences->size())
- {
- Text::pointer words = m_service->divide_into_words(tcode, sentences->begin());
- tp->set_sentence(words); // ココで、text_dirtyがfalseになる。
- }
- }
+ if(tp->is_text_dirty()) divide_target_sentence(panel);
panel->ensure_highlight();
}
*/
void TM::Editor::do_link_mode_leave(SourcePanel *panel)
{
- qDebug() << m_current_source_panel->linker()->to_json_array();
+ qDebug() << m_current_source_panel->linker()->to_json();
assert(panel);
panel->commit_link();
//panel->clear_highlight();
}
+/*!
+ * \brief 訳文パネルの文を単語に分割し、表示に反映します。
+ */
+void TM::Editor::divide_target_sentence(SourcePanel *source_panel)
+{
+ int tcode = parent_editor_widget()->target_language();
+ TargetPanel *tp = source_panel->target_panel();
+ QString string = tp->toPlainText();
+
+ Text::pointer sentences = m_service->divide_into_sentences(tcode, string);
+ if(sentences->size())
+ {
+ source_panel->linker()->clear();
+ Text::pointer words = m_service->divide_into_words(tcode, sentences->begin());
+ source_panel->set_target_sentence(words);
+ tp->ensure_sentence(); // ココで、text_dirtyがfalseになる。
+ }
+}
int target_language();
void set_segment(TextSegment::pointer segment);
- void save_sentence(int segment_id, int index,
- Text::pointer target_sentence, QJsonArray link);
+ void save_sentence(int segment_id, int index);
signals:
void editModeChanged(bool mode_);
public:
explicit EditorPanel(QWidget *parent);
- void set_editor(Editor *editor);
- Text::pointer sentence();
- virtual void set_sentence(Text::pointer sentence);
+ Editor* parent_editor();
+ void set_parent_editor(Editor *editor);
+
+ virtual Text::pointer sentence() = 0;
+ virtual void ensure_sentence();
bool is_empty() const;
QColor color(int index) const;
-protected:
- Editor *m_editor;
- Text::pointer m_sentence;
+private:
+ Editor *m_parent_editor;
};
class SourcePanel : public EditorPanel
int index() const;
void set_index(int index);
+ Text::pointer sentence();
+ Text::pointer source_sentence();
+ Text::pointer target_sentence();
+ void set_target_sentence(Text::pointer sentence);
+
+ TextSentence::pointer text_sentence();
+ void set_text_sentence(TextSentence::pointer text_sentence);
+
TargetPanel* target_panel();
void set_target_panel(TargetPanel *target);
void inputMethodEvent(QInputMethodEvent *ev);
void keyPressEvent(QKeyEvent *ev);
- //void do_enter();
void do_click(QPoint const &pos);
void do_click_in_link_mode(QPoint const &pos);
- void do_focusin();
- void do_focusout();
-
private:
- int m_index;
+ int m_index; /*!< Editor上での位置を示す索引 */
TargetPanel *m_target_panel;
- WordLinker m_linker;
+ TextSentence::pointer m_text_sentence;
};
class TargetPanel: public EditorPanel
SourcePanel* source_panel();
void set_source_panel(SourcePanel *source);
- void set_sentence(Text::pointer sentence);
+ Text::pointer sentence();
+ void save_sentence();
+ void ensure_sentence();
bool is_text_saved() const;
void set_text_saved(bool saved);
void inputMethodEvent(QInputMethodEvent *ev);
void keyPressEvent(QKeyEvent *ev);
- //void do_enter();
void do_click(QPoint const &pos);
void do_click_in_link_mode(QPoint const &pos);
- void do_focusin();
- void do_focusout();
-
void do_key_press_in_link_mode(QKeyEvent *ev);
private:
bool can_link_mode() const;
EditorWidget* parent_editor_widget();
+private:
TargetPanel* current_target_panel();
TargetPanel const* current_target_panel() const;
void do_link_mode_leave(SourcePanel *panel);
private:
+ void divide_target_sentence(SourcePanel *source_panel);
+
+private:
Settings *m_settings;
Service *m_service;
node = head.insert("script", head.end()).set_attribute("type", "text/javascript");
QString str1 = "\nwindow.wordring.port=";
str1 += QString::number(m_context->socket_port()) + ";\n";
+ str1 += "window.wordring.url='";
+ str1 += m_targetUrl.toString() + "';\n";
node.insert_comment(str1, node.end());
// パラグラフ設定
create_paragraph(body.lbegin(), body.ltail());
: QObject(parent)
, m_settings(settings)
, m_mutex(QMutex::Recursive)
- , m_database(nullptr)
+ , m_database_thread(new QThread(this))
+ , m_database(new Database(settings))
{
- m_database = new Database(settings, this);
+ setup_crc_table();
+
+ m_database->moveToThread(m_database_thread);
+ connect(m_database_thread, SIGNAL(finished()), m_database, SLOT(deleteLater()));
+ m_database_thread->start();
+ QMetaObject::invokeMethod(
+ m_database, "setup",
+ Qt::BlockingQueuedConnection);
+}
+
+TM::Service::~Service()
+{
+ m_database_thread->quit();
+ m_database_thread->wait();
+}
+
+void TM::Service::setup_crc_table()
+{
+ for (quint32 i = 0; i < 256; i++)
+ {
+ quint32 c = i;
+ for (int j = 0; j < 8; j++)
+ c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
+ m_crc_table[i] = c;
+ }
+}
+
+quint32 TM::Service::crc32(QString const &string)
+{
+ quint32 c = 0xFFFFFFFF;
+ QByteArray const ba = string.toUtf8();
+ for (int i = 0; i < ba.size(); i++)
+ {
+ c = m_crc_table[(c ^ ba.at(i)) & 0xFF] ^ (c >> 8);
+ }
+ return c ^ 0xFFFFFFFF;
+}
+
+quint32 TM::Service::crc32(int code, QString const &string)
+{
+ assert(m_languages.contains(code));
+ Language *language = m_languages[code];
+ QString s = language->normalize(string);
+ assert(!s.isEmpty());
+ return crc32(s);
}
void TM::Service::load_languages(QString const &path)
m_languages[code] = language;
language->set_settings(m_settings);
- m_database->open_word_database(code, name);
emit languageLoaded(code, name, language->icon());
+ QMetaObject::invokeMethod(
+ m_database, "open_word_database",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(int, code),
+ Q_ARG(QString, name));
}
}
}
return m_languages[code]->divide_into_words(sentence);
}
+quint32 TM::Service::find_site_id(QString host_name)
+{
+ quint32 result;
+ QMetaObject::invokeMethod(
+ m_database, "find_site_id",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(quint32, result),
+ Q_ARG(QString, host_name));
+ return result;
+}
+
/*!
- * \brief å\8e\9fæ\96\87ã\81¨è¨³æ\96\87ã\81®å¯¾ã\82\92ã\83\87ã\83¼ã\82¿ã\83\99ã\83¼ã\82¹ã\81¸ç\99»é\8c²します。
+ * \brief å\8e\9fæ\96\87ã\81\8bã\82\89訳æ\96\87ã\82\92æ¤\9cç´¢します。
* \param site_id 翻訳対象サイトを表すID。
* \param scode 原文の言語コード。
- * \param ssentence 原文の構造化テキスト。
* \param tcode 訳文の言語コード。
- * \param tsentence 訳文の構造化テキスト。
- * \param json 原文と訳文間の単語リンク。
+ * \param ssentence 原文の構造化テキスト。
+ * \param 結果を返すコールバック関数。
*/
-void TM::Service::insert_sentence(int site_id, int scode, Text::pointer ssentence,
- int tcode, Text::pointer tsentence, QJsonArray json)
+void TM::Service::find_sentence(quint32 site_id, int scode, int tcode,
+ Text::pointer ssentence, Database::sentence_callback callback)
{
- QMutexLocker lock(&m_mutex);
-
assert(m_languages.contains(scode));
- assert(m_languages.contains(tcode));
Language *slanguage = m_languages[scode];
- Language *tlanguage = m_languages[tcode];
-
- QString sstring = slanguage->normalize(ssentence->to_string());
- QString tstring = tlanguage->normalize(tsentence->to_string());
- //m_database->
+ QString sstring = ssentence->to_string();
+ assert(!sstring.isEmpty());
+ sstring = slanguage->normalize(sstring);
+ assert(!sstring.isEmpty());
+
+ QMetaObject::invokeMethod(
+ m_database, "find_sentence",
+ Qt::QueuedConnection,
+ Q_ARG(quint32, site_id),
+ Q_ARG(qint32, scode),
+ Q_ARG(qint32, tcode),
+ Q_ARG(QString, sstring),
+ Q_ARG(Database::sentence_callback, callback));
}
/*!
- * \brief å\8e\9fæ\96\87ã\81\8bã\82\89訳æ\96\87ã\82\92æ¤\9cç´¢します。
+ * \brief å\8e\9fæ\96\87ã\81¨è¨³æ\96\87ã\81®å¯¾ã\82\92ã\83\87ã\83¼ã\82¿ã\83\99ã\83¼ã\82¹ã\81¸ç\99»é\8c²します。
* \param site_id 翻訳対象サイトを表すID。
* \param scode 原文の言語コード。
- * \param ssentence 原文の構造化テキスト。
* \param tcode 訳文の言語コード。
- * \param tsentence 訳文の構造化テキストを受け取るポインタ。
- * \param json 原文と訳文間の単語リンクを受け取るポインタ。
- * \return 訳文が検索できた場合、trueを返します。
+ * \param sentence 挿入する原文、訳文の対。
*/
-bool TM::Service::find_sentence(int site_id, int scode, Text::pointer ssentence, int tcode,
- Text::pointer *tsentence, QJsonArray *json)
+void TM::Service::insert_sentence(quint32 site_id, int scode, int tcode,
+ TextSentence::pointer sentence, quint32 previous_crc, quint32 next_crc)
+
{
- return true;
+ sentence_data_type::pointer source_sentence_data =
+ stuff_sentence_data(scode, sentence->source_sentence());
+ source_sentence_data->previous_crc = previous_crc;
+ source_sentence_data->next_crc = next_crc;
+
+ sentence_data_type::pointer target_sentence_data =
+ stuff_sentence_data(tcode, sentence->target_sentence());
+ target_sentence_data->json = sentence->to_json();
+
+ QMetaObject::invokeMethod(
+ m_database, "insert_sentence",
+ Qt::QueuedConnection,
+ Q_ARG(quint32, site_id),
+ Q_ARG(quint32, scode),
+ Q_ARG(sentence_data_type::pointer, source_sentence_data),
+ Q_ARG(quint32, tcode),
+ Q_ARG(sentence_data_type::pointer, target_sentence_data));
+}
+
+TM::sentence_data_type::pointer TM::Service::stuff_sentence_data(
+ int code, Text::pointer text)
+{
+ assert(m_languages.contains(code));
+ Language *language = m_languages[code];
+
+ QString string = text->to_string();
+ assert(!string.isEmpty());
+ string = language->normalize(string);
+ assert(!string.isEmpty());
+
+ sentence_data_type::pointer result = sentence_data_type::create();
+
+ result->sentence = string;
+
+ return result;
}
#define TMSERVICE_H
#include "language.h"
+#include "tmtext.h"
+#include "tmdatabase.h"
#include <QObject>
#include <QString>
namespace TM
{
-class Database;
-
class Service : public QObject
{
Q_OBJECT
public:
Service(Settings *settings, QObject *parent = 0);
+ ~Service();
+
+ // CRC32
+ void setup_crc_table();
+ quint32 crc32(QString const &string);
+ quint32 crc32(int code, QString const &string);
+ // 言語プラグイン
void load_languages(QString const &path);
Text::pointer divide_into_sentences(int code, QString string);
Text::pointer divide_into_words(int code, Text::pointer sentence);
- void insert_sentence(int site_id, int scode, Text::pointer ssentence,
- int tcode, Text::pointer tsentence, QJsonArray json);
- bool find_sentence(int site_id, int scode, Text::pointer ssentence, int tcode,
- Text::pointer *tsentence, QJsonArray *json);
+ // データベース
+ quint32 find_site_id(QString host_name);
+ void insert_sentence(quint32 site_id, int scode, int tcode,
+ TextSentence::pointer sentence, quint32 previous_crc, quint32 next_crc);
+
+ void find_sentence(quint32 site_id, int scode, int tcode,
+ Text::pointer ssentence, Database::sentence_callback callback);
+private:
+ sentence_data_type::pointer stuff_sentence_data(int code, Text::pointer text);
+public:
signals:
/*!
* \brief 言語プラグインが読み込まれたときに発火するシグナルです。
QMutex m_mutex;
Settings *m_settings;
+
+ quint32 m_crc_table[256];
+
QMap<int, Language*> m_languages;
+
+ QThread *m_database_thread;
Database *m_database;
};
#include "settings.h"
#include "html.h"
+#include "htmltag.h"
#include <QMutex>
#include <QMutexLocker>
int TM::HtmlData::type() const { return Type; }
+HtmlNode TM::HtmlData::node() { return m_node; }
+
+QString TM::HtmlData::debug_dump() const
+{
+ QString result;
+ result += "[HtmlData:";
+ result += m_node.name() + ",";
+ result += QString::number(begin()) + ",";
+ result += QString::number(tail()) + "]";
+ return result;
+}
+
TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_)
{
return pointer(new HtmlData(node_, begin_, tail_));
return false;
}
-// TextSentence ---------------------------------------------------------------
+// HtmlConverter --------------------------------------------------------------
+
-TM::TextSentence::TextSentence(Text::pointer source_sentence)
- : m_source_sentence(source_sentence)
+void TM::HtmlConverter::append(QString string)
{
+ HtmlText::pointer text = HtmlText::create(HtmlText::weak_pointer());
+ text->set_value(string);
+ m_nodes.append(text);
}
-Text::pointer TM::TextSentence::source_sentence() { return m_source_sentence; }
+void TM::HtmlConverter::append(HtmlNode::pointer node, QString string)
+{
+ QList<HtmlNode::pointer> left, right;
+ for(HtmlNode hn = node->parent(); hn; hn = hn.parent())
+ {
+ if(hn.tname() == "body") break;
+ left.prepend(hn.lself());
+ right.append(hn.ltail());
+ }
-Text::pointer TM::TextSentence::target_sentence() { return m_target_sentence; }
+ m_nodes.append(left);
+ append(string);
+ m_nodes.append(right);
+}
-void TM::TextSentence::set_terget_sentence(Text::pointer target_sentence)
+QString TM::HtmlConverter::to_string()
{
- m_target_sentence = target_sentence;
-}
+ QString result;
-QJsonArray TM::TextSentence::link_data() { return m_link_data; }
+ adjust();
+ for(Html::pointer node : m_nodes) result += node->to_string();
-void TM::TextSentence::set_link_data(QJsonArray json) { m_link_data = json; }
+ return result;
+}
-TM::TextSentence::pointer TM::TextSentence::create(Text::pointer source_sentence)
+void TM::HtmlConverter::adjust()
{
- return pointer(new TextSentence(source_sentence));
+ int adjusted = 0;
+ do
+ {
+ adjusted = 0;
+ for(int i = 0; i < m_nodes.size() - 1; i++)
+ {
+ HtmlNode::pointer p1 = m_nodes.at(i);
+ if(p1->type() != Html::Element || p1->place() != Html::Close) continue;
+ HtmlNode::pointer p2 = m_nodes.at(i + 1);
+ if(p2->type() != Html::Element || p2->place() != Html::Open) continue;
+
+ if(p1->lbegin() == p2->lbegin())
+ {
+ m_nodes.removeAt(i);
+ m_nodes.removeAt(i);
+ ++adjusted;
+ }
+ }
+ }
+ while(adjusted);
}
// TextSegment ----------------------------------------------------------------
for(Text::pointer s = sentences->begin(); s; s = s->next()) // s: sentence
{
Text::pointer words = service->divide_into_words(scode, s);
- m_sentences.append(TextSentence::create(words));
+ m_sentences.append(TextSentence::create(segment_id, words));
}
}
int TM::TextSegment::segment_id() const { return m_segment_id; }
+/*!
+ * \brief 引数で指定されたインデックスの文について、CRC32を返します。
+ * \param service サービスへのポインタ。
+ * \param code 言語コード。
+ * \param index 文のインデックス。
+ * \return CRC32。
+ *
+ * このメンバは、不正なインデックスを指定した場合、0を返します。
+ */
+quint32 TM::TextSegment::crc32(Service *service, int code, int index)
+{
+ if(index < 0 || size() <= index) return 0;
+
+ TextSentence::pointer sentence = at(index);
+ quint32 result = sentence->crc32();
+ if(!result)
+ {
+ result = service->crc32(code, sentence->source_sentence()->to_string());
+ sentence->set_crc32(result);
+ }
+ return result;
+}
+
+int TM::TextSegment::size() const { return m_sentences.size(); }
+
TM::TextSentence::pointer TM::TextSegment::at(int index)
{
+ assert(0 <= index && index < m_sentences.size());
return m_sentences.at(index);
}
TM::TextSegment::iterator TM::TextSegment::end() { return m_sentences.end(); }
+HtmlNode::pointer TM::TextSegment::find_html_node_by_offset(int offset)
+{
+ HtmlNode::pointer result;
+
+ for(Text::pointer p = m_text->begin(); p; p = p->next())
+ {
+ UserData::pointer ud = p->data();
+ assert(ud->type() == HtmlData::Type);
+ HtmlData *hd = static_cast<HtmlData*>(ud.get());
+ if(hd->begin() <= offset && offset <= hd->tail())
+ {
+ result = hd->node().lself();
+ break;
+ }
+ }
+ return result;
+}
+
+/*!
+ * \brief セグメント全体をHTML文字列に変換します。
+ */
+QString TM::TextSegment::to_html()
+{
+ QString result;
+ for(TextSentence::pointer sentence : m_sentences)
+ {
+ if(!sentence->target_sentence())
+ result += to_html_from_source(sentence);
+ else result += to_html_from_target(sentence);
+ result += "\r\n";
+ }
+ return result;
+}
+
+/*!
+ * \brief 原文をHTMLに変換します。
+ *
+ * 訳文のついていない文のために在ります。
+ */
+QString TM::TextSegment::to_html_from_source(TextSentence::pointer sentence)
+{
+ HtmlConverter hc;
+
+ UserData *ud = sentence->source_sentence()->data().get();
+ assert(ud->type() == RangeData::Type);
+ RangeData *rd = static_cast<RangeData*>(ud);
+ int sentence_offset = rd->begin();
+
+ Text::pointer source_sentence = sentence->source_sentence();
+ if(!source_sentence) return "";
+
+ for(Text::pointer word = source_sentence->begin(); word; word = word->next())
+ {
+ QString string = word->to_string();
+ UserData *ud = word->data().get();
+ assert(ud->type() == RangeData::Type);
+ RangeData *rd = static_cast<RangeData*>(ud);
+ int word_offset = sentence_offset + rd->begin();
+ HtmlNode::pointer node = find_html_node_by_offset(word_offset);
+ if(node) hc.append(node, string);
+ else hc.append(string);
+ }
+ return hc.to_string();
+}
+
+/*!
+ * \brief 訳文をHTMLに変換します。
+ */
+QString TM::TextSegment::to_html_from_target(TextSentence::pointer sentence)
+{
+ HtmlConverter hc;
+
+ WordLinker* linker = sentence->linker();
+ UserData *ud = sentence->source_sentence()->data().get();
+ assert(ud->type() == RangeData::Type);
+ RangeData *rd = static_cast<RangeData*>(ud);
+ int sentence_offset = rd->begin();
+
+ Text::pointer target_sentence = sentence->target_sentence();
+ if(!target_sentence) return "";
+
+ for(Text::pointer word = target_sentence->begin(); word; word = word->next())
+ {
+ QString string = word->to_string();
+ WordLink::pointer link = linker->find(WordLink::Target, word);
+ if(!link) hc.append(string);
+ else
+ {
+ UserData::pointer ud = link->sources()->at(0)->data();
+ assert(ud->type() == RangeData::Type);
+ RangeData *rd = static_cast<RangeData*>(ud.get());
+ int word_offset = sentence_offset + rd->begin();
+ HtmlNode::pointer node = find_html_node_by_offset(word_offset);
+ if(node) hc.append(node, string);
+ else hc.append(string);
+ }
+ }
+ return hc.to_string();
+}
+
TM::TextSegment::pointer TM::TextSegment::create(
Service *service, int scode, int segment_id, QString source)
{
: QObject(socket)
, m_settings(settings)
, m_service(service)
- , m_editor_widget(editor_widget)
, m_mutex(QMutex::Recursive)
+ , m_editor_widget(editor_widget)
+ , m_site_id(0)
, m_edit_mode(false)
+ , m_source_language_code(0)
+ , m_target_language_code(0)
{
connect(socket, SIGNAL(textMessageReceived(QString const&)),
this, SLOT(onTextMessageReceived(QString const&)));
QWebSocket* TM::SocketConnection::socket()
{
QWebSocket * result = qobject_cast<QWebSocket*>(parent());
- assert(result);
return result;
}
void TM::SocketConnection::send_message(QString const &message)
{
- socket()->sendTextMessage(message);
+ QWebSocket *ws = socket();
+ if(ws) ws->sendTextMessage(message);
}
void TM::SocketConnection::send_message(QJsonObject const &json)
send_message(doc.toJson().data());
}
-void TM::SocketConnection::save_sentence(int segment_id, int index,
- Text::pointer target_sentence, QJsonArray link)
+/*!
+ * \brief データベースへセンテンスの登録とブラウザへの反映を行います。
+ * \param segment_id セグメントのID。
+ * \param index センテンスのインデックス。
+ */
+void TM::SocketConnection::save_sentence(int segment_id, int index)
{
+ assert(m_site_id);
+
+ // セグメントの検索。
segment_map_iterator it = m_segments.find(segment_id);
assert(it != m_segments.end());
- TextSentence::pointer sentence = it.value()->at(index);
- sentence->set_terget_sentence(target_sentence);
- sentence->set_link_data(link);
+ TextSegment::pointer segment = it.value();
+
+ // センテンスの検索。
+ TextSentence::pointer sentence = segment->at(index);
+ assert(sentence->source_sentence());
+ if(!sentence->target_sentence()) return;
+
+ // 言語コード。
+ int scode = m_editor_widget->source_language();
+ int tcode = m_editor_widget->target_language();
+
+ // ソースのCRC
+ quint32 previous_crc = segment->crc32(m_service, scode, index - 1);
+ quint32 next_crc = segment->crc32(m_service, scode, index + 1);
+
+ // データベースへ登録。
+ m_service->insert_sentence(m_site_id, scode, tcode, sentence, previous_crc, next_crc);
+
+ // ブラウザへ反映。
+ set_segment(segment_id, segment->to_html());
}
void TM::SocketConnection::changeEditMode(bool edit_mode)
set_edit_mode(edit_mode);
}
+/*!
+ * \brief ブラウザに編集モードを反映します。
+ *
+ * ブラウザは、編集モードにある場合、クリックでedit_segmentを発行します。
+ * 編集モードではリンクのクリックは無効です。
+ */
void TM::SocketConnection::set_edit_mode(bool edit_mode)
{
if(edit_mode == m_edit_mode) return;
json["cmd"] = "set_edit_mode";
json["edit_mode"] = edit_mode;
send_message(json);
-
}
/*!
- * \brief ウェブブラウザ上でクリックされ、editコマンドが発行されたときに呼び出されます。
+ * \brief ウェブブラウザへセグメントの訳文をセットします。
+ * \param segment_id セグメントのID。
+ * \param html セグメントに対応するHTML文字列。
*/
-void TM::SocketConnection::do_edit(QJsonObject const &json)
+void TM::SocketConnection::set_segment(int segment_id, QString html)
{
- int scode = m_editor_widget->source_language();
+ QJsonObject json;
+ json["cmd"] = "set_segment";
+ json["segment_id"] = segment_id;
+ json["html"] = html;
+ send_message(json);
+}
+/*!
+ * \brief ウェブブラウザ上でクリックされ、edit_segmentコマンドが発行されたときに
+ * 呼び出されます。
+ */
+void TM::SocketConnection::do_edit_segment(QJsonObject const &json)
+{
assert(json.contains("segment_id"));
int segment_id = json["segment_id"].toString().toInt();
segment_map_iterator it = m_segments.find(segment_id);
- if(it == m_segments.end())
+ assert(it != m_segments.end());
+ TextSegment::pointer segment = it.value();
+
+ if(segment != m_current_segment)
{
- assert(json.contains("source"));
- QString source = json["source"].toString();
- m_current_segment = TextSegment::create(m_service, scode, segment_id, source);
- m_segments.insert(segment_id, m_current_segment);
+ m_current_segment = segment;
+ m_editor_widget->set_segment(m_current_segment);
}
- else m_current_segment = it.value();
-
- m_editor_widget->set_segment(m_current_segment);
}
/*!
* \brief ウェブブラウザ上でドキュメントがフォーカスを取得し、
* focusコマンドが発行されたときに呼び出されます。
*/
-void TM::SocketConnection::do_focus(QJsonObject const &json)
+void TM::SocketConnection::do_focus(QJsonObject const &)
{
m_editor_widget->attach(this);
- bool edit_mode = json["edit_mode"].toBool();
- m_edit_mode = edit_mode;
- m_editor_widget->set_edit_mode(edit_mode);
+ m_editor_widget->set_edit_mode(m_edit_mode);
if(m_current_segment) m_editor_widget->set_segment(m_current_segment);
}
void TM::SocketConnection::do_blur(QJsonObject const &)
{
- //m_widget->detach(this);
+ //m_editor_widget->detach(this);
}
-void TM::SocketConnection::do_load(const QJsonObject &json)
+void TM::SocketConnection::do_load(QJsonObject const &json)
{
m_editor_widget->attach(this);
- bool edit_mode = json["edit_mode"].toBool();
- m_edit_mode = edit_mode;
- m_editor_widget->set_edit_mode(edit_mode);
- //set_edit_mode(m_widget->edit_mode());
+ set_edit_mode(m_editor_widget->edit_mode());
+
+ m_source_language_code = m_editor_widget->source_language();
+ m_target_language_code = m_editor_widget->target_language();
+ m_url = QUrl(json["url"].toString());
+ m_site_id = m_service->find_site_id(m_url.host());
+}
+
+/*!
+ * \brief ブラウザがセグメントを読み込んだ結果呼び出されます。
+ *
+ * この処理の中で、セグメントの設定、完全一致訳文の検索を行います。
+ * セグメント全体の検索が終わった時点でブラウザへsegment_loaded応答を返します。
+ * ブラウザは、segment_loadedを受けて、そのセグメントをクリック可能にします。
+ */
+void TM::SocketConnection::do_load_segment(QJsonObject const &json)
+{
+ assert(json.contains("segment_id"));
+ int segment_id = json["segment_id"].toString().toInt();
+ assert(!m_segments.contains(segment_id));
+ assert(json.contains("html"));
+ QString html = json["html"].toString();
+
+ // セグメントの挿入。
+ TextSegment::pointer segment = TextSegment::create(
+ m_service, m_source_language_code, segment_id, html);
+ m_segments.insert(segment_id, segment);
+
+
+ // センテンス完全一致訳文の検索。
+ //m_service->find_sentence(m_site_id,)
+
+ // セグメントの挿
+ //QJsonObject json;
+ //json["cmd"] = "set_edit_mode";
+ //json["edit_mode"] = edit_mode;
+ //send_message(json);
}
void TM::SocketConnection::onTextMessageReceived(QString const &message)
{
QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
QString cmd = json["cmd"].toString();
- if(cmd == "edit") do_edit(json);
+ if(cmd == "edit_segment") do_edit_segment(json);
else if(cmd == "focus") do_focus(json);
else if(cmd == "blur") do_blur(json);
else if(cmd == "load") do_load(json);
+ else if(cmd == "load_segment") do_load_segment(json);
}
void TM::SocketConnection::onBinaryMessageReceived(QByteArray const &message)
#include "html.h"
#include "text.h"
+#include "tmtext.h"
#include <QObject>
public:
int type() const;
+ HtmlNode node();
+
+ QString debug_dump() const;
static pointer create(HtmlNode node_, int begin_, int tail_);
int m_state;
};
-class TextSentence
+class HtmlConverter
{
public:
- typedef std::shared_ptr<TextSentence> pointer;
+ void append(QString string);
+ void append(HtmlNode::pointer node, QString string);
+ QString to_string();
private:
- TextSentence(Text::pointer source_sentence);
-public:
- Text::pointer source_sentence();
- Text::pointer target_sentence();
- void set_terget_sentence(Text::pointer target_sentence);
-
- QJsonArray link_data();
- void set_link_data(QJsonArray json);
-
- static pointer create(Text::pointer source_sentence);
-
- QString debug_dump();
+ void adjust();
private:
- Text::pointer m_source_sentence;
- Text::pointer m_target_sentence;
- QJsonArray m_link_data;
+ QList<HtmlNode::pointer> m_nodes;
};
class TextSegment
public:
int segment_id() const;
+ quint32 crc32(Service *service, int code, int index);
+ int size() const;
TextSentence::pointer at(int index);
iterator begin();
iterator end();
+ QString to_html();
+ QString to_html_from_source(TextSentence::pointer sentence);
+ QString to_html_from_target(TextSentence::pointer sentence);
+
+ HtmlNode::pointer find_html_node_by_offset(int offset);
+
static pointer create(
Service *service, int scode, int segment_id, QString source);
void set_edit_mode(bool edit_mode);
- void save_sentence(int segment_id, int index,
- Text::pointer target_sentence, QJsonArray link);
+ void save_sentence(int segment_id, int index);
+ void do_sentence_loaded();
signals:
void editCmd(int id, QString html);
void changeEditMode(bool edit_mode);
private:
- void do_edit(QJsonObject const &json);
+ void set_segment(int segment_id, QString html);
+
+ void do_edit_segment(QJsonObject const &json);
void do_focus(QJsonObject const &json);
void do_blur(QJsonObject const &json);
void do_load(QJsonObject const &json);
+ void do_load_segment(QJsonObject const &json);
+
+ void do_segment_loaded(int segmnet_id);
Text::pointer to_text(HtmlRange range) const;
TextSegment::pointer find_segment(int segment_id);
private:
Settings *m_settings;
Service *m_service;
-
QMutex m_mutex;
+ QUrl m_url;
+ int m_site_id;
+
bool m_edit_mode;
- int m_source_language;
- int m_target_language;
+ int m_source_language_code;
+ int m_target_language_code;
EditorWidget *m_editor_widget;
#include "tmtext.h"
+#include <QJsonDocument>
+#include <QJsonDocument>
+
#include "debug.h"
// WordLink -------------------------------------------------------------------
TM::WordLink::storage_type* TM::WordLink::targets() { return &m_targets; }
-QJsonObject TM::WordLink::to_json() const
+QJsonArray TM::WordLink::to_json() const
{
QJsonArray sources, targets;
for(Text::pointer const &p : m_sources)
{
RangeData const *rd = static_cast<RangeData const*>(p->data().get());
- QJsonArray ja;
- ja.append(rd->begin());
- ja.append(rd->tail());
- sources.append(ja);
+ sources.append(rd->begin());
+ sources.append(rd->tail());
}
for(Text::pointer const &p : m_targets)
{
RangeData const *rd = static_cast<RangeData const*>(p->data().get());
QJsonArray ja;
- ja.append(rd->begin());
- ja.append(rd->tail());
- targets.append(ja);
+ targets.append(rd->begin());
+ targets.append(rd->tail());
}
- QJsonObject result;
- result["s"] = sources;
- result["t"] = targets;
+ QJsonArray result;
+ result.append(sources);
+ result.append(targets);
return result;
}
+void TM::WordLink::set_json(QJsonArray json)
+{
+ for(QJsonValue jo : json);
+}
+
QString TM::WordLink::debug_dump() const
{
QString result;
/*!
* \brief placeからvalueを含むWordLinkを検索します。
+ *
+ * 存在しない場合、空のポインタを返します。
*/
TM::WordLink::pointer TM::WordLinker::find(int place, Text::pointer word)
{
TM::WordLinker::iterator TM::WordLinker::end() { return m_links.end(); }
-QJsonArray TM::WordLinker::to_json_array() const
+QByteArray TM::WordLinker::to_json() const
{
QJsonArray ja;
if(m_current_link && m_current_link->is_valid()) ja.append(m_current_link->to_json());
for(WordLink::pointer const &p : m_links) ja.append(p->to_json());
- return ja;
-}
-void TM::WordLinker::set_json_array(QJsonArray json)
-{
- for(QJsonValue jv : json)
+ QByteArray result;
+ if(!ja.isEmpty())
{
- QJsonArray ja = jv.toArray();
- //for()
+ QJsonDocument jdoc(ja);
+ result = jdoc.toJson(QJsonDocument::Compact);
}
+ return result;
+}
+
+void TM::WordLinker::set_json(QByteArray json)
+{
//m_links.append();
}
return result;
}
+// TextSentence ---------------------------------------------------------------
+
+TM::TextSentence::TextSentence(int segment_id, Text::pointer source_sentence)
+ : m_segment_id(segment_id)
+ , m_source_sentence(source_sentence)
+ , m_crc32(0)
+ , m_source_id(0)
+ , m_target_id(0)
+ , m_loaded(false)
+{
+}
+
+Text::pointer TM::TextSentence::source_sentence() { return m_source_sentence; }
+
+Text::pointer TM::TextSentence::target_sentence() { return m_target_sentence; }
+
+void TM::TextSentence::set_target_sentence(Text::pointer target_sentence)
+{
+ m_target_sentence = target_sentence;
+}
+
+TM::WordLinker* TM::TextSentence::linker() { return &m_linker; }
+
+QByteArray TM::TextSentence::to_json()
+{
+ return m_linker.to_json();
+}
+
+void TM::TextSentence::set_json(QJsonArray json)
+{
+ m_linker.clear();
+ for(QJsonValue jv : json)
+ {
+ QJsonArray ja = jv.toArray();
+ //for()
+ }
+}
+
+quint32 TM::TextSentence::crc32() const { return m_crc32; }
+
+void TM::TextSentence::set_crc32(quint32 crc32) { m_crc32 = crc32; }
+
+quint32 TM::TextSentence::source_id() const { return m_source_id; }
+
+void TM::TextSentence::set_source_id(quint32 source_id)
+{
+ m_source_id = source_id;
+}
+
+bool TM::TextSentence::is_loaded() const { return m_loaded; }
+
+void TM::TextSentence::set_loaded(bool loaded)
+{
+ m_loaded = loaded;
+}
+
+QString TM::TextSentence::debug_dump() const
+{
+ QString result;
+
+ result += "[TextSentence:";
+ if(m_source_sentence)
+ {
+ result += "[source_sentence:";
+ result += m_source_sentence->debug_dump();
+ result += "]";
+ }
+ if(m_target_sentence)
+ {
+ result += "[target_sentence:";
+ result += m_target_sentence->debug_dump();
+ result += "]";
+ }
+ result += "]";
+ return result;
+}
+TM::TextSentence::pointer TM::TextSentence::create(
+ int segment_id, Text::pointer source_sentence)
+{
+ return pointer(new TextSentence(segment_id, source_sentence));
+}
storage_type* sources();
storage_type* targets();
- QJsonObject to_json() const;
+ QJsonArray to_json() const;
+ void set_json(QJsonArray json);
QString debug_dump() const;
iterator begin();
iterator end();
- QJsonArray to_json_array() const;
- void set_json_array(QJsonArray json);
+ QByteArray to_json() const;
+ void set_json(QByteArray json);
QString debug_dump() const;
storage_type m_links;
};
+class TextSentence
+{
+public:
+ typedef std::shared_ptr<TextSentence> pointer;
+
+private:
+ TextSentence(int segment_id, Text::pointer source_sentence);
+public:
+ Text::pointer source_sentence();
+ Text::pointer target_sentence();
+ void set_target_sentence(Text::pointer target_sentence);
+
+ WordLinker* linker();
+ QByteArray to_json();
+ void set_json(QJsonArray json);
+
+ quint32 crc32() const;
+ void set_crc32(quint32 crc32);
+
+ quint32 source_id() const;
+ void set_source_id(quint32 source_id);
+
+ bool is_loaded() const;
+ void set_loaded(bool loaded);
+
+ QString debug_dump() const;
+
+ static pointer create(int segment_id, Text::pointer source_sentence);
+
+private:
+ Text::pointer m_source_sentence;
+ Text::pointer m_target_sentence;
+ WordLinker m_linker;
+
+ int m_segment_id;
+
+ quint32 m_crc32;
+ quint32 m_source_id;
+ quint32 m_target_id;
+
+ bool m_loaded; /*!< 完全一致訳文の初期検索が終了していればtrue */
+};
+
} // namespace TM
#endif // TMTEXT_H
return result;
}
-QString Text::debug_dump()
+QString Text::debug_dump() const
{
- return QString();
+ QString result;
+ if(m_data) result += m_data->debug_dump();
+
+ if(!m_string.isEmpty()) return result + m_string;
+
+ for(Text::const_pointer p = begin(); p; p = p->next())
+ {
+ result.append(p->debug_dump());
+ }
+
+ return result;
}
Text::pointer Text::create()
int RangeData::type() const { return Type; }
+QString RangeData::debug_dump() const
+{
+ return QString("[RangeData:") + QString::number(m_begin) + ","
+ + QString::number(m_tail) + "]";
+}
+
UserData::pointer RangeData::create(int begin_, int tail_)
{
return pointer(new RangeData(begin_, tail_));
Text(Text const &);
public:
+ virtual ~Text() { }
+
bool is_empty() const;
int size();
bool contains(pointer value_);
QString to_string() const;
- QString debug_dump();
+ virtual QString debug_dump() const;
static pointer create();
static pointer create(weak_pointer parent_);
int tail() const;
int type() const;
+ QString debug_dump() const;
static pointer create(int begin_, int tail_);
#ifndef USERDATA_H
#define USERDATA_H
+#include <QString>
+
#include <memory>
struct UserData
virtual ~UserData() { }
virtual int type() const { return User; }
+ virtual QString debug_dump() const { return "struct UserData."; }
};
#endif // USERDATA_H