OSDN Git Service

オランダ語プラグイン追加。
[wordring-tm/wordring-tm.git] / proxy / tmsocket.cpp
1 #include "tmsocket.h"
2 #include "tmeditorwidget.h"
3 #include "tmservice.h"
4
5 #include "settings.h"
6 #include "html.h"
7 #include "tmtext.h"
8
9 #include <QMutex>
10 #include <QMutexLocker>
11
12 #include <QJsonDocument>
13 #include <QJsonObject>
14
15 #include "debug.h"
16
17
18 // HtmlData -------------------------------------------------------------------
19
20 TM::HtmlData::HtmlData(HtmlNode node_, int begin_, int tail_)
21         : RangeData(begin_, tail_)
22         , m_node(node_)
23 {
24 }
25
26 int TM::HtmlData::type() const { return Type; }
27
28 HtmlNode TM::HtmlData::node() { return m_node; }
29
30 QString TM::HtmlData::debug_dump() const
31 {
32         QString result;
33         result += "[HtmlData:";
34         result += m_node.name() + ",";
35         result += QString::number(begin()) + ",";
36         result += QString::number(tail()) + "]";
37         return result;
38 }
39
40 TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_)
41 {
42         return pointer(new HtmlData(node_, begin_, tail_));
43 }
44
45 // SocketConnection -----------------------------------------------------------
46
47 TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
48                                                                         EditorWidget *editor_widget, QWebSocket *socket)
49         : QObject(socket)
50         , m_settings(settings)
51         , m_service(service)
52         , m_mutex(QMutex::Recursive)
53         , m_editor_widget(editor_widget)
54         , m_site_id(0)
55         , m_edit_mode(false)
56         , m_scode(0)
57         , m_target_language_code(0)
58 {
59         qRegisterMetaType<pointer>();
60
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&)));
65 }
66
67 TM::SocketConnection::~SocketConnection()
68 {
69         m_editor_widget->detach(this);
70 }
71
72 QWebSocket* TM::SocketConnection::socket()
73 {
74         QWebSocket * result = qobject_cast<QWebSocket*>(parent());
75         return result;
76 }
77
78 void TM::SocketConnection::send_message(QString const &message)
79 {
80         QWebSocket *ws = socket();
81         if(ws) ws->sendTextMessage(message);
82 }
83
84 void TM::SocketConnection::send_message(QJsonObject const &json)
85 {
86         QJsonDocument doc;
87         doc.setObject(json);
88         send_message(doc.toJson().data());
89 }
90
91 /*!
92  * \brief データベースへセンテンスの登録とブラウザへの反映を行います。
93  * \param segment_id セグメントのID。
94  * \param index センテンスのインデックス。
95  *
96  * エディタから呼び出されます。
97  */
98 void TM::SocketConnection::save_sentence(int segment_id, int index)
99 {
100         assert(m_site_id);
101
102         // セグメントの検索。
103         segment_map_iterator it = m_segments.find(segment_id);
104         assert(it != m_segments.end());
105         TextSegment::pointer segment = it.value();
106
107         // センテンスの検索。
108         TextSentence::pointer sentence = segment->at(index);
109         assert(sentence->source_sentence());
110         if(!sentence->target_sentence()) return;
111
112         // 言語コード。
113         int scode = m_editor_widget->source_language();
114         int tcode = m_editor_widget->target_language();
115
116         // データベースへ登録。
117         m_service->insert_sentence(segment_id, index,
118                                                 m_url.host(), scode, tcode, sentence, pointer(this));
119
120         // ブラウザへ反映。
121         set_segment(segment_id, segment->to_html());
122 }
123
124 /*!
125  * \brief データベースへセンテンスの消去とブラウザへの反映を行います。
126  * \param segment_id セグメントのID。
127  * \param index センテンスのインデックス。
128  *
129  * エディタから呼び出されます。
130  * 呼び出されたとき、訳文はありません。
131  * そこで、clear系がサービスに依頼する時は、原文を引数として渡します。
132  */
133 void TM::SocketConnection::remove_sentence(int segment_id, int index)
134 {
135         assert(m_site_id);
136
137         // セグメントの検索。
138         segment_map_iterator it = m_segments.find(segment_id);
139         assert(it != m_segments.end());
140         TextSegment::pointer segment = it.value();
141
142         // センテンスの検索。
143         TextSentence::pointer sentence = segment->at(index);
144         assert(sentence->source_sentence());
145         // 訳文の消去。
146         sentence->set_target_sentence(Text::pointer());
147
148         // 言語コード。
149         int scode = m_editor_widget->source_language();
150         int tcode = m_editor_widget->target_language();
151
152         // データベースとサーバへ反映。
153         m_service->remove_sentence(m_url.host(), scode, tcode, sentence);
154
155         // ブラウザへ反映。
156         set_segment(segment_id, segment->to_html());
157 }
158
159 /*!
160  * \brief TM::SocketConnection::sentence_found
161  * \param segment_id
162  * \param index
163  * \param result データベースが返す訳文。
164  */
165 void TM::SocketConnection::sentence_found(
166                 int segment_id, int index, sentence_data_type::pointer result)
167 {
168         if(!result) return; // 検索がヒットしなかった場合、何もしない。
169
170         segment_map_iterator it = m_segments.find(segment_id);
171         assert(it != m_segments.end());
172         TextSegment::pointer segment = it.value();
173
174         segment->at(index)->set_source_id(result->source_id);
175         if(100 <= result->quality) // 品質が100以上の場合、セグメントとブラウザを更新する。
176         {
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);
183
184                 set_segment(segment_id, segment->to_html()); // ブラウザに送信
185         }
186         else // 品質が100未満の場合、候補として登録する。
187         {
188                 segment->at(index)->append(result);
189         }
190 }
191
192 void TM::SocketConnection::sentence_inserted(int segment_id, int index,
193                                                 quint32 source_id, quint32 target_id)
194 {
195         segment_map_iterator it = m_segments.find(segment_id);
196         assert(it != m_segments.end());
197         TextSegment::pointer segment = it.value();
198
199         assert(source_id);
200         TextSentence::pointer sentence = segment->at(index);
201         sentence->set_source_id(source_id);
202         sentence->set_target_id(target_id);
203 }
204
205 void TM::SocketConnection::changeEditMode(bool edit_mode)
206 {
207         set_edit_mode(edit_mode);
208 }
209
210 void TM::SocketConnection::changeLanguage()
211 {
212         QJsonObject json;
213         json["cmd"] = "reload";
214         send_message(json);
215 }
216
217 /*!
218  * \brief ブラウザに編集モードを反映します。
219  *
220  * ブラウザは、編集モードにある場合、クリックでedit_segmentを発行します。
221  * 編集モードではリンクのクリックは無効です。
222  */
223 void TM::SocketConnection::set_edit_mode(bool edit_mode)
224 {
225         if(edit_mode == m_edit_mode) return;
226
227         m_edit_mode = edit_mode;
228         QJsonObject json;
229         json["cmd"] = "set_edit_mode";
230         json["edit_mode"] = edit_mode;
231         send_message(json);
232 }
233
234 /*!
235  * \brief ウェブブラウザへセグメントの訳文をセットします。
236  * \param segment_id セグメントのID。
237  * \param html セグメントに対応するHTML文字列。
238  */
239 void TM::SocketConnection::set_segment(int segment_id, QString html)
240 {
241         QJsonObject json;
242         json["cmd"] = "set_segment";
243         json["segment_id"] = segment_id;
244         json["html"] = html;
245         send_message(json);
246 }
247
248 /*!
249  * \brief ウェブブラウザ上でクリックされ、edit_segmentコマンドが発行されたときに
250  * 呼び出されます。
251  */
252 void TM::SocketConnection::do_edit_segment(QJsonObject const &json)
253 {
254         assert(json.contains("segment_id"));
255         if(!json.contains("segment_id")) return;
256         int segment_id = json["segment_id"].toString().toInt();
257
258         segment_map_iterator it = m_segments.find(segment_id);
259         assert(it != m_segments.end());
260         TextSegment::pointer segment = it.value();
261
262         if(segment != m_current_segment)
263         {
264                 m_current_segment = segment;
265                 m_editor_widget->set_segment(m_current_segment);
266         }
267 }
268
269 /*!
270  * \brief ウェブブラウザ上でドキュメントがフォーカスを取得し、
271  * focusコマンドが発行されたときに呼び出されます。
272  */
273 void TM::SocketConnection::do_focus(QJsonObject const &)
274 {
275         m_editor_widget->attach(this);
276
277         m_editor_widget->set_edit_mode(m_edit_mode);
278         if(m_current_segment) m_editor_widget->set_segment(m_current_segment);
279 }
280
281 void TM::SocketConnection::do_blur(QJsonObject const &)
282 {
283         //m_editor_widget->detach(this);
284 }
285
286 void TM::SocketConnection::do_load(QJsonObject const &json)
287 {
288         m_editor_widget->attach(this);
289
290         set_edit_mode(m_editor_widget->edit_mode());
291
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());
296 }
297
298 /*!
299  * \brief ブラウザがセグメントを読み込んだ結果呼び出されます。
300  *
301  * この処理の中で、セグメントの設定、完全一致訳文の検索を行います。
302  * セグメント全体の検索が終わった時点でブラウザへsegment_loaded応答を返します。
303  * ブラウザは、segment_loadedを受けて、そのセグメントをクリック可能にします。
304  */
305 void TM::SocketConnection::do_load_segment(QJsonObject const &json)
306 {
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();
312
313         // セグメントの挿入。
314         TextSegment::pointer segment = TextSegment::create(
315                 m_service, m_scode, segment_id, html);
316         m_segments.insert(segment_id, segment);
317
318         // センテンス完全一致訳文の検索。
319         for(TextSentence::pointer sentence : *segment)
320         {
321                 m_service->find_sentence(
322                                 segment_id, sentence->index(),
323                                 m_url.host(), m_scode, m_target_language_code,
324                                 sentence, pointer(this));
325         }
326 }
327
328 void TM::SocketConnection::onTextMessageReceived(QString const &message)
329 {
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);
337 }
338
339 void TM::SocketConnection::onBinaryMessageReceived(QByteArray const &message)
340 {
341         qDebug() << message;
342 }
343
344 // SocketServer ---------------------------------------------------------------
345
346 TM::SocketServer::SocketServer(Settings *settings, Service* service,
347                                                         EditorWidget *editor_widget, QObject *parent)
348         : QObject(parent)
349         , m_settings(settings)
350         , m_service(service)
351         , m_server(new QWebSocketServer(QStringLiteral("wordring websocket"),
352                                                                         QWebSocketServer::NonSecureMode, this))
353         , m_editor_widget(editor_widget)
354 {
355         connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
356
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);
360         assert(result);
361         if(!result) qFatal("An error occured in SocketServer().");
362 }
363
364 TM::SocketServer::~SocketServer()
365 {
366 }
367
368 quint16 TM::SocketServer::port() const
369 {
370         return m_server->serverPort();
371 }
372
373 void TM::SocketServer::abort()
374 {
375         for(QWebSocket *ws : m_sockets)
376         {
377                 ws->abort();
378                 ws->deleteLater();
379         }
380         m_sockets.clear();
381 }
382
383 void TM::SocketServer::onNewConnection()
384 {
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);
389 }
390
391 void TM::SocketServer::onDisconnected()
392 {
393         QWebSocket *socket = qobject_cast<QWebSocket*>(sender());
394         m_sockets.removeOne(socket);
395         socket->deleteLater();
396 }
397
398
399
400
401
402