OSDN Git Service

Editorのデータ構造変更前、一時保存。
[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
8 #include <QMutex>
9 #include <QMutexLocker>
10
11 #include <QJsonDocument>
12 #include <QJsonObject>
13
14 #include "debug.h"
15
16
17 // HtmlData -------------------------------------------------------------------
18
19 TM::HtmlData::HtmlData(HtmlNode node_, int begin_, int tail_)
20         : RangeData(begin_, tail_)
21         , m_node(node_)
22 {
23 }
24
25 int TM::HtmlData::type() const { return Type; }
26
27 TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_)
28 {
29         return pointer(new HtmlData(node_, begin_, tail_));
30 }
31
32 // TextConverter --------------------------------------------------------------
33
34 /*!
35  * \brief 引数として与えられたHtmlの範囲から構造化テキストを作成します。
36  */
37 Text::pointer TM::TextConverter::to_text(HtmlRange range)
38 {
39         m_state = 0;
40         Text::pointer result = Text::create();
41         int begin = 0;
42
43         for(HtmlNode const &node : range)
44         {
45                 if(node.type() != HtmlNode::Text) continue;
46                 Text::pointer p = stuff_text(result, node.value());
47                 if(!p) continue;
48                 int num = p->string().size();
49                 HtmlData::pointer hd = HtmlData::create(node, begin, begin + num - 1);
50                 p->set_data(hd);
51                 begin += num;
52         }
53         return result;
54 }
55
56 /*!
57  * \brief 引数として与えられたノードから構造化テキストを作成します。
58  *
59  * 連続する空白文字を一つにまとめます。
60  * 無効なノードが与えられた場合、空の構造化テキストを返します。
61  */
62 Text::pointer TM::TextConverter::stuff_text(Text::pointer parent, QString const &string)
63 {
64         QString outstring;
65         for(QChar const &ch : string)
66         {
67                 if(is_ignorable_white_space(ch)) continue;
68                 if(is_white_space(ch)) outstring.append(' ');
69                 else outstring.append(ch);
70         }
71         if(outstring.isEmpty()) return Text::pointer();
72
73         Text::pointer result = Text::create(parent, outstring);
74         parent->append(result);
75         return result;
76 }
77
78 /*!
79  * \brief 連続する空白文字を検出します。
80  *
81  * 最初の空白文字、あるいは空白文字以外は、falseを返します。
82  * 連続する空白文字の二番目以降に対してのみtrueを返します。
83  */
84 bool TM::TextConverter::is_ignorable_white_space(const QChar &ch)
85 {
86         if(!is_white_space(ch))
87         {
88                 m_state = 0;
89                 return false;
90         }
91         if(m_state == 0)
92         {
93                 m_state = 1;
94                 return false;
95         }
96         return true;
97 }
98
99 /*!
100  * \brief 引数として与えられたchが空白文字の場合true、それ以外の場合falseを返します。
101  */
102 bool TM::TextConverter::is_white_space(QChar const &ch)
103 {
104         switch(ch.unicode())
105         {
106         case 0x9:
107         case 0xA:
108         case 0xC:
109         case 0xD:
110         case 0x20:
111         case 0x200B:
112                 return true;
113         }
114         return false;
115 }
116
117 // TextSentence ---------------------------------------------------------------
118
119 TM::TextSentence::TextSentence(Text::pointer source_sentence)
120         : m_source_sentence(source_sentence)
121 {
122 }
123
124 Text::pointer TM::TextSentence::source_sentence() { return m_source_sentence; }
125
126 Text::pointer TM::TextSentence::target_sentence() { return m_target_sentence; }
127
128 void TM::TextSentence::set_terget_sentence(Text::pointer target_sentence)
129 {
130         m_target_sentence = target_sentence;
131 }
132
133 QJsonArray TM::TextSentence::link_data() { return m_link_data; }
134
135 void TM::TextSentence::set_link_data(QJsonArray json) { m_link_data = json; }
136
137 TM::TextSentence::pointer TM::TextSentence::create(Text::pointer source_sentence)
138 {
139         return pointer(new TextSentence(source_sentence));
140 }
141
142 // TextSegment ----------------------------------------------------------------
143
144 TM::TextSegment::TextSegment(
145                 Service *service, int scode, int segment_id, QString source)
146         : m_segment_id(segment_id)
147         , m_document(source.toUtf8())
148 {
149         HtmlNode body = m_document.first("html").first("body");
150         TextConverter tc;
151         m_text = tc.to_text(HtmlRange(body, body));
152
153         Text::pointer sentences = service->divide_into_sentences(scode, m_text->to_string());
154         for(Text::pointer s = sentences->begin(); s; s = s->next()) // s: sentence
155         {
156                 Text::pointer words = service->divide_into_words(scode, s);
157                 m_sentences.append(TextSentence::create(words));
158         }
159 }
160
161 int TM::TextSegment::segment_id() const { return m_segment_id; }
162
163 TM::TextSentence::pointer TM::TextSegment::at(int index)
164 {
165         return m_sentences.at(index);
166 }
167
168 TM::TextSegment::iterator TM::TextSegment::begin() { return m_sentences.begin(); }
169
170 TM::TextSegment::iterator TM::TextSegment::end() { return m_sentences.end(); }
171
172 TM::TextSegment::pointer TM::TextSegment::create(
173                 Service *service, int scode, int segment_id, QString source)
174 {
175         return pointer(new TextSegment(service, scode, segment_id, source));
176 }
177
178 // SocketConnection -----------------------------------------------------------
179
180 TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
181                                                                         EditorWidget *editor_widget, QWebSocket *socket)
182         : QObject(socket)
183         , m_settings(settings)
184         , m_service(service)
185         , m_editor_widget(editor_widget)
186         , m_mutex(QMutex::Recursive)
187         , m_edit_mode(false)
188 {
189         connect(socket, SIGNAL(textMessageReceived(QString const&)),
190                         this, SLOT(onTextMessageReceived(QString const&)));
191         connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
192                         this, SLOT(onBinaryMessageReceived(QByteArray const&)));
193 }
194
195 TM::SocketConnection::~SocketConnection()
196 {
197         m_editor_widget->detach(this);
198 }
199
200 QWebSocket* TM::SocketConnection::socket()
201 {
202         QWebSocket * result = qobject_cast<QWebSocket*>(parent());
203         assert(result);
204         return result;
205 }
206
207 void TM::SocketConnection::send_message(QString const &message)
208 {
209         socket()->sendTextMessage(message);
210 }
211
212 void TM::SocketConnection::send_message(QJsonObject const &json)
213 {
214         QJsonDocument doc;
215         doc.setObject(json);
216         send_message(doc.toJson().data());
217 }
218
219 void TM::SocketConnection::save_sentence(int segment_id, int index,
220                                         Text::pointer target_sentence, QJsonArray link)
221 {
222         segment_map_iterator it = m_segments.find(segment_id);
223         assert(it != m_segments.end());
224         TextSentence::pointer sentence = it.value()->at(index);
225         sentence->set_terget_sentence(target_sentence);
226         sentence->set_link_data(link);
227 }
228
229 void TM::SocketConnection::changeEditMode(bool edit_mode)
230 {
231         set_edit_mode(edit_mode);
232 }
233
234 void TM::SocketConnection::set_edit_mode(bool edit_mode)
235 {
236         if(edit_mode == m_edit_mode) return;
237
238         m_edit_mode = edit_mode;
239         QJsonObject json;
240         json["cmd"] = "set_edit_mode";
241         json["edit_mode"] = edit_mode;
242         send_message(json);
243
244 }
245
246 /*!
247  * \brief ウェブブラウザ上でクリックされ、editコマンドが発行されたときに呼び出されます。
248  */
249 void TM::SocketConnection::do_edit(QJsonObject const &json)
250 {
251         int scode = m_editor_widget->source_language();
252
253         assert(json.contains("segment_id"));
254         int segment_id = json["segment_id"].toString().toInt();
255
256         segment_map_iterator it = m_segments.find(segment_id);
257         if(it == m_segments.end())
258         {
259                 assert(json.contains("source"));
260                 QString source = json["source"].toString();
261                 m_current_segment = TextSegment::create(m_service, scode, segment_id, source);
262                 m_segments.insert(segment_id, m_current_segment);
263         }
264         else m_current_segment = it.value();
265
266         m_editor_widget->set_segment(m_current_segment);
267 }
268
269 /*!
270  * \brief ウェブブラウザ上でドキュメントがフォーカスを取得し、
271  * focusコマンドが発行されたときに呼び出されます。
272  */
273 void TM::SocketConnection::do_focus(QJsonObject const &json)
274 {
275         m_editor_widget->attach(this);
276         bool edit_mode = json["edit_mode"].toBool();
277         m_edit_mode = edit_mode;
278         m_editor_widget->set_edit_mode(edit_mode);
279
280         if(m_current_segment) m_editor_widget->set_segment(m_current_segment);
281 }
282
283 void TM::SocketConnection::do_blur(QJsonObject const &)
284 {
285         //m_widget->detach(this);
286 }
287
288 void TM::SocketConnection::do_load(const QJsonObject &json)
289 {
290         m_editor_widget->attach(this);
291         bool edit_mode = json["edit_mode"].toBool();
292         m_edit_mode = edit_mode;
293         m_editor_widget->set_edit_mode(edit_mode);
294
295         //set_edit_mode(m_widget->edit_mode());
296 }
297
298 void TM::SocketConnection::onTextMessageReceived(QString const &message)
299 {
300         QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
301         QString cmd = json["cmd"].toString();
302         if(cmd == "edit") do_edit(json);
303         else if(cmd == "focus") do_focus(json);
304         else if(cmd == "blur") do_blur(json);
305         else if(cmd == "load") do_load(json);
306 }
307
308 void TM::SocketConnection::onBinaryMessageReceived(QByteArray const &message)
309 {
310         qDebug() << message;
311 }
312
313 // SocketServer ---------------------------------------------------------------
314
315 TM::SocketServer::SocketServer(Settings *settings, Service* service,
316                                                         EditorWidget *editor_widget, QObject *parent)
317         : QObject(parent)
318         , m_settings(settings)
319         , m_service(service)
320         , m_server(new QWebSocketServer(QStringLiteral("wordring websocket"),
321                                                                         QWebSocketServer::NonSecureMode, this))
322         , m_editor_widget(editor_widget)
323 {
324         connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
325
326         quint16 port = m_settings->value("SocketServer/port").toUInt();
327         bool result = m_server->listen(QHostAddress::LocalHost, port);
328         if(!result) result = m_server->listen(QHostAddress::LocalHost, 0);
329         if(!result) qFatal("An error occured in SocketServer().");
330 }
331
332 TM::SocketServer::~SocketServer()
333 {
334 }
335
336 quint16 TM::SocketServer::port() const
337 {
338         return m_server->serverPort();
339 }
340
341 void TM::SocketServer::abort()
342 {
343         for(QWebSocket *ws : m_sockets)
344         {
345                 ws->abort();
346                 ws->deleteLater();
347         }
348         m_sockets.clear();
349 }
350
351 void TM::SocketServer::onNewConnection()
352 {
353         QWebSocket *socket = m_server->nextPendingConnection();
354         connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
355         new SocketConnection(m_settings, m_service, m_editor_widget, socket);
356         m_sockets.push_back(socket);
357 }
358
359 void TM::SocketServer::onDisconnected()
360 {
361         QWebSocket *socket = qobject_cast<QWebSocket*>(sender());
362         m_sockets.removeOne(socket);
363         socket->deleteLater();
364 }
365
366
367
368
369
370