blob: 2c3313700c71f991c6ba9fb1ad00a23be94cfef1 [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 Zhu86df7412012-09-27 16:30:20 -070056 connect(m_timer, SIGNAL(timeout()), this, SLOT(replot()));
57 connect(m_scene, SIGNAL(replot()), this, SLOT(replot()));
58 connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showNormal()));
59 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070060 connect(m_scene, SIGNAL(rosterChanged(QStringList)), this, SLOT(updateRosterList(QStringList)));
Zhenkai Zhu86df7412012-09-27 16:30:20 -070061
Zhenkai Zhu82a62752012-06-04 17:11:04 -070062 // create sync socket
63 if(!m_user.getChatroom().isEmpty()) {
64 std::string syncPrefix = BROADCAST_PREFIX_FOR_SYNC_DEMO;
65 syncPrefix += "/";
66 syncPrefix += m_user.getChatroom().toStdString();
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070067 try
68 {
Alexander Afanasyev288edd82012-10-04 17:07:56 -070069 m_sock = new Sync::SyncAppSocket(syncPrefix,
70 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
71 bind(&ChatDialog::processRemoveWrapper, this, _1));
Zhenkai Zhucf024442012-10-05 10:33:08 -070072 QTimer::singleShot(600, this, SLOT(sendJoin()));
Zhenkai Zhu86df7412012-09-27 16:30:20 -070073 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhu0e7a9b22012-10-05 17:55:17 -070074 disableTreeDisplay();
75 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070076 }
77 catch (Sync::CcnxOperationException ex)
78 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -070079 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 -070080 std::exit(1);
81 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -070082 }
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -070083
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -070084}
85
Zhenkai Zhu82a62752012-06-04 17:11:04 -070086ChatDialog::~ChatDialog()
87{
88 if (m_sock != NULL)
89 {
Zhenkai Zhucf024442012-10-05 10:33:08 -070090 sendLeave();
Zhenkai Zhu82a62752012-06-04 17:11:04 -070091 delete m_sock;
92 m_sock = NULL;
93 }
94}
95
Zhenkai Zhu86df7412012-09-27 16:30:20 -070096void
Zhenkai Zhucf024442012-10-05 10:33:08 -070097ChatDialog::sendLeave()
98{
99 SyncDemo::ChatMessage msg;
100 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
101 sendMsg(msg);
102 usleep(500000);
103 m_sock->remove(m_user.getPrefix().toStdString());
104 usleep(5000);
105#ifdef __DEBUG
106 std::cout << "Sync REMOVE signal sent" << std::endl;
107#endif
108}
109
110void
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700111ChatDialog::replot()
112{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700113 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700114 m_scene->plot(m_sock->getRootDigest().c_str());
Zhenkai Zhuc9e4e3c2012-10-02 11:47:31 -0700115 fitView();
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700116}
117
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700118void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700119ChatDialog::updateRosterList(QStringList staleUserList)
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700120{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700121 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700122 QStringList rosterList = m_scene->getRosterList();
123 m_rosterModel->setStringList(rosterList);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700124 QString user;
125 QStringListIterator it(staleUserList);
126 while(it.hasNext())
127 {
Zhenkai Zhu32cbdce2012-10-02 11:09:38 -0700128 std::string nick = it.next().toStdString();
129 if (nick.empty())
130 continue;
131
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700132 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700133 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
Zhenkai Zhu32cbdce2012-10-02 11:09:38 -0700134 msg.set_from(nick);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700135 appendMessage(msg);
136 }
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700137}
138
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700139void
140ChatDialog::setVisible(bool visible)
141{
142 minimizeAction->setEnabled(visible);
143 maximizeAction->setEnabled(!isMaximized());
144 restoreAction->setEnabled(isMaximized() || !visible);
145 QDialog::setVisible(visible);
146}
147
148void
149ChatDialog::closeEvent(QCloseEvent *e)
150{
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700151 if (trayIcon->isVisible() && !m_minimaniho)
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700152 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700153 QMessageBox::information(this, tr("Chronos"),
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700154 tr("The program will keep running in the "
155 "system tray. To terminate the program"
156 "choose <b>Quit</b> in the context memu"
157 "of the system tray entry."));
158 hide();
159 e->ignore();
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700160 m_minimaniho = true;
161 writeSettings();
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700162 }
163}
164
165void
166ChatDialog::changeEvent(QEvent *e)
167{
168 switch(e->type())
169 {
170 case QEvent::ActivationChange:
171 if (isActiveWindow())
172 {
173 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
174 }
175 break;
176 default:
177 break;
178 }
179}
180
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700181void
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700182ChatDialog::appendMessage(const SyncDemo::ChatMessage msg)
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700183{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700184 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
Zhenkai Zhub6338822012-05-31 13:27:24 -0700185
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700186 if (msg.type() == SyncDemo::ChatMessage::CHAT)
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700187 {
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700188
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700189 if (!msg.has_data())
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700190 {
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700191 return;
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700192 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700193
194 if (msg.from().empty() || msg.data().empty())
195 {
196 return;
197 }
198
199 if (!msg.has_timestamp())
200 {
201 return;
202 }
203
204 QTextCharFormat nickFormat;
205 nickFormat.setForeground(Qt::darkGreen);
206 nickFormat.setFontWeight(QFont::Bold);
207 nickFormat.setFontUnderline(true);
208 nickFormat.setUnderlineColor(Qt::gray);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700209
210 QTextCursor cursor(textEdit->textCursor());
211 cursor.movePosition(QTextCursor::End);
212 QTextTableFormat tableFormat;
213 tableFormat.setBorder(0);
214 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
215 QString from = QString("%1 ").arg(msg.from().c_str());
216 QTextTableCell fromCell = table->cellAt(0, 0);
217 fromCell.setFormat(nickFormat);
218 fromCell.firstCursorPosition().insertText(from);
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700219
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700220 time_t timestamp = msg.timestamp();
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700221 printTimeInCell(table, timestamp);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700222
223 QTextCursor nextCursor(textEdit->textCursor());
224 nextCursor.movePosition(QTextCursor::End);
225 table = nextCursor.insertTable(1, 1, tableFormat);
226 table->cellAt(0, 0).firstCursorPosition().insertText(msg.data().c_str());
227 showMessage(from, msg.data().c_str());
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700228 }
229
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700230 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
231 {
232 QTextCharFormat nickFormat;
233 nickFormat.setForeground(Qt::gray);
234 nickFormat.setFontWeight(QFont::Bold);
235 nickFormat.setFontUnderline(true);
236 nickFormat.setUnderlineColor(Qt::gray);
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700237
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700238 QTextCursor cursor(textEdit->textCursor());
239 cursor.movePosition(QTextCursor::End);
240 QTextTableFormat tableFormat;
241 tableFormat.setBorder(0);
242 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
243 QString action;
244 if (msg.type() == SyncDemo::ChatMessage::JOIN)
245 {
246 action = "enters room";
247 }
248 else
249 {
250 action = "leaves room";
251 }
252
253 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
254 QTextTableCell fromCell = table->cellAt(0, 0);
255 fromCell.setFormat(nickFormat);
256 fromCell.firstCursorPosition().insertText(from);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700257
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700258 time_t timestamp = msg.timestamp();
259 printTimeInCell(table, timestamp);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700260 }
261
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700262 QScrollBar *bar = textEdit->verticalScrollBar();
263 bar->setValue(bar->maximum());
264}
265
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700266void
267ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
268{
269 QTextCharFormat timeFormat;
270 timeFormat.setForeground(Qt::gray);
271 timeFormat.setFontUnderline(true);
272 timeFormat.setUnderlineColor(Qt::gray);
273 QTextTableCell timeCell = table->cellAt(0, 1);
274 timeCell.setFormat(timeFormat);
275 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
276}
277
278QString
279ChatDialog::formatTime(time_t timestamp)
280{
281 struct tm *tm_time = localtime(&timestamp);
282 int hour = tm_time->tm_hour;
283 QString amOrPM;
284 if (hour > 12)
285 {
286 hour -= 12;
287 amOrPM = "PM";
288 }
289 else
290 {
291 amOrPM = "AM";
292 if (hour == 0)
293 {
294 hour = 12;
295 }
296 }
297
298 char textTime[12];
299 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
300 return QString(textTime);
301}
302
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700303void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700304ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncAppSocket *sock)
305{
306 emit treeUpdated(v);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700307#ifdef __DEBUG
308 std::cout << "<<< Tree update signal emitted" << std::endl;
309#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700310}
311
312void
313ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700314{
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700315#ifdef __DEBUG
316 std::cout << "<<< processing Tree Update" << std::endl;
317#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700318 if (v.empty())
319 {
320 return;
321 }
322
323 // reflect the changes on digest tree
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700324 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700325 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700326 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
327 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700328
329 int n = v.size();
330 int totalMissingPackets = 0;
331 for (int i = 0; i < n; i++)
332 {
333 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
334 }
335
Zhenkai Zhubb198112012-09-27 11:31:42 -0700336 for (int i = 0; i < n; i++)
337 {
338 if (totalMissingPackets < 4)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700339 {
340 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
341 {
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700342 m_sock->fetchRaw(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1, _2, _3), 2);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700343#ifdef __DEBUG
344 std::cout << "<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq() << std::endl;
345#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700346 }
347 }
Zhenkai Zhubb198112012-09-27 11:31:42 -0700348 else
349 {
350 m_sock->fetchRaw(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1, _2, _3), 2);
351 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700352 }
353
354 // adjust the view
355 fitView();
356
357}
358
359void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700360ChatDialog::processDataWrapper(std::string name, const char *buf, size_t len)
361{
Zhenkai Zhuae29f5a2012-10-05 17:28:04 -0700362 char *tempBuf = new char[len];
363 memcpy(tempBuf, buf, len);
364 emit dataReceived(name.c_str(), tempBuf, len, true);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700365#ifdef __DEBUG
366 std::cout <<"<<< " << name << " fetched" << std::endl;
367#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700368}
369
370void
Zhenkai Zhubb198112012-09-27 11:31:42 -0700371ChatDialog::processDataNoShowWrapper(std::string name, const char *buf, size_t len)
372{
Zhenkai Zhuae29f5a2012-10-05 17:28:04 -0700373 char *tempBuf = new char[len];
374 memcpy(tempBuf, buf, len);
375 emit dataReceived(name.c_str(), tempBuf, len, false);
Zhenkai Zhubb198112012-09-27 11:31:42 -0700376}
377
378void
379ChatDialog::processData(QString name, const char *buf, size_t len, bool show)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700380{
381 SyncDemo::ChatMessage msg;
Zhenkai Zhuef8d0632012-10-05 10:24:55 -0700382 bool corrupted = false;
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700383 if (!msg.ParseFromArray(buf, len))
384 {
Zhenkai Zhubb198112012-09-27 11:31:42 -0700385 std::cerr << "Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?" << std::endl;
Zhenkai Zhuef8d0632012-10-05 10:24:55 -0700386 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
387 msg.set_from("inconnu");
388 msg.set_type(SyncDemo::ChatMessage::OTHER);
389 corrupted = true;
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700390 }
391
Zhenkai Zhuae29f5a2012-10-05 17:28:04 -0700392 delete [] buf;
393 buf = NULL;
394
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700395 // display msg received from network
396 // we have to do so; this function is called by ccnd thread
397 // so if we call appendMsg directly
398 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
399 // the "cannonical" way to is use signal-slot
Zhenkai Zhuef8d0632012-10-05 10:24:55 -0700400 if (show && !corrupted)
Zhenkai Zhubb198112012-09-27 11:31:42 -0700401 {
402 appendMessage(msg);
403 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700404
405 // update the tree view
Zhenkai Zhu097bfe72012-06-05 14:30:17 -0700406 std::string stdStrName = name.toStdString();
407 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
408 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
409#ifdef __DEBUG
410 std::cout <<"<<< updating scene for" << prefix << ": " << msg.from() << std::endl;
411#endif
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700412 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
413 {
414 processRemove(prefix.c_str());
415 }
416 else
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700417 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700418 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700419 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
420 }
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700421 fitView();
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700422}
423
424void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700425ChatDialog::processRemoveWrapper(std::string prefix)
426{
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700427#ifdef __DEBUG
428 std::cout << "Sync REMOVE signal received for prefix: " << prefix << std::endl;
429#endif
430 //emit removeReceived(prefix.c_str());
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700431}
432
433void
434ChatDialog::processRemove(QString prefix)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700435{
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700436#ifdef __DEBUG
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700437 std::cout << "<<< remove node for prefix" << prefix.toStdString() << std::endl;
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700438#endif
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700439 bool removed = m_scene->removeNode(prefix);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700440 if (removed)
441 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700442 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700443 m_scene->plot(m_sock->getRootDigest().c_str());
444 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700445}
446
447void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700448ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700449 msg.set_from(m_user.getNick().toStdString());
450 msg.set_to(m_user.getChatroom().toStdString());
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700451 msg.set_data(text.toStdString());
452 time_t seconds = time(NULL);
453 msg.set_timestamp(seconds);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700454 msg.set_type(SyncDemo::ChatMessage::CHAT);
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700455}
456
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700457void
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700458ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700459{
460 msg.set_from(m_user.getNick().toStdString());
461 msg.set_to(m_user.getChatroom().toStdString());
462 time_t seconds = time(NULL);
463 msg.set_timestamp(seconds);
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700464 msg.set_type(type);
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700465}
466
Zhenkai Zhu59245aa2012-09-26 16:07:04 -0700467static std::string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
468
469QString
470ChatDialog::getRandomString()
471{
472 std::string randStr;
473 boost::random::random_device rng;
474 boost::random::uniform_int_distribution<> index_dist(0, chars.size() - 1);
475 for (int i = 0; i < 10; i ++)
476 {
477 randStr += chars[index_dist(rng)];
478 }
479 return randStr.c_str();
480}
481
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700482bool
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700483ChatDialog::readSettings()
484{
485 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700486 QString nick = s.value("nick", "").toString();
487 QString chatroom = s.value("chatroom", "").toString();
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700488 QString originPrefix = s.value("originPrefix", "").toString();
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700489 m_minimaniho = s.value("minimaniho", false).toBool();
490 if (nick == "" || chatroom == "" || originPrefix == "") {
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700491 QTimer::singleShot(500, this, SLOT(buttonPressed()));
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700492 return false;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700493 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700494 else {
495 m_user.setNick(nick);
496 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700497 m_user.setOriginPrefix(originPrefix);
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700498 m_user.setPrefix(originPrefix + "/" + chatroom + "/" + getRandomString());
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700499 return true;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700500 }
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700501
502// QTimer::singleShot(500, this, SLOT(buttonPressed()));
503 // return false;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700504}
505
506void
507ChatDialog::writeSettings()
508{
509 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700510 s.setValue("nick", m_user.getNick());
511 s.setValue("chatroom", m_user.getChatroom());
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700512 s.setValue("originPrefix", m_user.getOriginPrefix());
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700513 s.setValue("minimaniho", m_minimaniho);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700514}
515
516void
517ChatDialog::updateLabels()
518{
Zhenkai Zhu76ff02b2012-09-27 21:11:03 -0700519 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
520 infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700521 infoLabel->setText(settingDisp);
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700522 //QString prefixDisp = QString("<Prefix: %1>").arg(m_user.getPrefix());
523 //prefixLabel->setText(prefixDisp);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700524}
525
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700526void
527ChatDialog::returnPressed()
528{
529 QString text = lineEdit->text();
530 if (text.isEmpty())
531 return;
532
Zhenkai Zhub6338822012-05-31 13:27:24 -0700533 lineEdit->clear();
534
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700535 SyncDemo::ChatMessage msg;
536 formChatMessage(text, msg);
537
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700538 appendMessage(msg);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700539
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700540 sendMsg(msg);
541
542 fitView();
543}
544
545void
546ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
547{
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700548 // send msg
549 size_t size = msg.ByteSize();
550 char *buf = new char[size];
551 msg.SerializeToArray(buf, size);
552 if (!msg.IsInitialized())
553 {
554 std::cerr << "Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?" << std::endl;
555 abort();
556 }
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700557 m_sock->publishRaw(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700558
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700559 delete buf;
560
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700561 m_lastMsgTime = time(NULL);
562
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700563 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700564 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700565 std::vector<Sync::MissingDataInfo> v;
566 v.push_back(mdi);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700567 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700568 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700569 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
570 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
571 }
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700572}
573
574void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700575ChatDialog::sendJoin()
576{
577 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700578 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700579 sendMsg(msg);
580 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
581}
582
583void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700584ChatDialog::sendHello()
585{
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700586 time_t now = time(NULL);
587 int elapsed = now - m_lastMsgTime;
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700588 if (elapsed >= m_randomizedInterval / 1000)
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700589 {
590 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700591 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700592 sendMsg(msg);
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700593 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700594 }
595 else
596 {
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700597 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700598 }
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700599}
600
601void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700602ChatDialog::buttonPressed()
603{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700604 SettingDialog dialog(this, m_user.getNick(), m_user.getChatroom(), m_user.getOriginPrefix());
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700605 connect(&dialog, SIGNAL(updated(QString, QString, QString)), this, SLOT(settingUpdated(QString, QString, QString)));
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700606 dialog.exec();
Zhenkai Zhue837f792012-06-05 20:47:54 -0700607 QTimer::singleShot(100, this, SLOT(checkSetting()));
608}
609
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700610void ChatDialog::treeButtonPressed()
611{
612 if (treeViewer->isVisible())
613 {
614 treeViewer->hide();
615 treeButton->setText("Show Sync Tree");
616 }
617 else
618 {
619 treeViewer->show();
620 treeButton->setText("Hide Sync Tree");
621 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700622
623 fitView();
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700624}
625
Zhenkai Zhu0e7a9b22012-10-05 17:55:17 -0700626void ChatDialog::enableTreeDisplay()
627{
628 treeButton->setEnabled(true);
629 treeViewer->show();
630 fitView();
631}
632
633void ChatDialog::disableTreeDisplay()
634{
635 treeButton->setEnabled(false);
636 treeViewer->hide();
637 fitView();
638}
639
Zhenkai Zhue837f792012-06-05 20:47:54 -0700640void
641ChatDialog::checkSetting()
642{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700643 if (m_user.getOriginPrefix().isEmpty() || m_user.getNick().isEmpty() || m_user.getChatroom().isEmpty())
Zhenkai Zhue837f792012-06-05 20:47:54 -0700644 {
645 buttonPressed();
646 }
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -0700647}
Zhenkai Zhue08afe02012-05-31 15:49:07 -0700648
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700649void
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700650ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700651{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700652 QString randString = getRandomString();
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700653 bool needWrite = false;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700654 bool needFresh = false;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700655 if (!nick.isEmpty() && nick != m_user.getNick()) {
656 m_user.setNick(nick);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700657 needWrite = true;
658 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700659 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
660 m_user.setOriginPrefix(originPrefix);
661 m_user.setPrefix(originPrefix + "/" + m_user.getChatroom() + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700662 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700663 needFresh = true;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700664 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700665 if (!chatroom.isEmpty() && chatroom != m_user.getChatroom()) {
666 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700667 m_user.setPrefix(m_user.getOriginPrefix() + "/" + chatroom + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700668 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700669 needFresh = true;
670 }
671
672 if (needFresh)
673 {
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700674
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700675 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700676 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700677 m_scene->clearAll();
678 m_scene->plot("Empty");
679 }
Zhenkai Zhucc4c2c02012-09-27 21:24:37 -0700680
681 textEdit->clear();
682
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700683 // TODO: perhaps need to do a lot. e.g. use a new SyncAppSokcet
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700684 if (m_sock != NULL)
685 {
Zhenkai Zhucf024442012-10-05 10:33:08 -0700686 sendLeave();
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700687 delete m_sock;
688 m_sock = NULL;
689 }
690 std::string syncPrefix = BROADCAST_PREFIX_FOR_SYNC_DEMO;
691 syncPrefix += "/";
692 syncPrefix += m_user.getChatroom().toStdString();
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700693 try
694 {
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700695 m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
Alexander Afanasyev288edd82012-10-04 17:07:56 -0700696 QTimer::singleShot(1000, this, SLOT(sendJoin()));
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700697 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhu0e7a9b22012-10-05 17:55:17 -0700698 disableTreeDisplay();
699 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700700 }
701 catch (Sync::CcnxOperationException ex)
702 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700703 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 -0700704 std::exit(1);
705 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700706
707 fitView();
708
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700709 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700710
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700711 if (needWrite) {
712 writeSettings();
713 updateLabels();
714 }
715}
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700716
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700717void
718ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
719{
720 switch (reason)
721 {
722 case QSystemTrayIcon::Trigger:
723 case QSystemTrayIcon::DoubleClick:
724 break;
725 case QSystemTrayIcon::MiddleClick:
726 // showMessage();
727 break;
728 default:;
729 }
730}
731
732void
733ChatDialog::showMessage(QString from, QString data)
734{
735 // std::cout <<"Showing Message: " << from.toStdString() << ": " << data.toStdString() << std::endl;
736 if (!isActiveWindow())
737 {
738 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
739 trayIcon->setIcon(QIcon(":/images/note.png"));
740 }
741}
742
743void
744ChatDialog::messageClicked()
745{
746 this->showMaximized();
747}
748
749void
750ChatDialog::createActions()
751{
752 minimizeAction = new QAction(tr("Mi&nimize"), this);
753 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
754
755 maximizeAction = new QAction(tr("Ma&ximize"), this);
756 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
757
758 restoreAction = new QAction(tr("&Restore"), this);
759 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
760
761 quitAction = new QAction(tr("Quit"), this);
762 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
763}
764
765void
766ChatDialog::createTrayIcon()
767{
768 trayIconMenu = new QMenu(this);
769 trayIconMenu->addAction(minimizeAction);
770 trayIconMenu->addAction(maximizeAction);
771 trayIconMenu->addAction(restoreAction);
772 trayIconMenu->addSeparator();
773 trayIconMenu->addAction(quitAction);
774
775 trayIcon = new QSystemTrayIcon(this);
776 trayIcon->setContextMenu(trayIconMenu);
777
778 QIcon icon(":/images/icon_small.png");
779 trayIcon->setIcon(icon);
780 setWindowIcon(icon);
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700781 trayIcon->setToolTip("Chronos System Tray Icon");
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700782 trayIcon->setVisible(true);
783}
784
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700785void
786ChatDialog::resizeEvent(QResizeEvent *e)
787{
788 fitView();
789}
790
791void
792ChatDialog::showEvent(QShowEvent *e)
793{
794 fitView();
795}
796
797void
798ChatDialog::fitView()
799{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700800 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700801 QRectF rect = m_scene->itemsBoundingRect();
802 m_scene->setSceneRect(rect);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700803 treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700804}