OSDN Git Service

フォルダ名変更
[wordring-tm/wordring-tm.git] / proxy / tmsocket.cpp
1 #include "tmsocket.h"
2 #include "tmeditorwidget.h"
3
4 #include "settings.h"
5 #include "html.h"
6
7 #include <QMutex>
8 #include <QMutexLocker>
9
10 #include <QJsonDocument>
11 #include <QJsonObject>
12
13 #include "debug.h"
14
15
16 // HtmlData -------------------------------------------------------------------
17
18 TM::HtmlData::HtmlData(HtmlNode node_, int begin_, int tail_)
19         : RangeData(begin_, tail_)
20         , m_node(node_)
21 {
22 }
23
24 int TM::HtmlData::type() const { return Type; }
25
26 TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_)
27 {
28         return pointer(new HtmlData(node_, begin_, tail_));
29 }
30
31 // TextConverter --------------------------------------------------------------
32
33 /*!
34  * \brief 引数として与えられたHtmlの範囲から構造化テキストを作成します。
35  */
36 Text::pointer TM::TextConverter::to_text(HtmlRange range)
37 {
38         m_state = 0;
39         Text::pointer result = Text::create();
40         int begin = 0;
41
42         for(HtmlNode const &node : range)
43         {
44                 if(node.type() != HtmlNode::Text) continue;
45                 Text::pointer p = stuff_text(result, node.value());
46                 if(!p) continue;
47                 int num = p->string().size();
48                 HtmlData::pointer hd = HtmlData::create(node, begin, begin + num - 1);
49                 p->set_data(hd);
50                 begin += num;
51         }
52         return result;
53 }
54
55 /*!
56  * \brief 引数として与えられたノードから構造化テキストを作成します。
57  *
58  * 連続する空白文字を一つにまとめます。
59  * 無効なノードが与えられた場合、空の構造化テキストを返します。
60  */
61 Text::pointer TM::TextConverter::stuff_text(Text::pointer parent, QString const &string)
62 {
63         QString outstring;
64         for(QChar const &ch : string)
65         {
66                 if(is_ignorable_white_space(ch)) continue;
67                 if(is_white_space(ch)) outstring.append(' ');
68                 else outstring.append(ch);
69         }
70         if(outstring.isEmpty()) return Text::pointer();
71
72         Text::pointer result = Text::create(parent, outstring);
73         parent->append(result);
74         return result;
75 }
76
77 /*!
78  * \brief 連続する空白文字を検出します。
79  *
80  * 最初の空白文字、あるいは空白文字以外は、falseを返します。
81  * 連続する空白文字の二番目以降に対してのみtrueを返します。
82  */
83 bool TM::TextConverter::is_ignorable_white_space(const QChar &ch)
84 {
85         if(!is_white_space(ch))
86         {
87                 m_state = 0;
88                 return false;
89         }
90         if(m_state == 0)
91         {
92                 m_state = 1;
93                 return false;
94         }
95         return true;
96 }
97
98 /*!
99  * \brief 引数として与えられたchが空白文字の場合true、それ以外の場合falseを返します。
100  */
101 bool TM::TextConverter::is_white_space(QChar const &ch)
102 {
103         switch(ch.unicode())
104         {
105         case 0x9:
106         case 0xA:
107         case 0xC:
108         case 0xD:
109         case 0x20:
110         case 0x200B:
111                 return true;
112         }
113         return false;
114 }
115
116 // SocketConnection -----------------------------------------------------------
117
118 TM::SocketConnection::SocketConnection(EditorWidget *editor_widget, QWebSocket *socket)
119         : QObject(socket)
120         , m_editor_widget(editor_widget)
121         , m_mutex(QMutex::Recursive)
122         , m_edit_mode(false)
123 {
124         connect(socket, SIGNAL(textMessageReceived(QString const&)),
125                         this, SLOT(onTextMessageReceived(QString const&)));
126         connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
127                         this, SLOT(onBinaryMessageReceived(QByteArray const&)));
128 }
129
130 TM::SocketConnection::~SocketConnection()
131 {
132         m_editor_widget->detach(this);
133 }
134
135 QWebSocket* TM::SocketConnection::socket()
136 {
137         QWebSocket * result = qobject_cast<QWebSocket*>(parent());
138         assert(result);
139         return result;
140 }
141
142 void TM::SocketConnection::send_message(QString const &message)
143 {
144         socket()->sendTextMessage(message);
145 }
146
147 void TM::SocketConnection::send_message(QJsonObject const &json)
148 {
149         QJsonDocument doc;
150         doc.setObject(json);
151         send_message(doc.toJson().data());
152 }
153
154 void TM::SocketConnection::changeEditMode(bool edit_mode)
155 {
156         set_edit_mode(edit_mode);
157 }
158
159 void TM::SocketConnection::set_edit_mode(bool edit_mode)
160 {
161         if(edit_mode == m_edit_mode) return;
162
163         m_edit_mode = edit_mode;
164         QJsonObject json;
165         json["cmd"] = "set_edit_mode";
166         json["edit_mode"] = edit_mode;
167         send_message(json);
168
169 }
170
171 /*!
172  * \brief ウェブブラウザ上でクリックされ、editコマンドが発行されたときに呼び出されます。
173  */
174 void TM::SocketConnection::do_edit(QJsonObject const &json)
175 {
176         m_document.set_content(json["source"].toString().toUtf8());
177         HtmlNode body = m_document.first("html").first("body");
178
179         TextConverter tc;
180         m_text = tc.to_text(HtmlRange(body, body));
181         m_editor_widget->set_string(m_text->to_string(), json["target"].toString());
182         //emit editCmd(json["id"].toInt(), m_document.to_string());
183         /*
184         QJsonObject reply;
185         reply["cmd"] = "doedit";
186         reply["id"] = json["id"].toString();
187         reply["html"] = "test2";
188         QJsonDocument doc;
189         doc.setObject(reply);
190         send_message(doc.toJson().data());
191         */
192 }
193
194 /*!
195  * \brief ウェブブラウザ上でドキュメントがフォーカスを取得し、
196  * focusコマンドが発行されたときに呼び出されます。
197  */
198 void TM::SocketConnection::do_focus(QJsonObject const &json)
199 {
200         m_editor_widget->attach(this);
201         bool edit_mode = json["edit_mode"].toBool();
202         m_edit_mode = edit_mode;
203         m_editor_widget->set_edit_mode(edit_mode);
204
205         if(m_text) m_editor_widget->set_string(m_text->to_string(), "");
206 }
207
208 void TM::SocketConnection::do_blur(QJsonObject const &)
209 {
210         //m_widget->detach(this);
211 }
212
213 void TM::SocketConnection::do_load(const QJsonObject &json)
214 {
215         m_editor_widget->attach(this);
216         bool edit_mode = json["edit_mode"].toBool();
217         m_edit_mode = edit_mode;
218         m_editor_widget->set_edit_mode(edit_mode);
219
220         //set_edit_mode(m_widget->edit_mode());
221 }
222
223 void TM::SocketConnection::onTextMessageReceived(QString const &message)
224 {
225         QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
226         QString cmd = json["cmd"].toString();
227         if(cmd == "edit") do_edit(json);
228         else if(cmd == "focus") do_focus(json);
229         else if(cmd == "blur") do_blur(json);
230         else if(cmd == "load") do_load(json);
231 }
232
233 void TM::SocketConnection::onBinaryMessageReceived(QByteArray const &message)
234 {
235         qDebug() << message;
236 }
237
238 // SocketServer ---------------------------------------------------------------
239
240 TM::SocketServer::SocketServer(Settings *settings, EditorWidget *editor_widget, QObject *parent)
241         : QObject(parent)
242         , m_settings(settings)
243         , m_server(new QWebSocketServer(QStringLiteral("wordring websocket"),
244                                                                         QWebSocketServer::NonSecureMode, this))
245         , m_editor_widget(editor_widget)
246 {
247         connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
248
249         quint16 port = m_settings->value("SocketServer/port").toUInt();
250         bool result = m_server->listen(QHostAddress::LocalHost, port);
251         if(!result) result = m_server->listen(QHostAddress::LocalHost, 0);
252         if(!result) qFatal("An error occured in SocketServer().");
253 }
254
255 TM::SocketServer::~SocketServer()
256 {
257 }
258
259 quint16 TM::SocketServer::port() const
260 {
261         return m_server->serverPort();
262 }
263
264 void TM::SocketServer::abort()
265 {
266         for(QWebSocket *ws : m_sockets)
267         {
268                 ws->abort();
269                 ws->deleteLater();
270         }
271         m_sockets.clear();
272 }
273
274 void TM::SocketServer::onNewConnection()
275 {
276         QWebSocket *socket = m_server->nextPendingConnection();
277         connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
278         new SocketConnection(m_editor_widget, socket);
279         m_sockets.push_back(socket);
280 }
281
282 void TM::SocketServer::onDisconnected()
283 {
284         QWebSocket *socket = qobject_cast<QWebSocket*>(sender());
285         m_sockets.removeOne(socket);
286         socket->deleteLater();
287 }
288
289
290
291
292
293