blob: d8010a083c18ed7b918066814902034053fabc3c [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 {
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -070069 m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070070 sendJoin();
Zhenkai Zhu86df7412012-09-27 16:30:20 -070071 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -070072 }
73 catch (Sync::CcnxOperationException ex)
74 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -070075 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 -070076 std::exit(1);
77 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -070078 }
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -070079
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -070080}
81
Zhenkai Zhu82a62752012-06-04 17:11:04 -070082ChatDialog::~ChatDialog()
83{
84 if (m_sock != NULL)
85 {
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070086 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -070087 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070088 sendMsg(msg);
Zhenkai Zhu3974a492012-09-28 14:39:45 -070089 usleep(500000);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -070090 m_sock->remove(m_user.getPrefix().toStdString());
Zhenkai Zhu3974a492012-09-28 14:39:45 -070091 usleep(5000);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -070092#ifdef __DEBUG
93 std::cout << "Sync REMOVE signal sent" << std::endl;
94#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -070095 delete m_sock;
96 m_sock = NULL;
97 }
98}
99
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700100void
101ChatDialog::replot()
102{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700103 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700104 m_scene->plot(m_sock->getRootDigest().c_str());
105}
106
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700107void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700108ChatDialog::updateRosterList(QStringList staleUserList)
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700109{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700110 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700111 QStringList rosterList = m_scene->getRosterList();
112 m_rosterModel->setStringList(rosterList);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700113 QString user;
114 QStringListIterator it(staleUserList);
115 while(it.hasNext())
116 {
117 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700118 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700119 msg.set_from(it.next().toStdString());
120 appendMessage(msg);
121 }
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700122}
123
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700124void
125ChatDialog::setVisible(bool visible)
126{
127 minimizeAction->setEnabled(visible);
128 maximizeAction->setEnabled(!isMaximized());
129 restoreAction->setEnabled(isMaximized() || !visible);
130 QDialog::setVisible(visible);
131}
132
133void
134ChatDialog::closeEvent(QCloseEvent *e)
135{
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700136 if (trayIcon->isVisible() && !m_minimaniho)
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700137 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700138 QMessageBox::information(this, tr("Chronos"),
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700139 tr("The program will keep running in the "
140 "system tray. To terminate the program"
141 "choose <b>Quit</b> in the context memu"
142 "of the system tray entry."));
143 hide();
144 e->ignore();
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700145 m_minimaniho = true;
146 writeSettings();
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700147 }
148}
149
150void
151ChatDialog::changeEvent(QEvent *e)
152{
153 switch(e->type())
154 {
155 case QEvent::ActivationChange:
156 if (isActiveWindow())
157 {
158 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
159 }
160 break;
161 default:
162 break;
163 }
164}
165
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700166void
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700167ChatDialog::appendMessage(const SyncDemo::ChatMessage msg)
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700168{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700169 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
Zhenkai Zhub6338822012-05-31 13:27:24 -0700170
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700171 if (msg.type() == SyncDemo::ChatMessage::CHAT)
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700172 {
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700173
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700174 if (!msg.has_data())
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700175 {
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700176 return;
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700177 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700178
179 if (msg.from().empty() || msg.data().empty())
180 {
181 return;
182 }
183
184 if (!msg.has_timestamp())
185 {
186 return;
187 }
188
189 QTextCharFormat nickFormat;
190 nickFormat.setForeground(Qt::darkGreen);
191 nickFormat.setFontWeight(QFont::Bold);
192 nickFormat.setFontUnderline(true);
193 nickFormat.setUnderlineColor(Qt::gray);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700194
195 QTextCursor cursor(textEdit->textCursor());
196 cursor.movePosition(QTextCursor::End);
197 QTextTableFormat tableFormat;
198 tableFormat.setBorder(0);
199 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
200 QString from = QString("%1 ").arg(msg.from().c_str());
201 QTextTableCell fromCell = table->cellAt(0, 0);
202 fromCell.setFormat(nickFormat);
203 fromCell.firstCursorPosition().insertText(from);
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700204
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700205 time_t timestamp = msg.timestamp();
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700206 printTimeInCell(table, timestamp);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700207
208 QTextCursor nextCursor(textEdit->textCursor());
209 nextCursor.movePosition(QTextCursor::End);
210 table = nextCursor.insertTable(1, 1, tableFormat);
211 table->cellAt(0, 0).firstCursorPosition().insertText(msg.data().c_str());
212 showMessage(from, msg.data().c_str());
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700213 }
214
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700215 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
216 {
217 QTextCharFormat nickFormat;
218 nickFormat.setForeground(Qt::gray);
219 nickFormat.setFontWeight(QFont::Bold);
220 nickFormat.setFontUnderline(true);
221 nickFormat.setUnderlineColor(Qt::gray);
Zhenkai Zhub98e6022012-09-27 13:48:23 -0700222
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700223 QTextCursor cursor(textEdit->textCursor());
224 cursor.movePosition(QTextCursor::End);
225 QTextTableFormat tableFormat;
226 tableFormat.setBorder(0);
227 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
228 QString action;
229 if (msg.type() == SyncDemo::ChatMessage::JOIN)
230 {
231 action = "enters room";
232 }
233 else
234 {
235 action = "leaves room";
236 }
237
238 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
239 QTextTableCell fromCell = table->cellAt(0, 0);
240 fromCell.setFormat(nickFormat);
241 fromCell.firstCursorPosition().insertText(from);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700242
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700243 time_t timestamp = msg.timestamp();
244 printTimeInCell(table, timestamp);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700245 }
246
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700247 QScrollBar *bar = textEdit->verticalScrollBar();
248 bar->setValue(bar->maximum());
249}
250
Zhenkai Zhu560ef1b2012-09-28 14:23:33 -0700251void
252ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
253{
254 QTextCharFormat timeFormat;
255 timeFormat.setForeground(Qt::gray);
256 timeFormat.setFontUnderline(true);
257 timeFormat.setUnderlineColor(Qt::gray);
258 QTextTableCell timeCell = table->cellAt(0, 1);
259 timeCell.setFormat(timeFormat);
260 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
261}
262
263QString
264ChatDialog::formatTime(time_t timestamp)
265{
266 struct tm *tm_time = localtime(&timestamp);
267 int hour = tm_time->tm_hour;
268 QString amOrPM;
269 if (hour > 12)
270 {
271 hour -= 12;
272 amOrPM = "PM";
273 }
274 else
275 {
276 amOrPM = "AM";
277 if (hour == 0)
278 {
279 hour = 12;
280 }
281 }
282
283 char textTime[12];
284 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
285 return QString(textTime);
286}
287
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700288void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700289ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncAppSocket *sock)
290{
291 emit treeUpdated(v);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700292#ifdef __DEBUG
293 std::cout << "<<< Tree update signal emitted" << std::endl;
294#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700295}
296
297void
298ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700299{
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700300#ifdef __DEBUG
301 std::cout << "<<< processing Tree Update" << std::endl;
302#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700303 if (v.empty())
304 {
305 return;
306 }
307
308 // reflect the changes on digest tree
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700309 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700310 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700311 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
312 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700313
314 int n = v.size();
315 int totalMissingPackets = 0;
316 for (int i = 0; i < n; i++)
317 {
318 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
319 }
320
Zhenkai Zhubb198112012-09-27 11:31:42 -0700321 for (int i = 0; i < n; i++)
322 {
323 if (totalMissingPackets < 4)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700324 {
325 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
326 {
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700327 m_sock->fetchRaw(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1, _2, _3), 2);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700328#ifdef __DEBUG
329 std::cout << "<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq() << std::endl;
330#endif
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700331 }
332 }
Zhenkai Zhubb198112012-09-27 11:31:42 -0700333 else
334 {
335 m_sock->fetchRaw(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1, _2, _3), 2);
336 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700337 }
338
339 // adjust the view
340 fitView();
341
342}
343
344void
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700345ChatDialog::processDataWrapper(std::string name, const char *buf, size_t len)
346{
Zhenkai Zhubb198112012-09-27 11:31:42 -0700347 emit dataReceived(name.c_str(), buf, len, true);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700348#ifdef __DEBUG
349 std::cout <<"<<< " << name << " fetched" << std::endl;
350#endif
Zhenkai Zhu64f9ede2012-06-05 11:32:00 -0700351}
352
353void
Zhenkai Zhubb198112012-09-27 11:31:42 -0700354ChatDialog::processDataNoShowWrapper(std::string name, const char *buf, size_t len)
355{
356 emit dataReceived(name.c_str(), buf, len, false);
357}
358
359void
360ChatDialog::processData(QString name, const char *buf, size_t len, bool show)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700361{
362 SyncDemo::ChatMessage msg;
363 if (!msg.ParseFromArray(buf, len))
364 {
Zhenkai Zhubb198112012-09-27 11:31:42 -0700365 std::cerr << "Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?" << std::endl;
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700366 }
367
Zhenkai Zhu10ccb5a2012-06-04 21:55:14 -0700368 // display msg received from network
369 // we have to do so; this function is called by ccnd thread
370 // so if we call appendMsg directly
371 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
372 // the "cannonical" way to is use signal-slot
Zhenkai Zhubb198112012-09-27 11:31:42 -0700373 if (show)
374 {
375 appendMessage(msg);
376 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700377
378 // update the tree view
Zhenkai Zhu097bfe72012-06-05 14:30:17 -0700379 std::string stdStrName = name.toStdString();
380 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
381 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
382#ifdef __DEBUG
383 std::cout <<"<<< updating scene for" << prefix << ": " << msg.from() << std::endl;
384#endif
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700385 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
386 {
387 processRemove(prefix.c_str());
388 }
389 else
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700390 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700391 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700392 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
393 }
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700394 fitView();
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700395}
396
397void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700398ChatDialog::processRemoveWrapper(std::string prefix)
399{
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700400#ifdef __DEBUG
401 std::cout << "Sync REMOVE signal received for prefix: " << prefix << std::endl;
402#endif
403 //emit removeReceived(prefix.c_str());
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700404}
405
406void
407ChatDialog::processRemove(QString prefix)
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700408{
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700409#ifdef __DEBUG
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700410 std::cout << "<<< remove node for prefix" << prefix.toStdString() << std::endl;
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700411#endif
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700412 bool removed = m_scene->removeNode(prefix);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700413 if (removed)
414 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700415 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu591e8c32012-09-26 11:57:50 -0700416 m_scene->plot(m_sock->getRootDigest().c_str());
417 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700418}
419
420void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700421ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700422 msg.set_from(m_user.getNick().toStdString());
423 msg.set_to(m_user.getChatroom().toStdString());
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700424 msg.set_data(text.toStdString());
425 time_t seconds = time(NULL);
426 msg.set_timestamp(seconds);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700427 msg.set_type(SyncDemo::ChatMessage::CHAT);
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700428}
429
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700430void
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700431ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700432{
433 msg.set_from(m_user.getNick().toStdString());
434 msg.set_to(m_user.getChatroom().toStdString());
435 time_t seconds = time(NULL);
436 msg.set_timestamp(seconds);
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700437 msg.set_type(type);
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700438}
439
Zhenkai Zhu59245aa2012-09-26 16:07:04 -0700440static std::string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
441
442QString
443ChatDialog::getRandomString()
444{
445 std::string randStr;
446 boost::random::random_device rng;
447 boost::random::uniform_int_distribution<> index_dist(0, chars.size() - 1);
448 for (int i = 0; i < 10; i ++)
449 {
450 randStr += chars[index_dist(rng)];
451 }
452 return randStr.c_str();
453}
454
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700455bool
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700456ChatDialog::readSettings()
457{
458 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700459 QString nick = s.value("nick", "").toString();
460 QString chatroom = s.value("chatroom", "").toString();
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700461 QString originPrefix = s.value("originPrefix", "").toString();
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700462 m_minimaniho = s.value("minimaniho", false).toBool();
463 if (nick == "" || chatroom == "" || originPrefix == "") {
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700464 QTimer::singleShot(500, this, SLOT(buttonPressed()));
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700465 return false;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700466 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700467 else {
468 m_user.setNick(nick);
469 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700470 m_user.setOriginPrefix(originPrefix);
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700471 m_user.setPrefix(originPrefix + "/" + chatroom + "/" + getRandomString());
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700472 return true;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700473 }
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700474
475// QTimer::singleShot(500, this, SLOT(buttonPressed()));
476 // return false;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700477}
478
479void
480ChatDialog::writeSettings()
481{
482 QSettings s(ORGANIZATION, APPLICATION);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700483 s.setValue("nick", m_user.getNick());
484 s.setValue("chatroom", m_user.getChatroom());
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700485 s.setValue("originPrefix", m_user.getOriginPrefix());
Zhenkai Zhu3974a492012-09-28 14:39:45 -0700486 s.setValue("minimaniho", m_minimaniho);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700487}
488
489void
490ChatDialog::updateLabels()
491{
Zhenkai Zhu76ff02b2012-09-27 21:11:03 -0700492 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
493 infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700494 infoLabel->setText(settingDisp);
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700495 //QString prefixDisp = QString("<Prefix: %1>").arg(m_user.getPrefix());
496 //prefixLabel->setText(prefixDisp);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700497}
498
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700499void
500ChatDialog::returnPressed()
501{
502 QString text = lineEdit->text();
503 if (text.isEmpty())
504 return;
505
Zhenkai Zhub6338822012-05-31 13:27:24 -0700506 lineEdit->clear();
507
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700508 SyncDemo::ChatMessage msg;
509 formChatMessage(text, msg);
510
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700511 appendMessage(msg);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700512
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700513 sendMsg(msg);
514
515 fitView();
516}
517
518void
519ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
520{
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700521 // send msg
522 size_t size = msg.ByteSize();
523 char *buf = new char[size];
524 msg.SerializeToArray(buf, size);
525 if (!msg.IsInitialized())
526 {
527 std::cerr << "Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?" << std::endl;
528 abort();
529 }
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700530 m_sock->publishRaw(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700531
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700532 delete buf;
533
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700534 m_lastMsgTime = time(NULL);
535
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700536 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -0700537 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700538 std::vector<Sync::MissingDataInfo> v;
539 v.push_back(mdi);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700540 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700541 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700542 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
543 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
544 }
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700545}
546
547void
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700548ChatDialog::sendJoin()
549{
550 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700551 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700552 sendMsg(msg);
553 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
554}
555
556void
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700557ChatDialog::sendHello()
558{
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700559 time_t now = time(NULL);
560 int elapsed = now - m_lastMsgTime;
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700561 if (elapsed >= m_randomizedInterval / 1000)
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700562 {
563 SyncDemo::ChatMessage msg;
Zhenkai Zhub5b78462012-09-28 14:10:37 -0700564 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700565 sendMsg(msg);
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700566 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700567 }
568 else
569 {
Zhenkai Zhuee5c90f2012-09-27 14:05:41 -0700570 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
Zhenkai Zhu3e26bb42012-09-27 11:04:09 -0700571 }
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700572}
573
574void
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700575ChatDialog::buttonPressed()
576{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700577 SettingDialog dialog(this, m_user.getNick(), m_user.getChatroom(), m_user.getOriginPrefix());
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700578 connect(&dialog, SIGNAL(updated(QString, QString, QString)), this, SLOT(settingUpdated(QString, QString, QString)));
Zhenkai Zhu85845d22012-06-01 23:10:43 -0700579 dialog.exec();
Zhenkai Zhue837f792012-06-05 20:47:54 -0700580 QTimer::singleShot(100, this, SLOT(checkSetting()));
581}
582
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700583void ChatDialog::treeButtonPressed()
584{
585 if (treeViewer->isVisible())
586 {
587 treeViewer->hide();
588 treeButton->setText("Show Sync Tree");
589 }
590 else
591 {
592 treeViewer->show();
593 treeButton->setText("Hide Sync Tree");
594 }
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700595
596 fitView();
Zhenkai Zhub60b7e12012-09-28 11:34:21 -0700597}
598
Zhenkai Zhue837f792012-06-05 20:47:54 -0700599void
600ChatDialog::checkSetting()
601{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700602 if (m_user.getOriginPrefix().isEmpty() || m_user.getNick().isEmpty() || m_user.getChatroom().isEmpty())
Zhenkai Zhue837f792012-06-05 20:47:54 -0700603 {
604 buttonPressed();
605 }
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -0700606}
Zhenkai Zhue08afe02012-05-31 15:49:07 -0700607
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700608void
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700609ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700610{
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700611 QString randString = getRandomString();
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700612 bool needWrite = false;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700613 bool needFresh = false;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700614 if (!nick.isEmpty() && nick != m_user.getNick()) {
615 m_user.setNick(nick);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700616 needWrite = true;
617 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700618 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
619 m_user.setOriginPrefix(originPrefix);
620 m_user.setPrefix(originPrefix + "/" + m_user.getChatroom() + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700621 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700622 needFresh = true;
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700623 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700624 if (!chatroom.isEmpty() && chatroom != m_user.getChatroom()) {
625 m_user.setChatroom(chatroom);
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700626 m_user.setPrefix(m_user.getOriginPrefix() + "/" + chatroom + "/" + randString);
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700627 needWrite = true;
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700628 needFresh = true;
629 }
630
631 if (needFresh)
632 {
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700633
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700634 {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700635 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu44b43752012-06-05 21:18:37 -0700636 m_scene->clearAll();
637 m_scene->plot("Empty");
638 }
Zhenkai Zhucc4c2c02012-09-27 21:24:37 -0700639
640 textEdit->clear();
641
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700642 // TODO: perhaps need to do a lot. e.g. use a new SyncAppSokcet
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700643 if (m_sock != NULL)
644 {
645 delete m_sock;
646 m_sock = NULL;
647 }
648 std::string syncPrefix = BROADCAST_PREFIX_FOR_SYNC_DEMO;
649 syncPrefix += "/";
650 syncPrefix += m_user.getChatroom().toStdString();
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700651 try
652 {
Zhenkai Zhu2425b6a2012-09-26 17:11:44 -0700653 m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
Zhenkai Zhu25e33e52012-09-28 13:00:07 -0700654 sendJoin();
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700655 m_timer->start(FRESHNESS * 2000);
Zhenkai Zhua4fb1242012-06-05 20:26:05 -0700656 }
657 catch (Sync::CcnxOperationException ex)
658 {
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700659 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 -0700660 std::exit(1);
661 }
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700662
663 fitView();
664
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700665 }
Zhenkai Zhue95c64a2012-09-27 21:46:44 -0700666
Zhenkai Zhu7e9b06d2012-06-02 00:44:42 -0700667 if (needWrite) {
668 writeSettings();
669 updateLabels();
670 }
671}
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700672
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700673void
674ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
675{
676 switch (reason)
677 {
678 case QSystemTrayIcon::Trigger:
679 case QSystemTrayIcon::DoubleClick:
680 break;
681 case QSystemTrayIcon::MiddleClick:
682 // showMessage();
683 break;
684 default:;
685 }
686}
687
688void
689ChatDialog::showMessage(QString from, QString data)
690{
691 // std::cout <<"Showing Message: " << from.toStdString() << ": " << data.toStdString() << std::endl;
692 if (!isActiveWindow())
693 {
694 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
695 trayIcon->setIcon(QIcon(":/images/note.png"));
696 }
697}
698
699void
700ChatDialog::messageClicked()
701{
702 this->showMaximized();
703}
704
705void
706ChatDialog::createActions()
707{
708 minimizeAction = new QAction(tr("Mi&nimize"), this);
709 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
710
711 maximizeAction = new QAction(tr("Ma&ximize"), this);
712 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
713
714 restoreAction = new QAction(tr("&Restore"), this);
715 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
716
717 quitAction = new QAction(tr("Quit"), this);
718 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
719}
720
721void
722ChatDialog::createTrayIcon()
723{
724 trayIconMenu = new QMenu(this);
725 trayIconMenu->addAction(minimizeAction);
726 trayIconMenu->addAction(maximizeAction);
727 trayIconMenu->addAction(restoreAction);
728 trayIconMenu->addSeparator();
729 trayIconMenu->addAction(quitAction);
730
731 trayIcon = new QSystemTrayIcon(this);
732 trayIcon->setContextMenu(trayIconMenu);
733
734 QIcon icon(":/images/icon_small.png");
735 trayIcon->setIcon(icon);
736 setWindowIcon(icon);
Zhenkai Zhu0b3fa332012-09-27 21:58:43 -0700737 trayIcon->setToolTip("Chronos System Tray Icon");
Zhenkai Zhu3a008fc2012-06-08 17:36:39 -0700738 trayIcon->setVisible(true);
739}
740
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700741void
742ChatDialog::resizeEvent(QResizeEvent *e)
743{
744 fitView();
745}
746
747void
748ChatDialog::showEvent(QShowEvent *e)
749{
750 fitView();
751}
752
753void
754ChatDialog::fitView()
755{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700756 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700757 QRectF rect = m_scene->itemsBoundingRect();
758 m_scene->setSceneRect(rect);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700759 treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700760}