2 #include "tmeditorwidget.h"
10 #include <QMutexLocker>
12 #include <QJsonDocument>
13 #include <QJsonObject>
18 // HtmlData -------------------------------------------------------------------
20 TM::HtmlData::HtmlData(HtmlNode node_, int begin_, int tail_)
21 : RangeData(begin_, tail_)
26 int TM::HtmlData::type() const { return Type; }
28 HtmlNode TM::HtmlData::node() { return m_node; }
30 QString TM::HtmlData::debug_dump() const
33 result += "[HtmlData:";
34 result += m_node.name() + ",";
35 result += QString::number(begin()) + ",";
36 result += QString::number(tail()) + "]";
40 TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_)
42 return pointer(new HtmlData(node_, begin_, tail_));
45 // SocketConnection -----------------------------------------------------------
47 TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
48 EditorWidget *editor_widget, QWebSocket *socket)
50 , m_settings(settings)
52 , m_mutex(QMutex::Recursive)
53 , m_editor_widget(editor_widget)
57 , m_target_language_code(0)
59 qRegisterMetaType<pointer>();
61 connect(socket, SIGNAL(textMessageReceived(QString const&)),
62 this, SLOT(onTextMessageReceived(QString const&)));
63 connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
64 this, SLOT(onBinaryMessageReceived(QByteArray const&)));
67 TM::SocketConnection::~SocketConnection()
69 m_editor_widget->detach(this);
72 QWebSocket* TM::SocketConnection::socket()
74 QWebSocket * result = qobject_cast<QWebSocket*>(parent());
78 void TM::SocketConnection::send_message(QString const &message)
80 QWebSocket *ws = socket();
81 if(ws) ws->sendTextMessage(message);
84 void TM::SocketConnection::send_message(QJsonObject const &json)
88 send_message(doc.toJson().data());
92 * \brief データベースへセンテンスの登録とブラウザへの反映を行います。
93 * \param segment_id セグメントのID。
94 * \param index センテンスのインデックス。
98 void TM::SocketConnection::save_sentence(int segment_id, int index)
103 segment_map_iterator it = m_segments.find(segment_id);
104 assert(it != m_segments.end());
105 TextSegment::pointer segment = it.value();
108 TextSentence::pointer sentence = segment->at(index);
109 assert(sentence->source_sentence());
110 if(!sentence->target_sentence()) return;
113 int scode = m_editor_widget->source_language();
114 int tcode = m_editor_widget->target_language();
117 m_service->insert_sentence(segment_id, index,
118 m_url.host(), scode, tcode, sentence, pointer(this));
121 set_segment(segment_id, segment->to_html());
125 * \brief データベースへセンテンスの消去とブラウザへの反映を行います。
126 * \param segment_id セグメントのID。
127 * \param index センテンスのインデックス。
131 * そこで、clear系がサービスに依頼する時は、原文を引数として渡します。
133 void TM::SocketConnection::remove_sentence(int segment_id, int index)
138 segment_map_iterator it = m_segments.find(segment_id);
139 assert(it != m_segments.end());
140 TextSegment::pointer segment = it.value();
143 TextSentence::pointer sentence = segment->at(index);
144 assert(sentence->source_sentence());
146 sentence->set_target_sentence(Text::pointer());
149 int scode = m_editor_widget->source_language();
150 int tcode = m_editor_widget->target_language();
153 m_service->remove_sentence(m_url.host(), scode, tcode, sentence);
156 set_segment(segment_id, segment->to_html());
160 * \brief TM::SocketConnection::sentence_found
163 * \param result データベースが返す訳文。
165 void TM::SocketConnection::sentence_found(
166 int segment_id, int index, sentence_data_type::pointer result)
168 if(!result) return; // 検索がヒットしなかった場合、何もしない。
170 segment_map_iterator it = m_segments.find(segment_id);
171 assert(it != m_segments.end());
172 TextSegment::pointer segment = it.value();
174 segment->at(index)->set_source_id(result->source_id);
175 if(100 <= result->quality) // 品質が100以上の場合、セグメントとブラウザを更新する。
177 Text::pointer sentences = m_service->divide_into_sentences(
178 m_target_language_code, result->sentence);
179 Text::pointer words = m_service->divide_into_words(
180 m_target_language_code, sentences->begin());
181 segment->at(index)->set_target_sentence(words);
182 segment->at(index)->set_json(result->json);
184 set_segment(segment_id, segment->to_html()); // ブラウザに送信
186 else // 品質が100未満の場合、候補として登録する。
188 segment->at(index)->append(result);
192 void TM::SocketConnection::sentence_inserted(int segment_id, int index,
193 quint32 source_id, quint32 target_id)
195 segment_map_iterator it = m_segments.find(segment_id);
196 assert(it != m_segments.end());
197 TextSegment::pointer segment = it.value();
200 TextSentence::pointer sentence = segment->at(index);
201 sentence->set_source_id(source_id);
202 sentence->set_target_id(target_id);
205 void TM::SocketConnection::changeEditMode(bool edit_mode)
207 set_edit_mode(edit_mode);
210 void TM::SocketConnection::changeLanguage()
213 json["cmd"] = "reload";
218 * \brief ブラウザに編集モードを反映します。
220 * ブラウザは、編集モードにある場合、クリックでedit_segmentを発行します。
221 * 編集モードではリンクのクリックは無効です。
223 void TM::SocketConnection::set_edit_mode(bool edit_mode)
225 if(edit_mode == m_edit_mode) return;
227 m_edit_mode = edit_mode;
229 json["cmd"] = "set_edit_mode";
230 json["edit_mode"] = edit_mode;
235 * \brief ウェブブラウザへセグメントの訳文をセットします。
236 * \param segment_id セグメントのID。
237 * \param html セグメントに対応するHTML文字列。
239 void TM::SocketConnection::set_segment(int segment_id, QString html)
242 json["cmd"] = "set_segment";
243 json["segment_id"] = segment_id;
249 * \brief ウェブブラウザ上でクリックされ、edit_segmentコマンドが発行されたときに
252 void TM::SocketConnection::do_edit_segment(QJsonObject const &json)
254 assert(json.contains("segment_id"));
255 if(!json.contains("segment_id")) return;
256 int segment_id = json["segment_id"].toString().toInt();
258 segment_map_iterator it = m_segments.find(segment_id);
259 assert(it != m_segments.end());
260 TextSegment::pointer segment = it.value();
262 if(segment != m_current_segment)
264 m_current_segment = segment;
265 m_editor_widget->set_segment(m_current_segment);
270 * \brief ウェブブラウザ上でドキュメントがフォーカスを取得し、
271 * focusコマンドが発行されたときに呼び出されます。
273 void TM::SocketConnection::do_focus(QJsonObject const &)
275 m_editor_widget->attach(this);
277 m_editor_widget->set_edit_mode(m_edit_mode);
278 if(m_current_segment) m_editor_widget->set_segment(m_current_segment);
281 void TM::SocketConnection::do_blur(QJsonObject const &)
283 //m_editor_widget->detach(this);
286 void TM::SocketConnection::do_load(QJsonObject const &json)
288 m_editor_widget->attach(this);
290 set_edit_mode(m_editor_widget->edit_mode());
292 m_scode = m_editor_widget->source_language();
293 m_target_language_code = m_editor_widget->target_language();
294 m_url = QUrl(json["url"].toString());
295 m_site_id = m_service->find_site_id(m_url.host());
299 * \brief ブラウザがセグメントを読み込んだ結果呼び出されます。
301 * この処理の中で、セグメントの設定、完全一致訳文の検索を行います。
302 * セグメント全体の検索が終わった時点でブラウザへsegment_loaded応答を返します。
303 * ブラウザは、segment_loadedを受けて、そのセグメントをクリック可能にします。
305 void TM::SocketConnection::do_load_segment(QJsonObject const &json)
307 assert(json.contains("segment_id"));
308 int segment_id = json["segment_id"].toString().toInt();
309 assert(!m_segments.contains(segment_id));
310 assert(json.contains("html"));
311 QString html = json["html"].toString();
314 TextSegment::pointer segment = TextSegment::create(
315 m_service, m_scode, segment_id, html);
316 m_segments.insert(segment_id, segment);
319 for(TextSentence::pointer sentence : *segment)
321 m_service->find_sentence(
322 segment_id, sentence->index(),
323 m_url.host(), m_scode, m_target_language_code,
324 sentence, pointer(this));
328 void TM::SocketConnection::onTextMessageReceived(QString const &message)
330 QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
331 QString cmd = json["cmd"].toString();
332 if(cmd == "edit_segment") do_edit_segment(json);
333 else if(cmd == "focus") do_focus(json);
334 else if(cmd == "blur") do_blur(json);
335 else if(cmd == "load") do_load(json);
336 else if(cmd == "load_segment") do_load_segment(json);
339 void TM::SocketConnection::onBinaryMessageReceived(QByteArray const &message)
344 // SocketServer ---------------------------------------------------------------
346 TM::SocketServer::SocketServer(Settings *settings, Service* service,
347 EditorWidget *editor_widget, QObject *parent)
349 , m_settings(settings)
351 , m_server(new QWebSocketServer(QStringLiteral("wordring websocket"),
352 QWebSocketServer::NonSecureMode, this))
353 , m_editor_widget(editor_widget)
355 connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
357 quint16 port = m_settings->value("SocketServer/port").toUInt();
358 bool result = m_server->listen(QHostAddress::LocalHost, port);
359 if(!result) result = m_server->listen(QHostAddress::LocalHost, 0);
361 if(!result) qFatal("An error occured in SocketServer().");
364 TM::SocketServer::~SocketServer()
368 quint16 TM::SocketServer::port() const
370 return m_server->serverPort();
373 void TM::SocketServer::abort()
375 for(QWebSocket *ws : m_sockets)
383 void TM::SocketServer::onNewConnection()
385 QWebSocket *socket = m_server->nextPendingConnection();
386 connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
387 new SocketConnection(m_settings, m_service, m_editor_widget, socket);
388 m_sockets.push_back(socket);
391 void TM::SocketServer::onDisconnected()
393 QWebSocket *socket = qobject_cast<QWebSocket*>(sender());
394 m_sockets.removeOne(socket);
395 socket->deleteLater();