blob: 057098594c64fb26dbeeedde456a40f5e4090310 [file] [log] [blame]
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -07001#include <QtGui>
2#include "chatdialog.h"
Zhenkai Zhu85845d22012-06-01 23:10:43 -07003#include "settingdialog.h"
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -07004#include <ctime>
Zhenkai Zhub6338822012-05-31 13:27:24 -07005#include <iostream>
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -07006#include <QTimer>
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -07007#include <QMetaType>
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -07008#include <QMessageBox>
Zhenkai Zhu59245aa2012-09-26 16:07:04 -07009#include <boost/random/random_device.hpp>
10#include <boost/random/uniform_int_distribution.hpp>
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070011
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -070012#define BROADCAST_PREFIX_FOR_SYNC_DEMO "/ndn/broadcast/chronos"
Zhenkai Zhu82a62752012-06-04 17:11:04 -070013
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -070014static const int HELLO_INTERVAL = 90; // seconds
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -070015
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070016ChatDialog::ChatDialog(QWidget *parent)
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070017 : QDialog(parent), m_sock(NULL), m_lastMsgTime(0)
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070018{
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -070019 // have to register this, otherwise
20 // the signal-slot system won't recognize this type
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -070021 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
22 qRegisterMetaType<size_t>("size_t");
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070023 setupUi(this);
Zhenkai Zhu82a62752012-06-04 17:11:04 -070024 m_session = time(NULL);
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -070025 boost::random::random_device rng;
26 boost::random::uniform_int_distribution<> uniform(1, 29000);
27 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -070028
29 readSettings();
Zhenkai Zhu82a62752012-06-04 17:11:04 -070030
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -070031 updateLabels();
32
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070033 lineEdit->setFocusPolicy(Qt::StrongFocus);
Zhenkai Zhu82a62752012-06-04 17:11:04 -070034 m_scene = new DigestTreeScene(this);
Zhenkai Zhub45e38a2012-06-01 15:44:36 -070035
Zhenkai Zhu82a62752012-06-04 17:11:04 -070036 treeViewer->setScene(m_scene);
37 m_scene->plot("Empty");
38 QRectF rect = m_scene->itemsBoundingRect();
39 m_scene->setSceneRect(rect);
40
Zhenkai Zhu9036e032012-09-27 20:59:33 -070041 listView->setStyleSheet("QListView { alternate-background-color: white; background: #F0F0F0; color: darkGreen; font: bold large; }");
Zhenkai Zhuf55f4382012-09-28 10:58:54 -070042 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
43 listView->setDragDropMode(QAbstractItemView::NoDragDrop);
44 listView->setSelectionMode(QAbstractItemView::NoSelection);
Zhenkai Zhu6082ede2012-09-27 17:28:46 -070045 m_rosterModel = new QStringListModel(this);
46 listView->setModel(m_rosterModel);
47
Zhenkai Zhu86df7412012-09-27 16:30:20 -070048 createActions();
49 createTrayIcon();
50 m_timer = new QTimer(this);
51 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
52 connect(setButton, SIGNAL(pressed()), this, SLOT(buttonPressed()));
Zhenkai Zhub60b7e12012-09-28 11:34:21 -070053 connect(treeButton, SIGNAL(pressed()), this, SLOT(treeButtonPressed()));
Zhenkai Zhu86df7412012-09-27 16:30:20 -070054 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool)), this, SLOT(processData(QString, const char *, size_t, bool)));
55 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)), this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070056 //connect(this, SIGNAL(removeReceived(QString)), this, SLOT(processRemove(QString)));
Zhenkai Zhu86df7412012-09-27 16:30:20 -070057 connect(m_timer, SIGNAL(timeout()), this, SLOT(replot()));
58 connect(m_scene, SIGNAL(replot()), this, SLOT(replot()));
59 connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showNormal()));
60 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070061 connect(m_scene, SIGNAL(rosterChanged(QStringList)), this, SLOT(updateRosterList(QStringList)));
Zhenkai Zhu86df7412012-09-27 16:30:20 -070062
Zhenkai Zhu82a62752012-06-04 17:11:04 -070063 // create sync socket
64 if(!m_user.getChatroom().isEmpty()) {
65 std::string syncPrefix = BROADCAST_PREFIX_FOR_SYNC_DEMO;
66 syncPrefix += "/";
67 syncPrefix += m_user.getChatroom().toStdString();
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070068 try
69 {
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -070070 m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070071 sendJoin();
Zhenkai Zhu86df7412012-09-27 16:30:20 -070072 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070073 }
74 catch (Sync::CcnxOperationException ex)
75 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -070076 QMessageBox::critical(this, tr("Chronos"), tr("Canno connect to ccnd.\n Have you started your ccnd?"), QMessageBox::Ok);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070077 std::exit(1);
78 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -070079 }
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -070080
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -070081//testDraw();
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -070082}
83
Zhenkai Zhu82a62752012-06-04 17:11:04 -070084ChatDialog::~ChatDialog()
85{
86 if (m_sock != NULL)
87 {
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070088 SyncDemo::ChatMessage msg;
89 formHelloMessage(msg);
90 msg.set_type(SyncDemo::ChatMessage::LEAVE);
91 sendMsg(msg);
92 usleep(100000);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -070093 m_sock->remove(m_user.getPrefix().toStdString());
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070094 usleep(100000);
95#ifdef __DEBUG
96 std::cout << "Sync REMOVE signal sent" << std::endl;
97#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -070098 delete m_sock;
99 m_sock = NULL;
100 }
101}
102
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700103void
104ChatDialog::replot()
105{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700106 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700107 m_scene->plot(m_sock->getRootDigest().c_str());
108}
109
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700110void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700111ChatDialog::updateRosterList(QStringList staleUserList)
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700112{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700113 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700114 QStringList rosterList = m_scene->getRosterList();
115 m_rosterModel->setStringList(rosterList);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700116 QString user;
117 QStringListIterator it(staleUserList);
118 while(it.hasNext())
119 {
120 SyncDemo::ChatMessage msg;
121 formHelloMessage(msg);
122 msg.set_type(SyncDemo::ChatMessage::LEAVE);
123 msg.set_from(it.next().toStdString());
124 appendMessage(msg);
125 }
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700126}
127
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700128void
129ChatDialog::setVisible(bool visible)
130{
131 minimizeAction->setEnabled(visible);
132 maximizeAction->setEnabled(!isMaximized());
133 restoreAction->setEnabled(isMaximized() || !visible);
134 QDialog::setVisible(visible);
135}
136
137void
138ChatDialog::closeEvent(QCloseEvent *e)
139{
140 if (trayIcon->isVisible())
141 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700142 QMessageBox::information(this, tr("Chronos"),
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700143 tr("The program will keep running in the "
144 "system tray. To terminate the program"
145 "choose <b>Quit</b> in the context memu"
146 "of the system tray entry."));
147 hide();
148 e->ignore();
149 }
150}
151
152void
153ChatDialog::changeEvent(QEvent *e)
154{
155 switch(e->type())
156 {
157 case QEvent::ActivationChange:
158 if (isActiveWindow())
159 {
160 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
161 }
162 break;
163 default:
164 break;
165 }
166}
167
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700168void
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700169ChatDialog::appendMessage(const SyncDemo::ChatMessage msg)
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700170{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700171 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
Zhenkai Zhub6338822012-05-31 13:27:24 -0700172
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700173 if (msg.type() == SyncDemo::ChatMessage::CHAT)
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700174 {
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700175
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700176 if (!msg.has_data())
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700177 {
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700178 return;
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700179 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700180
181 if (msg.from().empty() || msg.data().empty())
182 {
183 return;
184 }
185
186 if (!msg.has_timestamp())
187 {
188 return;
189 }
190
191 QTextCharFormat nickFormat;
192 nickFormat.setForeground(Qt::darkGreen);
193 nickFormat.setFontWeight(QFont::Bold);
194 nickFormat.setFontUnderline(true);
195 nickFormat.setUnderlineColor(Qt::gray);
196 QTextCharFormat timeFormat;
197 timeFormat.setForeground(Qt::gray);
198 timeFormat.setFontUnderline(true);
199 timeFormat.setUnderlineColor(Qt::gray);
200
201 QTextCursor cursor(textEdit->textCursor());
202 cursor.movePosition(QTextCursor::End);
203 QTextTableFormat tableFormat;
204 tableFormat.setBorder(0);
205 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
206 QString from = QString("%1 ").arg(msg.from().c_str());
207 QTextTableCell fromCell = table->cellAt(0, 0);
208 fromCell.setFormat(nickFormat);
209 fromCell.firstCursorPosition().insertText(from);
210 QTextTableCell timeCell = table->cellAt(0, 1);
211 timeCell.setFormat(timeFormat);
212 time_t timestamp = msg.timestamp();
213 struct tm *tm_time = localtime(&timestamp);
214 int hour = tm_time->tm_hour;
215 QString amOrPM;
216 if (hour > 12)
217 {
218 hour -= 12;
219 amOrPM = "PM";
220 }
221 else
222 {
223 amOrPM = "AM";
224 if (hour == 0)
225 {
226 hour = 12;
227 }
228 }
229
230 char textTime[12];
231 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
232 timeCell.firstCursorPosition().insertText(textTime);
233
234
235 QTextCursor nextCursor(textEdit->textCursor());
236 nextCursor.movePosition(QTextCursor::End);
237 table = nextCursor.insertTable(1, 1, tableFormat);
238 table->cellAt(0, 0).firstCursorPosition().insertText(msg.data().c_str());
239 showMessage(from, msg.data().c_str());
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700240 }
241
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700242 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
243 {
244 QTextCharFormat nickFormat;
245 nickFormat.setForeground(Qt::gray);
246 nickFormat.setFontWeight(QFont::Bold);
247 nickFormat.setFontUnderline(true);
248 nickFormat.setUnderlineColor(Qt::gray);
249 QTextCharFormat timeFormat;
250 timeFormat.setForeground(Qt::gray);
251 timeFormat.setFontUnderline(true);
252 timeFormat.setUnderlineColor(Qt::gray);
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700253
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700254 QTextCursor cursor(textEdit->textCursor());
255 cursor.movePosition(QTextCursor::End);
256 QTextTableFormat tableFormat;
257 tableFormat.setBorder(0);
258 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
259 QString action;
260 if (msg.type() == SyncDemo::ChatMessage::JOIN)
261 {
262 action = "enters room";
263 }
264 else
265 {
266 action = "leaves room";
267 }
268
269 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
270 QTextTableCell fromCell = table->cellAt(0, 0);
271 fromCell.setFormat(nickFormat);
272 fromCell.firstCursorPosition().insertText(from);
273 QTextTableCell timeCell = table->cellAt(0, 1);
274 timeCell.setFormat(timeFormat);
275 time_t timestamp = msg.timestamp();
276 struct tm *tm_time = localtime(&timestamp);
277 int hour = tm_time->tm_hour;
278 QString amOrPM;
279 if (hour > 12)
280 {
281 hour -= 12;
282 amOrPM = "PM";
283 }
284 else
285 {
286 amOrPM = "AM";
287 if (hour == 0)
288 {
289 hour = 12;
290 }
291 }
292
293 char textTime[12];
294 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
295 timeCell.firstCursorPosition().insertText(textTime);
296 }
297
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700298 QScrollBar *bar = textEdit->verticalScrollBar();
299 bar->setValue(bar->maximum());
300}
301
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700302void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700303ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncAppSocket *sock)
304{
305 emit treeUpdated(v);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700306#ifdef __DEBUG
307 std::cout << "<<< Tree update signal emitted" << std::endl;
308#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700309}
310
311void
312ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700313{
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700314#ifdef __DEBUG
315 std::cout << "<<< processing Tree Update" << std::endl;
316#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700317 if (v.empty())
318 {
319 return;
320 }
321
322 // reflect the changes on digest tree
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700323 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700324 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700325 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
326 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700327
328 int n = v.size();
329 int totalMissingPackets = 0;
330 for (int i = 0; i < n; i++)
331 {
332 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
333 }
334
Zhenkai Zhubb198112012-09-27 11:31:42 -0700335 for (int i = 0; i < n; i++)
336 {
337 if (totalMissingPackets < 4)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700338 {
339 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
340 {
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700341 m_sock->fetchRaw(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1, _2, _3), 2);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700342#ifdef __DEBUG
343 std::cout << "<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq() << std::endl;
344#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700345 }
346 }
Zhenkai Zhubb198112012-09-27 11:31:42 -0700347 else
348 {
349 m_sock->fetchRaw(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1, _2, _3), 2);
350 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700351 }
352
353 // adjust the view
354 fitView();
355
356}
357
358void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700359ChatDialog::processDataWrapper(std::string name, const char *buf, size_t len)
360{
Zhenkai Zhubb198112012-09-27 11:31:42 -0700361 emit dataReceived(name.c_str(), buf, len, true);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700362#ifdef __DEBUG
363 std::cout <<"<<< " << name << " fetched" << std::endl;
364#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700365}
366
367void
Zhenkai Zhubb198112012-09-27 11:31:42 -0700368ChatDialog::processDataNoShowWrapper(std::string name, const char *buf, size_t len)
369{
370 emit dataReceived(name.c_str(), buf, len, false);
371}
372
373void
374ChatDialog::processData(QString name, const char *buf, size_t len, bool show)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700375{
376 SyncDemo::ChatMessage msg;
377 if (!msg.ParseFromArray(buf, len))
378 {
Zhenkai Zhubb198112012-09-27 11:31:42 -0700379 std::cerr << "Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?" << std::endl;
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700380 }
381
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700382 // display msg received from network
383 // we have to do so; this function is called by ccnd thread
384 // so if we call appendMsg directly
385 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
386 // the "cannonical" way to is use signal-slot
Zhenkai Zhubb198112012-09-27 11:31:42 -0700387 if (show)
388 {
389 appendMessage(msg);
390 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700391
392 // update the tree view
Zhenkai Zhu097bfe72012-06-05 14:30:17 -0700393 std::string stdStrName = name.toStdString();
394 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
395 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
396#ifdef __DEBUG
397 std::cout <<"<<< updating scene for" << prefix << ": " << msg.from() << std::endl;
398#endif
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700399 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
400 {
401 processRemove(prefix.c_str());
402 }
403 else
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700404 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700405 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700406 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
407 }
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700408 fitView();
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700409}
410
411void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700412ChatDialog::processRemoveWrapper(std::string prefix)
413{
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700414#ifdef __DEBUG
415 std::cout << "Sync REMOVE signal received for prefix: " << prefix << std::endl;
416#endif
417 //emit removeReceived(prefix.c_str());
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700418}
419
420void
421ChatDialog::processRemove(QString prefix)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700422{
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700423#ifdef __DEBUG
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700424 std::cout << "<<< remove node for prefix" << prefix.toStdString() << std::endl;
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700425#endif
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700426 bool removed = m_scene->removeNode(prefix);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700427 if (removed)
428 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700429 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700430 m_scene->plot(m_sock->getRootDigest().c_str());
431 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700432}
433
434void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700435ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700436 msg.set_from(m_user.getNick().toStdString());
437 msg.set_to(m_user.getChatroom().toStdString());
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700438 msg.set_data(text.toStdString());
439 time_t seconds = time(NULL);
440 msg.set_timestamp(seconds);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700441 msg.set_type(SyncDemo::ChatMessage::CHAT);
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700442}
443
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700444void
445ChatDialog::formHelloMessage(SyncDemo::ChatMessage &msg)
446{
447 msg.set_from(m_user.getNick().toStdString());
448 msg.set_to(m_user.getChatroom().toStdString());
449 time_t seconds = time(NULL);
450 msg.set_timestamp(seconds);
451 msg.set_type(SyncDemo::ChatMessage::HELLO);
452}
453
Zhenkai Zhu59245aa2012-09-26 16:07:04 -0700454static std::string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
455
456QString
457ChatDialog::getRandomString()
458{
459 std::string randStr;
460 boost::random::random_device rng;
461 boost::random::uniform_int_distribution<> index_dist(0, chars.size() - 1);
462 for (int i = 0; i < 10; i ++)
463 {
464 randStr += chars[index_dist(rng)];
465 }
466 return randStr.c_str();
467}
468
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700469bool
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700470ChatDialog::readSettings()
471{
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700472#ifndef __DEBUG
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700473 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700474 QString nick = s.value("nick", "").toString();
475 QString chatroom = s.value("chatroom", "").toString();
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700476 QString originPrefix = s.value("originPrefix", "").toString();
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700477 if (nick == "" || chatroom == "" || prefix == "") {
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700478 QTimer::singleShot(500, this, SLOT(buttonPressed()));
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700479 return false;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700480 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700481 else {
482 m_user.setNick(nick);
483 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700484 m_user.setOriginPrefix(originPrefix);
485 m_user.setPrefix(origin_prefix + "/" + chatroom + "/" + getRandomString());
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700486 return true;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700487 }
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700488#else
489 QTimer::singleShot(500, this, SLOT(buttonPressed()));
490 return false;
491#endif
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700492}
493
494void
495ChatDialog::writeSettings()
496{
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700497#ifndef __DEBUG
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700498 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700499 s.setValue("nick", m_user.getNick());
500 s.setValue("chatroom", m_user.getChatroom());
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700501 s.setValue("originPrefix", m_user.getOriginPrefix());
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700502#endif
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700503}
504
505void
506ChatDialog::updateLabels()
507{
Zhenkai Zhu76ff02b2012-09-27 21:11:03 -0700508 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
509 infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700510 infoLabel->setText(settingDisp);
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700511 //QString prefixDisp = QString("<Prefix: %1>").arg(m_user.getPrefix());
512 //prefixLabel->setText(prefixDisp);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700513}
514
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700515void
516ChatDialog::returnPressed()
517{
518 QString text = lineEdit->text();
519 if (text.isEmpty())
520 return;
521
Zhenkai Zhub6338822012-05-31 13:27:24 -0700522 lineEdit->clear();
523
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700524 SyncDemo::ChatMessage msg;
525 formChatMessage(text, msg);
526
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700527 appendMessage(msg);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700528
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700529 sendMsg(msg);
530
531 fitView();
532}
533
534void
535ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
536{
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700537 // send msg
538 size_t size = msg.ByteSize();
539 char *buf = new char[size];
540 msg.SerializeToArray(buf, size);
541 if (!msg.IsInitialized())
542 {
543 std::cerr << "Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?" << std::endl;
544 abort();
545 }
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700546 m_sock->publishRaw(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700547
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700548 delete buf;
549
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700550 m_lastMsgTime = time(NULL);
551
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700552 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700553 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700554 std::vector<Sync::MissingDataInfo> v;
555 v.push_back(mdi);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700556 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700557 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700558 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
559 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
560 }
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700561}
562
563void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700564ChatDialog::sendJoin()
565{
566 SyncDemo::ChatMessage msg;
567 formHelloMessage(msg);
568 msg.set_type(SyncDemo::ChatMessage::JOIN);
569 sendMsg(msg);
570 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
571}
572
573void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700574ChatDialog::sendHello()
575{
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700576 time_t now = time(NULL);
577 int elapsed = now - m_lastMsgTime;
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700578 if (elapsed >= m_randomizedInterval / 1000)
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700579 {
580 SyncDemo::ChatMessage msg;
581 formHelloMessage(msg);
582 sendMsg(msg);
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700583 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700584 }
585 else
586 {
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700587 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700588 }
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700589}
590
591void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700592ChatDialog::buttonPressed()
593{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700594 SettingDialog dialog(this, m_user.getNick(), m_user.getChatroom(), m_user.getOriginPrefix());
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700595 connect(&dialog, SIGNAL(updated(QString, QString, QString)), this, SLOT(settingUpdated(QString, QString, QString)));
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700596 dialog.exec();
Zhenkai Zhue837f792012-06-05 20:47:54 -0700597 QTimer::singleShot(100, this, SLOT(checkSetting()));
598}
599
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700600void ChatDialog::treeButtonPressed()
601{
602 if (treeViewer->isVisible())
603 {
604 treeViewer->hide();
605 treeButton->setText("Show Sync Tree");
606 }
607 else
608 {
609 treeViewer->show();
610 treeButton->setText("Hide Sync Tree");
611 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700612
613 fitView();
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700614}
615
Zhenkai Zhue837f792012-06-05 20:47:54 -0700616void
617ChatDialog::checkSetting()
618{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700619 if (m_user.getOriginPrefix().isEmpty() || m_user.getNick().isEmpty() || m_user.getChatroom().isEmpty())
Zhenkai Zhue837f792012-06-05 20:47:54 -0700620 {
621 buttonPressed();
622 }
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -0700623}
Zhenkai Zhue08afe02012-05-31 15:49:07 -0700624
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700625void
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700626ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700627{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700628 QString randString = getRandomString();
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700629 bool needWrite = false;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700630 bool needFresh = false;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700631 if (!nick.isEmpty() && nick != m_user.getNick()) {
632 m_user.setNick(nick);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700633 needWrite = true;
634 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700635 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
636 m_user.setOriginPrefix(originPrefix);
637 m_user.setPrefix(originPrefix + "/" + m_user.getChatroom() + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700638 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700639 needFresh = true;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700640 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700641 if (!chatroom.isEmpty() && chatroom != m_user.getChatroom()) {
642 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700643 m_user.setPrefix(m_user.getOriginPrefix() + "/" + chatroom + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700644 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700645 needFresh = true;
646 }
647
648 if (needFresh)
649 {
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700650
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700651 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700652 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700653 m_scene->clearAll();
654 m_scene->plot("Empty");
655 }
Zhenkai Zhucc4c2c02012-09-27 21:24:37 -0700656
657 textEdit->clear();
658
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700659 // TODO: perhaps need to do a lot. e.g. use a new SyncAppSokcet
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700660 if (m_sock != NULL)
661 {
662 delete m_sock;
663 m_sock = NULL;
664 }
665 std::string syncPrefix = BROADCAST_PREFIX_FOR_SYNC_DEMO;
666 syncPrefix += "/";
667 syncPrefix += m_user.getChatroom().toStdString();
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700668 try
669 {
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700670 m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700671 sendJoin();
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700672 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700673 }
674 catch (Sync::CcnxOperationException ex)
675 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700676 QMessageBox::critical(this, tr("Chronos"), tr("Canno connect to ccnd.\n Have you started your ccnd?"), QMessageBox::Ok);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700677 std::exit(1);
678 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700679
680 fitView();
681
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700682 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700683
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700684 if (needWrite) {
685 writeSettings();
686 updateLabels();
687 }
688}
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700689
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700690void
691ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
692{
693 switch (reason)
694 {
695 case QSystemTrayIcon::Trigger:
696 case QSystemTrayIcon::DoubleClick:
697 break;
698 case QSystemTrayIcon::MiddleClick:
699 // showMessage();
700 break;
701 default:;
702 }
703}
704
705void
706ChatDialog::showMessage(QString from, QString data)
707{
708 // std::cout <<"Showing Message: " << from.toStdString() << ": " << data.toStdString() << std::endl;
709 if (!isActiveWindow())
710 {
711 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
712 trayIcon->setIcon(QIcon(":/images/note.png"));
713 }
714}
715
716void
717ChatDialog::messageClicked()
718{
719 this->showMaximized();
720}
721
722void
723ChatDialog::createActions()
724{
725 minimizeAction = new QAction(tr("Mi&nimize"), this);
726 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
727
728 maximizeAction = new QAction(tr("Ma&ximize"), this);
729 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
730
731 restoreAction = new QAction(tr("&Restore"), this);
732 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
733
734 quitAction = new QAction(tr("Quit"), this);
735 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
736}
737
738void
739ChatDialog::createTrayIcon()
740{
741 trayIconMenu = new QMenu(this);
742 trayIconMenu->addAction(minimizeAction);
743 trayIconMenu->addAction(maximizeAction);
744 trayIconMenu->addAction(restoreAction);
745 trayIconMenu->addSeparator();
746 trayIconMenu->addAction(quitAction);
747
748 trayIcon = new QSystemTrayIcon(this);
749 trayIcon->setContextMenu(trayIconMenu);
750
751 QIcon icon(":/images/icon_small.png");
752 trayIcon->setIcon(icon);
753 setWindowIcon(icon);
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700754 trayIcon->setToolTip("Chronos System Tray Icon");
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700755 trayIcon->setVisible(true);
756}
757
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700758void
759ChatDialog::resizeEvent(QResizeEvent *e)
760{
761 fitView();
762}
763
764void
765ChatDialog::showEvent(QShowEvent *e)
766{
767 fitView();
768}
769
770void
771ChatDialog::fitView()
772{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700773 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700774 QRectF rect = m_scene->itemsBoundingRect();
775 m_scene->setSceneRect(rect);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700776 treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700777}