#include "settings.h"
#include "html.h"
-#include "htmltag.h"
+#include "tmtext.h"
#include <QMutex>
#include <QMutexLocker>
return pointer(new HtmlData(node_, begin_, tail_));
}
-// TextConverter --------------------------------------------------------------
-
-/*!
- * \brief 引数として与えられたHtmlの範囲から構造化テキストを作成します。
- */
-Text::pointer TM::TextConverter::to_text(HtmlRange range)
-{
- m_state = 0;
- Text::pointer result = Text::create();
- int begin = 0;
-
- for(HtmlNode const &node : range)
- {
- if(node.type() != HtmlNode::Text) continue;
- Text::pointer p = stuff_text(result, node.value());
- if(!p) continue;
- int num = p->string().size();
- HtmlData::pointer hd = HtmlData::create(node, begin, begin + num - 1);
- p->set_data(hd);
- begin += num;
- }
- return result;
-}
-
-/*!
- * \brief 引数として与えられたノードから構造化テキストを作成します。
- *
- * 連続する空白文字を一つにまとめます。
- * 無効なノードが与えられた場合、空の構造化テキストを返します。
- */
-Text::pointer TM::TextConverter::stuff_text(Text::pointer parent, QString const &string)
-{
- QString outstring;
- for(QChar const &ch : string)
- {
- if(is_ignorable_white_space(ch)) continue;
- if(is_white_space(ch)) outstring.append(' ');
- else outstring.append(ch);
- }
- if(outstring.isEmpty()) return Text::pointer();
-
- Text::pointer result = Text::create(parent, outstring);
- parent->append(result);
- return result;
-}
-
-/*!
- * \brief 連続する空白文字を検出します。
- *
- * 最初の空白文字、あるいは空白文字以外は、falseを返します。
- * 連続する空白文字の二番目以降に対してのみtrueを返します。
- */
-bool TM::TextConverter::is_ignorable_white_space(const QChar &ch)
-{
- if(!is_white_space(ch))
- {
- m_state = 0;
- return false;
- }
- if(m_state == 0)
- {
- m_state = 1;
- return false;
- }
- return true;
-}
-
-/*!
- * \brief 引数として与えられたchが空白文字の場合true、それ以外の場合falseを返します。
- */
-bool TM::TextConverter::is_white_space(QChar const &ch)
-{
- switch(ch.unicode())
- {
- case 0x9:
- case 0xA:
- case 0xC:
- case 0xD:
- case 0x20:
- case 0x200B:
- return true;
- }
- return false;
-}
-
-// HtmlConverter --------------------------------------------------------------
-
-
-void TM::HtmlConverter::append(QString string)
-{
- HtmlText::pointer text = HtmlText::create(HtmlText::weak_pointer());
- text->set_value(string);
- m_nodes.append(text);
-}
-
-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());
- }
-
- m_nodes.append(left);
- append(string);
- m_nodes.append(right);
-}
-
-QString TM::HtmlConverter::to_string()
-{
- QString result;
-
- adjust();
- for(Html::pointer node : m_nodes) result += node->to_string();
-
- return result;
-}
-
-void TM::HtmlConverter::adjust()
-{
- 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 ----------------------------------------------------------------
-
-TM::TextSegment::TextSegment(
- Service *service, int scode, int segment_id, QString source)
- : m_segment_id(segment_id)
- , m_document(source.toUtf8())
-{
- HtmlNode body = m_document.first("html").first("body");
- TextConverter tc;
- m_text = tc.to_text(HtmlRange(body, body));
-
- Text::pointer sentences = service->divide_into_sentences(scode, m_text->to_string());
- 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(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::begin() { return m_sentences.begin(); }
-
-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)
-{
- return pointer(new TextSegment(service, scode, segment_id, source));
-}
-
// SocketConnection -----------------------------------------------------------
TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
, m_editor_widget(editor_widget)
, m_site_id(0)
, m_edit_mode(false)
- , m_source_language_code(0)
+ , m_scode(0)
, m_target_language_code(0)
{
+ qRegisterMetaType<pointer>();
+
connect(socket, SIGNAL(textMessageReceived(QString const&)),
this, SLOT(onTextMessageReceived(QString const&)));
connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
* \brief データベースへセンテンスの登録とブラウザへの反映を行います。
* \param segment_id セグメントのID。
* \param index センテンスのインデックス。
+ *
+ * エディタから呼び出されます。
*/
void TM::SocketConnection::save_sentence(int segment_id, int index)
{
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);
+ m_service->insert_sentence(segment_id, index,
+ m_url.host(), scode, tcode, sentence, pointer(this));
+
+ // ブラウザへ反映。
+ set_segment(segment_id, segment->to_html());
+}
+
+/*!
+ * \brief データベースへセンテンスの消去とブラウザへの反映を行います。
+ * \param segment_id セグメントのID。
+ * \param index センテンスのインデックス。
+ *
+ * エディタから呼び出されます。
+ * 呼び出されたとき、訳文はありません。
+ * そこで、clear系がサービスに依頼する時は、原文を引数として渡します。
+ */
+void TM::SocketConnection::remove_sentence(int segment_id, int index)
+{
+ assert(m_site_id);
+
+ // セグメントの検索。
+ segment_map_iterator it = m_segments.find(segment_id);
+ assert(it != m_segments.end());
+ TextSegment::pointer segment = it.value();
+
+ // センテンスの検索。
+ TextSentence::pointer sentence = segment->at(index);
+ assert(sentence->source_sentence());
+ // 訳文の消去。
+ sentence->set_target_sentence(Text::pointer());
+
+ // 言語コード。
+ int scode = m_editor_widget->source_language();
+ int tcode = m_editor_widget->target_language();
+
+ // データベースとサーバへ反映。
+ m_service->remove_sentence(m_url.host(), scode, tcode, sentence);
// ブラウザへ反映。
set_segment(segment_id, segment->to_html());
}
+/*!
+ * \brief TM::SocketConnection::sentence_found
+ * \param segment_id
+ * \param index
+ * \param result データベースが返す訳文。
+ */
+void TM::SocketConnection::sentence_found(
+ int segment_id, int index, sentence_data_type::pointer result)
+{
+ if(!result) return; // 検索がヒットしなかった場合、何もしない。
+
+ segment_map_iterator it = m_segments.find(segment_id);
+ assert(it != m_segments.end());
+ TextSegment::pointer segment = it.value();
+
+ segment->at(index)->set_source_id(result->source_id);
+ if(100 <= result->quality) // 品質が100以上の場合、セグメントとブラウザを更新する。
+ {
+ Text::pointer sentences = m_service->divide_into_sentences(
+ m_target_language_code, result->sentence);
+ Text::pointer words = m_service->divide_into_words(
+ m_target_language_code, sentences->begin());
+ segment->at(index)->set_target_sentence(words);
+ segment->at(index)->set_json(result->json);
+
+ set_segment(segment_id, segment->to_html()); // ブラウザに送信
+ }
+ else // 品質が100未満の場合、候補として登録する。
+ {
+ segment->at(index)->append(result);
+ }
+}
+
+void TM::SocketConnection::sentence_inserted(int segment_id, int index,
+ quint32 source_id, quint32 target_id)
+{
+ segment_map_iterator it = m_segments.find(segment_id);
+ assert(it != m_segments.end());
+ TextSegment::pointer segment = it.value();
+
+ assert(source_id);
+ TextSentence::pointer sentence = segment->at(index);
+ sentence->set_source_id(source_id);
+ sentence->set_target_id(target_id);
+}
+
void TM::SocketConnection::changeEditMode(bool edit_mode)
{
set_edit_mode(edit_mode);
}
+void TM::SocketConnection::changeLanguage()
+{
+ QJsonObject json;
+ json["cmd"] = "reload";
+ send_message(json);
+}
+
/*!
* \brief ブラウザに編集モードを反映します。
*
void TM::SocketConnection::do_edit_segment(QJsonObject const &json)
{
assert(json.contains("segment_id"));
+ if(!json.contains("segment_id")) return;
int segment_id = json["segment_id"].toString().toInt();
segment_map_iterator it = m_segments.find(segment_id);
set_edit_mode(m_editor_widget->edit_mode());
- m_source_language_code = m_editor_widget->source_language();
+ m_scode = 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());
// セグメントの挿入。
TextSegment::pointer segment = TextSegment::create(
- m_service, m_source_language_code, segment_id, html);
+ m_service, m_scode, 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);
+ for(TextSentence::pointer sentence : *segment)
+ {
+ m_service->find_sentence(
+ segment_id, sentence->index(),
+ m_url.host(), m_scode, m_target_language_code,
+ sentence, pointer(this));
+ }
}
void TM::SocketConnection::onTextMessageReceived(QString const &message)
quint16 port = m_settings->value("SocketServer/port").toUInt();
bool result = m_server->listen(QHostAddress::LocalHost, port);
if(!result) result = m_server->listen(QHostAddress::LocalHost, 0);
+ assert(result);
if(!result) qFatal("An error occured in SocketServer().");
}