blob: 6603183066a78631f1215ef9379b7d6da0ba96f5 [file] [log] [blame]
Yingdi Yu04842232013-10-23 14:03:09 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
4 * Yingdi Yu
5 *
6 * BSD license, See the LICENSE file for more information
7 *
Yingdi Yu42f66462013-10-31 17:38:22 -07008 * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
9 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
10 * Yingdi Yu <yingdi@cs.ucla.edu>
Yingdi Yu04842232013-10-23 14:03:09 -070011 */
12
13#include "chatdialog.h"
14#include "ui_chatdialog.h"
15
Yingdi Yu42f66462013-10-31 17:38:22 -070016#include <QScrollBar>
Yingdi Yu07b5b092013-11-07 17:00:54 -080017#include <QMessageBox>
18#include <QCloseEvent>
Yingdi Yu42f66462013-10-31 17:38:22 -070019
Yingdi Yuc4d08d22013-10-23 23:07:29 -070020#ifndef Q_MOC_RUN
Yingdi Yua1a688f2014-02-06 18:09:22 -080021#include "invitation.h"
22
23#ifdef WITH_SECURITY
Yingdi Yu42f66462013-10-31 17:38:22 -070024#include <sync-intro-certificate.h>
Yingdi Yua1a688f2014-02-06 18:09:22 -080025#endif
26
Yingdi Yu42f66462013-10-31 17:38:22 -070027#include <boost/random/random_device.hpp>
28#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yu6df61252014-01-21 11:05:11 -080029#include <ndn-cpp-dev/security/signature-sha256-with-rsa.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070030#include "logging.h"
31#endif
32
Yingdi Yu04842232013-10-23 14:03:09 -070033using namespace std;
Yingdi Yua1a688f2014-02-06 18:09:22 -080034using namespace ndn;
35using namespace chronos;
Yingdi Yu04842232013-10-23 14:03:09 -070036
Yingdi Yuc4d08d22013-10-23 23:07:29 -070037INIT_LOGGER("ChatDialog");
38
Yingdi Yu42f66462013-10-31 17:38:22 -070039static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
40
41Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
Yingdi Yua1a688f2014-02-06 18:09:22 -080042Q_DECLARE_METATYPE(ndn::shared_ptr<const ndn::Data> )
Yingdi Yu42f66462013-10-31 17:38:22 -070043Q_DECLARE_METATYPE(size_t)
44
Yingdi Yua1a688f2014-02-06 18:09:22 -080045ChatDialog::ChatDialog(shared_ptr<ContactManager> contactManager,
46 shared_ptr<Face> face,
47 const Name& chatroomPrefix,
48 const Name& localPrefix,
49 const Name& defaultIdentity,
50 const string& nick,
Yingdi Yu04842232013-10-23 14:03:09 -070051 QWidget *parent)
Yingdi Yua1a688f2014-02-06 18:09:22 -080052 : QDialog(parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070053 , ui(new Ui::ChatDialog)
54 , m_contactManager(contactManager)
Yingdi Yua1a688f2014-02-06 18:09:22 -080055 , m_face(face)
56 , m_ioService(face->ioService())
Yingdi Yu42f66462013-10-31 17:38:22 -070057 , m_chatroomPrefix(chatroomPrefix)
58 , m_localPrefix(localPrefix)
59 , m_defaultIdentity(defaultIdentity)
Yingdi Yu42372442013-11-06 18:43:31 -080060 , m_nick(nick)
Yingdi Yua1a688f2014-02-06 18:09:22 -080061 , m_scheduler(*face->ioService())
62 , m_keyChain(new KeyChain())
Yingdi Yu42f66462013-10-31 17:38:22 -070063 , m_sock(NULL)
Yingdi Yua1a688f2014-02-06 18:09:22 -080064 , m_lastMsgTime(time::now())
Yingdi Yu42f66462013-10-31 17:38:22 -070065 , m_joined(false)
66 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070067{
Yingdi Yu42f66462013-10-31 17:38:22 -070068 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
Yingdi Yua1a688f2014-02-06 18:09:22 -080069 qRegisterMetaType<ndn::shared_ptr<const ndn::Data> >("ndn::shared_ptr<const ndn::Data>");
Yingdi Yu42f66462013-10-31 17:38:22 -070070 qRegisterMetaType<size_t>("size_t");
71
Yingdi Yuc4d08d22013-10-23 23:07:29 -070072 ui->setupUi(this);
73
Yingdi Yu2ab22e72013-11-10 01:38:21 -080074 QString randString = getRandomString();
Yingdi Yu42f66462013-10-31 17:38:22 -070075 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080076 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu2ab22e72013-11-10 01:38:21 -080077 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
Yingdi Yu42f66462013-10-31 17:38:22 -070078
Yingdi Yua1a688f2014-02-06 18:09:22 -080079 m_session = static_cast<uint64_t>(time::now());
Yingdi Yua524a262014-02-11 22:54:11 -080080 m_scene = new DigestTreeScene(m_ioService, this);
Yingdi Yu42f66462013-10-31 17:38:22 -070081
82 initializeSetting();
83 updateLabels();
84
85 ui->treeViewer->setScene(m_scene);
86 ui->treeViewer->hide();
87 m_scene->plot("Empty");
88 QRectF rect = m_scene->itemsBoundingRect();
89 m_scene->setSceneRect(rect);
90
91 m_rosterModel = new QStringListModel(this);
92 ui->listView->setModel(m_rosterModel);
93
Yingdi Yua0594092013-11-06 22:07:38 -080094 createActions();
95 createTrayIcon();
96
Yingdi Yua1a688f2014-02-06 18:09:22 -080097#ifndef SECURITY
98 m_invitationValidator = make_shared<ValidatorNull>();
99 m_syncValidator = make_shared<ValidatorNull>();
100#else
101 m_invitationValidator = make_shared<chronos::ValidatorInvitation>();
102 m_syncValidator = ...;
103#endif
Yingdi Yu42f66462013-10-31 17:38:22 -0700104
105 connect(ui->inviteButton, SIGNAL(clicked()),
106 this, SLOT(openInviteListDialog()));
107 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
108 this, SLOT(sendInvitationWrapper(QString, bool)));
109 connect(ui->lineEdit, SIGNAL(returnPressed()),
110 this, SLOT(returnPressed()));
111 connect(ui->treeButton, SIGNAL(pressed()),
112 this, SLOT(treeButtonPressed()));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800113 connect(this, SIGNAL(dataReceived(ndn::shared_ptr<const ndn::Data>, bool, bool)),
114 this, SLOT(processData(ndn::shared_ptr<const ndn::Data>, bool, bool)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700115 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
116 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700117 connect(m_scene, SIGNAL(replot()),
118 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800119 connect(trayIcon, SIGNAL(messageClicked()),
120 this, SLOT(showNormal()));
121 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
122 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700123 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
124 this, SLOT(updateRosterList(QStringList)));
Yingdi Yua524a262014-02-11 22:54:11 -0800125 connect(this, SIGNAL(triggerHello()),
126 this, SLOT(sendHello()));
127 connect(this, SIGNAL(triggerJoin()),
128 this, SLOT(sendJoin()));
129 connect(this, SIGNAL(triggerLeave()),
130 this, SLOT(sendLeave()));
131 connect(this, SIGNAL(triggerReplot()),
132 this, SLOT(replot()));
133 connect(this, SIGNAL(triggerEnableTreeDisplay()),
134 this, SLOT(enableTreeDisplay()));
135 connect(this, SIGNAL(triggerReap()),
136 this, SLOT(reap()));
137
Yingdi Yu42f66462013-10-31 17:38:22 -0700138
Yingdi Yu42f66462013-10-31 17:38:22 -0700139
140 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700141}
142
Yingdi Yu42f66462013-10-31 17:38:22 -0700143
Yingdi Yu04842232013-10-23 14:03:09 -0700144ChatDialog::~ChatDialog()
145{
Yingdi Yu42372442013-11-06 18:43:31 -0800146 if(m_sock != NULL)
147 {
148 sendLeave();
149 delete m_sock;
150 m_sock = NULL;
151 }
Yingdi Yuaccbda92013-12-27 08:44:12 +0800152}
153
154void
Yingdi Yu42f66462013-10-31 17:38:22 -0700155ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700156{
Yingdi Yu42372442013-11-06 18:43:31 -0800157 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu64206112013-12-24 11:16:32 +0800158 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toEscapedString()));
Yingdi Yu42f66462013-10-31 17:38:22 -0700159 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
160 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
161 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
162}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700163
Yingdi Yu42f66462013-10-31 17:38:22 -0700164void
165ChatDialog::updateLabels()
166{
167 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
168 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
169 ui->infoLabel->setText(settingDisp);
170 QString prefixDisp;
171 if (m_user.getPrefix().startsWith("/private/local"))
172 {
173 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
174 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
175 }
176 else
177 {
178 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
179 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
180 }
181 ui->prefixLabel->setText(prefixDisp);
182}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700183
Yingdi Yu42f66462013-10-31 17:38:22 -0700184void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800185ChatDialog::initializeSync()
186{
187
188 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
189 m_syncValidator,
190 m_face,
191 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
192 bind(&ChatDialog::processRemoveWrapper, this, _1));
193
194 usleep(100000);
195
Yingdi Yua524a262014-02-11 22:54:11 -0800196 m_scheduler.scheduleEvent(time::milliseconds(600), bind(&ChatDialog::sendJoinWrapper, this));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800197
198 if(static_cast<bool>(m_replotEventId))
199 m_scheduler.cancelEvent(m_replotEventId);
200 m_replotEventId = m_scheduler.schedulePeriodicEvent(time::seconds(0), time::milliseconds(FRESHNESS * 1000),
Yingdi Yua524a262014-02-11 22:54:11 -0800201 bind(&ChatDialog::replotWrapper, this));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800202 disableTreeDisplay();
Yingdi Yua524a262014-02-11 22:54:11 -0800203 m_scheduler.scheduleEvent(time::milliseconds(2200), bind(&ChatDialog::enableTreeDisplayWrapper, this));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800204}
205
206void
Yingdi Yu64206112013-12-24 11:16:32 +0800207ChatDialog::sendInterest(const ndn::Interest& interest,
Yingdi Yua1a688f2014-02-06 18:09:22 -0800208 const OnDataValidated& onValidated,
209 const OnDataValidationFailed& onValidationFailed,
Yingdi Yu64206112013-12-24 11:16:32 +0800210 const OnEventualTimeout& timeoutNotify,
Yingdi Yu7630f642014-01-16 19:13:03 -0800211 int retry /* = 1 */)
Yingdi Yu64206112013-12-24 11:16:32 +0800212{
213 m_face->expressInterest(interest,
Yingdi Yua1a688f2014-02-06 18:09:22 -0800214 bind(&ChatDialog::onTargetData,
215 this, _1, _2, onValidated, onValidationFailed),
216 bind(&ChatDialog::onTargetTimeout,
217 this, _1, retry,
218 onValidated, onValidationFailed, timeoutNotify));
Yingdi Yu64206112013-12-24 11:16:32 +0800219}
220
221void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800222ChatDialog::sendInvitation(shared_ptr<ContactItem> contact, bool isIntroducer)
Yingdi Yu64206112013-12-24 11:16:32 +0800223{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800224#ifdef WITH_SECURITY
225 m_invitationValidator->addTrustAnchor(contact->getSelfEndorseCertificate());
226#endif
Yingdi Yu64206112013-12-24 11:16:32 +0800227
Yingdi Yua1a688f2014-02-06 18:09:22 -0800228 Invitation invitation(contact->getNameSpace(),
229 m_chatroomPrefix.get(-1),
230 m_localPrefix);
231 ndn::Interest interest(invitation.getUnsignedInterestName());
Yingdi Yu64206112013-12-24 11:16:32 +0800232
Yingdi Yua1a688f2014-02-06 18:09:22 -0800233 m_keyChain->signByIdentity(interest, m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -0700234
Yingdi Yua1a688f2014-02-06 18:09:22 -0800235 OnDataValidated onValidated = bind(&ChatDialog::onInviteReplyValidated,
236 this, _1, contact->getNameSpace(), isIntroducer);
Yingdi Yu42f66462013-10-31 17:38:22 -0700237
Yingdi Yua1a688f2014-02-06 18:09:22 -0800238 OnDataValidationFailed onValidationFailed = bind(&ChatDialog::onInviteReplyValidationFailed,
239 this, _1, contact->getNameSpace());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700240
Yingdi Yua1a688f2014-02-06 18:09:22 -0800241 OnEventualTimeout timeoutNotify = bind(&ChatDialog::onInviteReplyTimeout,
242 this, contact->getNameSpace());
Yingdi Yu64206112013-12-24 11:16:32 +0800243
Yingdi Yua1a688f2014-02-06 18:09:22 -0800244 sendInterest(interest, onValidated, onValidationFailed, timeoutNotify);
Yingdi Yu64206112013-12-24 11:16:32 +0800245}
246
247void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800248ChatDialog::onInviteReplyValidated(const shared_ptr<const Data>& data,
249 const Name& identity,
250 bool isIntroducer)
Yingdi Yu64206112013-12-24 11:16:32 +0800251{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800252 string content(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +0800253 if(content == string("nack"))
254 invitationRejected(identity);
255 else
256 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700257}
258
259void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800260ChatDialog::onInviteReplyValidationFailed(const shared_ptr<const Data>& data,
261 const Name& identity)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700262{
Yingdi Yu64206112013-12-24 11:16:32 +0800263 QString msg = QString::fromUtf8("Reply from ") + QString::fromStdString(identity.toUri()) + " cannot be verified!";
264 emit inivationRejection(msg);
265}
266
267
268void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800269ChatDialog::onInviteReplyTimeout(const Name& identity)
Yingdi Yu64206112013-12-24 11:16:32 +0800270{
Yingdi Yu64206112013-12-24 11:16:32 +0800271 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(identity.toUri()) + " times out!";
272 emit inivationRejection(msg);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700273}
274
275void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800276ChatDialog::invitationRejected(const Name& identity)
Yingdi Yu42f66462013-10-31 17:38:22 -0700277{
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800278 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
279 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700280}
281
282void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800283ChatDialog::invitationAccepted(const Name& identity,
284 shared_ptr<const Data> data,
285 const string& inviteePrefix,
286 bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700287{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800288 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yua1a688f2014-02-06 18:09:22 -0800289
290#ifdef WITH_SECURITY
291 SignatureSha256WithRsa sig(data->getSignature());
292 const Name & keyLocatorName = sig.getKeyLocator().getName();
293 shared_ptr<IdentityCertificate> dskCertificate = m_invitationValidator->getValidatedDskCertificate(keyLocatorName);
Yingdi Yu6df61252014-01-21 11:05:11 -0800294 m_syncPolicy->addSyncDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800295 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800296#endif
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700297}
298
Yingdi Yu64206112013-12-24 11:16:32 +0800299void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800300ChatDialog::publishIntroCert(const IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700301{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800302#ifdef WITH_SECURITY
Yingdi Yu64206112013-12-24 11:16:32 +0800303 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
304 dskCertificate.getPublicKeyName(),
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800305 m_keyChain->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yu64206112013-12-24 11:16:32 +0800306 dskCertificate.getNotBefore(),
307 dskCertificate.getNotAfter(),
308 dskCertificate.getPublicKeyInfo(),
309 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800310 ndn::Name certName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu64206112013-12-24 11:16:32 +0800311 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800312 m_keyChain->sign(syncIntroCertificate, certName);
313 m_face->put(syncIntroCertificate);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800314#endif
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700315}
316
Yingdi Yu64206112013-12-24 11:16:32 +0800317void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800318ChatDialog::addChatDataRule(const Name& prefix,
319 const IdentityCertificate& identityCertificate,
Yingdi Yu64206112013-12-24 11:16:32 +0800320 bool isIntroducer)
Yingdi Yua1a688f2014-02-06 18:09:22 -0800321{
322#ifdef WITH_SECURITY
323 m_syncValidator->addSyncDataRule(prefix, identityCertificate, isIntroducer);
324#endif
325}
Yingdi Yu42372442013-11-06 18:43:31 -0800326
327void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800328ChatDialog::addTrustAnchor(const EndorseCertificate& cert)
Yingdi Yu42f66462013-10-31 17:38:22 -0700329{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800330#ifdef WITH_SECURITY
331 m_syncValidator->addTrustAnchor(cert);
332#endif
Yingdi Yu42f66462013-10-31 17:38:22 -0700333}
334
Yingdi Yua1a688f2014-02-06 18:09:22 -0800335
Yingdi Yu42f66462013-10-31 17:38:22 -0700336void
337ChatDialog::returnPressed()
338{
339 QString text = ui->lineEdit->text();
340 if (text.isEmpty())
341 return;
342
343 ui->lineEdit->clear();
344
345 if (text.startsWith("boruoboluomi"))
346 {
347 summonReaper ();
348 // reapButton->show();
349 fitView();
350 return;
351 }
352
353 if (text.startsWith("minimanihong"))
354 {
355 // reapButton->hide();
356 fitView();
357 return;
358 }
359
360 SyncDemo::ChatMessage msg;
361 formChatMessage(text, msg);
362
363 appendMessage(msg);
364
365 sendMsg(msg);
366
367 fitView();
368}
369
370void
371ChatDialog::treeButtonPressed()
372{
373 if (ui->treeViewer->isVisible())
374 {
375 ui->treeViewer->hide();
376 ui->treeButton->setText("Show ChronoSync Tree");
377 }
378 else
379 {
380 ui->treeViewer->show();
381 ui->treeButton->setText("Hide ChronoSync Tree");
382 }
383
384 fitView();
385}
386
387void ChatDialog::disableTreeDisplay()
388{
389 ui->treeButton->setEnabled(false);
390 ui->treeViewer->hide();
391 fitView();
392}
393
394void ChatDialog::enableTreeDisplay()
395{
396 ui->treeButton->setEnabled(true);
397 // treeViewer->show();
398 // fitView();
399}
400
401void
Yingdi Yua524a262014-02-11 22:54:11 -0800402ChatDialog::enableTreeDisplayWrapper()
403{ emit triggerEnableTreeDisplay(); }
404
405void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800406ChatDialog::processTreeUpdateWrapper(const vector<Sync::MissingDataInfo>& v, Sync::SyncSocket *sock)
Yingdi Yu42f66462013-10-31 17:38:22 -0700407{
408 emit treeUpdated(v);
409 _LOG_DEBUG("<<< Tree update signal emitted");
410}
411
412void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800413ChatDialog::processRemoveWrapper(string prefix)
Yingdi Yu42f66462013-10-31 17:38:22 -0700414{
415 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
416}
417
418void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800419ChatDialog::processTreeUpdate(const vector<Sync::MissingDataInfo>& v)
Yingdi Yu42f66462013-10-31 17:38:22 -0700420{
421 _LOG_DEBUG("<<< processing Tree Update");
422
423 if (v.empty())
424 {
425 return;
426 }
427
428 // reflect the changes on digest tree
429 {
430 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
431 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
432 }
433
434 int n = v.size();
435 int totalMissingPackets = 0;
436 for (int i = 0; i < n; i++)
437 {
438 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
439 }
440
441 for (int i = 0; i < n; i++)
442 {
443 if (totalMissingPackets < 4)
444 {
445 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
446 {
447 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
448 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
449 }
450 }
451 else
452 {
453 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
454 }
455 }
456
457 // adjust the view
458 fitView();
459
460}
461
462void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800463ChatDialog::processDataWrapper(const shared_ptr<const Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700464{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800465 emit dataReceived(data, true, false);
466 _LOG_DEBUG("<<< " << data->getName() << " fetched");
Yingdi Yu42f66462013-10-31 17:38:22 -0700467}
468
469void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800470ChatDialog::processDataNoShowWrapper(const shared_ptr<const Data>& data)
471{ emit dataReceived(data, false, false); }
Yingdi Yu42f66462013-10-31 17:38:22 -0700472
473void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800474ChatDialog::processData(shared_ptr<const Data> data, bool show, bool isHistory)
Yingdi Yu42f66462013-10-31 17:38:22 -0700475{
476 SyncDemo::ChatMessage msg;
477 bool corrupted = false;
Yingdi Yua1a688f2014-02-06 18:09:22 -0800478 if (!msg.ParseFromArray(data->getContent().value(), data->getContent().value_size()))
Yingdi Yu42f66462013-10-31 17:38:22 -0700479 {
Yingdi Yua1a688f2014-02-06 18:09:22 -0800480 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << data->getName() << ". what is happening?");
Yingdi Yu42f66462013-10-31 17:38:22 -0700481 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
482 msg.set_from("inconnu");
483 msg.set_type(SyncDemo::ChatMessage::OTHER);
484 corrupted = true;
485 }
486
Yingdi Yu42f66462013-10-31 17:38:22 -0700487 // display msg received from network
488 // we have to do so; this function is called by ccnd thread
489 // so if we call appendMsg directly
490 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
491 // the "cannonical" way to is use signal-slot
492 if (show && !corrupted)
493 {
494 appendMessage(msg, isHistory);
495 }
496
497 if (!isHistory)
498 {
499 // update the tree view
Yingdi Yua1a688f2014-02-06 18:09:22 -0800500 std::string stdStrName = data->getName().toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700501 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
502 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
503 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
504 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
505 {
506 processRemove(prefix.c_str());
507 }
508 else
509 {
510 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
511 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
512 }
513 }
514 fitView();
515}
516
517void
518ChatDialog::processRemove(QString prefix)
519{
520 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
521
522 bool removed = m_scene->removeNode(prefix);
523 if (removed)
524 {
525 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
526 m_scene->plot(m_sock->getRootDigest().c_str());
527 }
528}
529
530void
531ChatDialog::sendJoin()
532{
533 m_joined = true;
534 SyncDemo::ChatMessage msg;
535 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
536 sendMsg(msg);
537 boost::random::random_device rng;
538 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
539 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
Yingdi Yua524a262014-02-11 22:54:11 -0800540 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval), bind(&ChatDialog::sendHelloWrapper, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700541}
542
543void
Yingdi Yua524a262014-02-11 22:54:11 -0800544ChatDialog::sendJoinWrapper()
545{ emit triggerJoin(); }
546
547void
Yingdi Yu42f66462013-10-31 17:38:22 -0700548ChatDialog::sendHello()
549{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800550 int64_t now = time::now();
551 int elapsed = (now - m_lastMsgTime) / 1000000000;
Yingdi Yu42f66462013-10-31 17:38:22 -0700552 if (elapsed >= m_randomizedInterval / 1000)
553 {
554 SyncDemo::ChatMessage msg;
555 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
556 sendMsg(msg);
557 boost::random::random_device rng;
558 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
559 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
Yingdi Yua524a262014-02-11 22:54:11 -0800560 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval), bind(&ChatDialog::sendHelloWrapper, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700561 }
562 else
563 {
Yingdi Yua1a688f2014-02-06 18:09:22 -0800564 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval - elapsed * 1000),
Yingdi Yua524a262014-02-11 22:54:11 -0800565 bind(&ChatDialog::sendHelloWrapper, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700566 }
567}
568
569void
Yingdi Yua524a262014-02-11 22:54:11 -0800570ChatDialog::sendHelloWrapper()
571{ emit triggerHello(); }
572
573void
Yingdi Yu42f66462013-10-31 17:38:22 -0700574ChatDialog::sendLeave()
575{
576 SyncDemo::ChatMessage msg;
577 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
578 sendMsg(msg);
579 usleep(500000);
580 m_sock->remove(m_user.getPrefix().toStdString());
581 usleep(5000);
582 m_joined = false;
583 _LOG_DEBUG("Sync REMOVE signal sent");
584}
585
586void
Yingdi Yua524a262014-02-11 22:54:11 -0800587ChatDialog::sendLeaveWrapper()
588{ emit triggerLeave(); }
589
590void
Yingdi Yu42f66462013-10-31 17:38:22 -0700591ChatDialog::replot()
592{
593 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
594 m_scene->plot(m_sock->getRootDigest().c_str());
595 fitView();
596}
597
598void
Yingdi Yua524a262014-02-11 22:54:11 -0800599ChatDialog::replotWrapper()
600{ emit triggerReplot(); }
601
602void
Yingdi Yu42f66462013-10-31 17:38:22 -0700603ChatDialog::summonReaper()
604{
605 Sync::SyncLogic &logic = m_sock->getLogic ();
606 map<string, bool> branches = logic.getBranchPrefixes();
607 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
608
609 m_zombieList.clear();
610
611 QMapIterator<QString, DisplayUserPtr> it(roster);
612 map<string, bool>::iterator mapIt;
613 while(it.hasNext())
614 {
615 it.next();
616 DisplayUserPtr p = it.value();
617 if (p != DisplayUserNullPtr)
618 {
619 mapIt = branches.find(p->getPrefix().toStdString());
620 if (mapIt != branches.end())
621 {
622 mapIt->second = true;
623 }
624 }
625 }
626
627 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
628 {
629 // this is zombie. all active users should have been marked true
630 if (! mapIt->second)
631 {
632 m_zombieList.append(mapIt->first.c_str());
633 }
634 }
635
636 m_zombieIndex = 0;
637
638 // start reaping
639 reap();
640}
641
642void
643ChatDialog::reap()
644{
645 if (m_zombieIndex < m_zombieList.size())
646 {
647 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
648 m_sock->remove(prefix);
649 _LOG_DEBUG("Reaped: prefix = " << prefix);
650 m_zombieIndex++;
651 // reap again in 10 seconds
Yingdi Yua1a688f2014-02-06 18:09:22 -0800652 m_scheduler.scheduleEvent(time::milliseconds(10000),
Yingdi Yua524a262014-02-11 22:54:11 -0800653 bind(&ChatDialog::reapWrapper, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700654 }
655}
656
657void
Yingdi Yua524a262014-02-11 22:54:11 -0800658ChatDialog::reapWrapper()
659{ emit triggerReap(); }
660
661void
Yingdi Yu42f66462013-10-31 17:38:22 -0700662ChatDialog::updateRosterList(QStringList staleUserList)
663{
664 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
665 QStringList rosterList = m_scene->getRosterList();
666 m_rosterModel->setStringList(rosterList);
667 QString user;
668 QStringListIterator it(staleUserList);
669 while(it.hasNext())
670 {
671 std::string nick = it.next().toStdString();
672 if (nick.empty())
673 continue;
674
675 SyncDemo::ChatMessage msg;
676 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
677 msg.set_from(nick);
678 appendMessage(msg);
679 }
680}
681
682void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800683ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
684{
Yingdi Yu9f657242013-11-10 12:25:43 -0800685 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800686 QString randString = getRandomString();
687 bool needWrite = false;
688 bool needFresh = false;
689
690 QString oldPrefix = m_user.getPrefix();
691 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
692 m_user.setOriginPrefix(originPrefix);
693
694 m_localPrefix = ndn::Name(originPrefix.toStdString());
695 m_localChatPrefix = m_localPrefix;
696 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
697 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
698 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
699 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
700 needWrite = true;
701 needFresh = true;
702 }
703
704 if (needWrite) {
705 updateLabels();
706 }
707
708 if (needFresh && m_sock != NULL)
709 {
710
711 {
712 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
713 m_scene->clearAll();
714 m_scene->plot("Empty");
715 }
716
Yingdi Yu0a953c32013-11-10 10:32:18 -0800717 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800718
719 // keep the new prefix
720 QString newPrefix = m_user.getPrefix();
721 // send leave for the old
722 m_user.setPrefix(oldPrefix);
723 // there is no point to send leave if we haven't joined yet
724 if (m_joined)
725 {
726 sendLeave();
727 }
728 // resume new prefix
729 m_user.setPrefix(newPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800730 delete m_sock;
731 m_sock = NULL;
732
733 try
734 {
735 usleep(100000);
736 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yua1a688f2014-02-06 18:09:22 -0800737 m_syncValidator,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800738 m_face,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800739 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
740 bind(&ChatDialog::processRemoveWrapper, this, _1));
741 usleep(100000);
Yingdi Yua524a262014-02-11 22:54:11 -0800742 m_scheduler.scheduleEvent(time::milliseconds(600), bind(&ChatDialog::sendJoinWrapper, this));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800743 if(static_cast<bool>(m_replotEventId))
744 m_scheduler.cancelEvent(m_replotEventId);
745 m_replotEventId = m_scheduler.schedulePeriodicEvent(time::seconds(0), time::milliseconds(FRESHNESS * 1000),
Yingdi Yua524a262014-02-11 22:54:11 -0800746 bind(&ChatDialog::replotWrapper, this));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800747 disableTreeDisplay();
Yingdi Yua524a262014-02-11 22:54:11 -0800748 m_scheduler.scheduleEvent(time::milliseconds(2200), bind(&ChatDialog::enableTreeDisplayWrapper, this));
Yingdi Yua1a688f2014-02-06 18:09:22 -0800749 }catch(Face::Error& e){
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800750 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
751 }
752 }
753 else if (needFresh && m_sock == NULL)
754 {
755 initializeSync();
756 }
757 else if (m_sock == NULL)
758 {
759 initializeSync();
760 }
761 else
762 {
763// #ifdef __DEBUG
764// std::cout << "Just changing nicks, we're good. " << std::endl;
765// #endif
766 }
767
768 fitView();
769}
770
771void
Yingdi Yua0594092013-11-06 22:07:38 -0800772ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
773{
774 switch (reason)
775 {
776 case QSystemTrayIcon::Trigger:
777 case QSystemTrayIcon::DoubleClick:
778 break;
779 case QSystemTrayIcon::MiddleClick:
780 // showMessage();
781 break;
782 default:;
783 }
784}
785
786
787void
788ChatDialog::messageClicked()
789{
790 this->showMaximized();
791}
792
793
794void
795ChatDialog::createActions()
796{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800797 minimizeAction = new QAction(tr("Mi&nimize"), this);
798 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800799
Yingdi Yu07b5b092013-11-07 17:00:54 -0800800 maximizeAction = new QAction(tr("Ma&ximize"), this);
801 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800802
Yingdi Yu07b5b092013-11-07 17:00:54 -0800803 restoreAction = new QAction(tr("&Restore"), this);
804 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800805
806 // settingsAction = new QAction(tr("Settings"), this);
807 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
808
809 // settingsAction->setMenuRole (QAction::PreferencesRole);
810
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800811 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
812 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800813
Yingdi Yu07b5b092013-11-07 17:00:54 -0800814 quitAction = new QAction(tr("Quit"), this);
815 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800816}
817
818void
819ChatDialog::createTrayIcon()
820{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800821 trayIconMenu = new QMenu(this);
822 trayIconMenu->addAction(minimizeAction);
823 trayIconMenu->addAction(maximizeAction);
824 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800825 // trayIconMenu->addSeparator();
826 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800827 trayIconMenu->addSeparator();
828 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800829 trayIconMenu->addSeparator();
830 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800831
832 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800833 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800834
835 QIcon icon(":/images/icon_small.png");
836 trayIcon->setIcon(icon);
837 setWindowIcon(icon);
838 trayIcon->setToolTip("ChronoChat System Tray Icon");
839 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800840}
841
842
843void
Yingdi Yu42f66462013-10-31 17:38:22 -0700844ChatDialog::resizeEvent(QResizeEvent *e)
845{
846 fitView();
847}
848
849void
850ChatDialog::showEvent(QShowEvent *e)
851{
852 fitView();
853}
854
855void
856ChatDialog::fitView()
857{
858 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
859 QRectF rect = m_scene->itemsBoundingRect();
860 m_scene->setSceneRect(rect);
861 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
862}
863
864void
865ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
866 msg.set_from(m_user.getNick().toStdString());
867 msg.set_to(m_user.getChatroom().toStdString());
868 msg.set_data(text.toUtf8().constData());
Yingdi Yua1a688f2014-02-06 18:09:22 -0800869 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
Yingdi Yu42f66462013-10-31 17:38:22 -0700870 msg.set_timestamp(seconds);
871 msg.set_type(SyncDemo::ChatMessage::CHAT);
872}
873
874void
875ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
876{
877 msg.set_from(m_user.getNick().toStdString());
878 msg.set_to(m_user.getChatroom().toStdString());
Yingdi Yua1a688f2014-02-06 18:09:22 -0800879 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
Yingdi Yu42f66462013-10-31 17:38:22 -0700880 msg.set_timestamp(seconds);
881 msg.set_type(type);
882}
883
884void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800885ChatDialog::updateLocalPrefix()
886{
Yingdi Yu64206112013-12-24 11:16:32 +0800887 ndn::Name interestName("/local/ndn/prefix");
888 ndn::Interest interest(interestName);
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800889 interest.setInterestLifetime(1000);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800890
Yingdi Yu64206112013-12-24 11:16:32 +0800891 m_face->expressInterest(interest,
892 bind(&ChatDialog::onLocalPrefix, this, _1, _2),
Yingdi Yua1a688f2014-02-06 18:09:22 -0800893 bind(&ChatDialog::onLocalPrefixTimeout, this, _1));
894}
895
896
897void
898ChatDialog::onLocalPrefix(const ndn::Interest& interest,
899 ndn::Data& data)
900{
901 string dataString(reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size());
902 QString originPrefix = QString::fromStdString (dataString).trimmed ();
903 string trimmedString = originPrefix.toStdString();
904 m_newLocalPrefix = Name(trimmedString);
905
906 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
907 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
908
909 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
910 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
911}
912
913void
914ChatDialog::onLocalPrefixTimeout(const ndn::Interest& interest)
915{
916 m_newLocalPrefix = m_localPrefix;
917
Yingdi Yu9f657242013-11-10 12:25:43 -0800918 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -0800919 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -0800920 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
921
922 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
923 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800924}
925
926static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
927
928QString
929ChatDialog::getRandomString()
930{
931 std::string randStr;
932 boost::random::random_device rng;
933 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
934 for (int i = 0; i < 10; i ++)
935 {
936 randStr += chars2[index_dist(rng)];
937 }
938 return randStr.c_str();
939}
940
941void
Yingdi Yua0594092013-11-06 22:07:38 -0800942ChatDialog::changeEvent(QEvent *e)
943{
944 switch(e->type())
945 {
946 case QEvent::ActivationChange:
947 if (isActiveWindow())
948 {
949 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
950 }
951 break;
952 default:
953 break;
954 }
955}
956
957void
Yingdi Yu42f66462013-10-31 17:38:22 -0700958ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
959{
960 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
961
962 if (msg.type() == SyncDemo::ChatMessage::CHAT)
963 {
964
965 if (!msg.has_data())
966 {
967 return;
968 }
969
970 if (msg.from().empty() || msg.data().empty())
971 {
972 return;
973 }
974
975 if (!msg.has_timestamp())
976 {
977 return;
978 }
979
980 // if (m_history.size() == MAX_HISTORY_ENTRY)
981 // {
982 // m_history.dequeue();
983 // }
984
985 // m_history.enqueue(msg);
986
987 QTextCharFormat nickFormat;
988 nickFormat.setForeground(Qt::darkGreen);
989 nickFormat.setFontWeight(QFont::Bold);
990 nickFormat.setFontUnderline(true);
991 nickFormat.setUnderlineColor(Qt::gray);
992
993 QTextCursor cursor(ui->textEdit->textCursor());
994 cursor.movePosition(QTextCursor::End);
995 QTextTableFormat tableFormat;
996 tableFormat.setBorder(0);
997 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
998 QString from = QString("%1 ").arg(msg.from().c_str());
999 QTextTableCell fromCell = table->cellAt(0, 0);
1000 fromCell.setFormat(nickFormat);
1001 fromCell.firstCursorPosition().insertText(from);
1002
1003 time_t timestamp = msg.timestamp();
1004 printTimeInCell(table, timestamp);
1005
1006 QTextCursor nextCursor(ui->textEdit->textCursor());
1007 nextCursor.movePosition(QTextCursor::End);
1008 table = nextCursor.insertTable(1, 1, tableFormat);
1009 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
1010 if (!isHistory)
1011 {
1012 showMessage(from, QString::fromUtf8(msg.data().c_str()));
1013 }
1014 }
1015
1016 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
1017 {
1018 QTextCharFormat nickFormat;
1019 nickFormat.setForeground(Qt::gray);
1020 nickFormat.setFontWeight(QFont::Bold);
1021 nickFormat.setFontUnderline(true);
1022 nickFormat.setUnderlineColor(Qt::gray);
1023
1024 QTextCursor cursor(ui->textEdit->textCursor());
1025 cursor.movePosition(QTextCursor::End);
1026 QTextTableFormat tableFormat;
1027 tableFormat.setBorder(0);
1028 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1029 QString action;
1030 if (msg.type() == SyncDemo::ChatMessage::JOIN)
1031 {
1032 action = "enters room";
1033 }
1034 else
1035 {
1036 action = "leaves room";
1037 }
1038
1039 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1040 QTextTableCell fromCell = table->cellAt(0, 0);
1041 fromCell.setFormat(nickFormat);
1042 fromCell.firstCursorPosition().insertText(from);
1043
1044 time_t timestamp = msg.timestamp();
1045 printTimeInCell(table, timestamp);
1046 }
1047
1048 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1049 bar->setValue(bar->maximum());
1050}
1051
1052QString
1053ChatDialog::formatTime(time_t timestamp)
1054{
1055 struct tm *tm_time = localtime(&timestamp);
1056 int hour = tm_time->tm_hour;
1057 QString amOrPM;
1058 if (hour > 12)
1059 {
1060 hour -= 12;
1061 amOrPM = "PM";
1062 }
1063 else
1064 {
1065 amOrPM = "AM";
1066 if (hour == 0)
1067 {
1068 hour = 12;
1069 }
1070 }
1071
1072 char textTime[12];
1073 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1074 return QString(textTime);
1075}
1076
1077void
1078ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1079{
1080 QTextCharFormat timeFormat;
1081 timeFormat.setForeground(Qt::gray);
1082 timeFormat.setFontUnderline(true);
1083 timeFormat.setUnderlineColor(Qt::gray);
1084 QTextTableCell timeCell = table->cellAt(0, 1);
1085 timeCell.setFormat(timeFormat);
1086 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1087}
1088
1089void
1090ChatDialog::showMessage(QString from, QString data)
1091{
1092 if (!isActiveWindow())
1093 {
Yingdi Yua0594092013-11-06 22:07:38 -08001094 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1095 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001096 }
1097}
1098
1099void
1100ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1101{
1102 // send msg
1103 size_t size = msg.ByteSize();
1104 char *buf = new char[size];
1105 msg.SerializeToArray(buf, size);
Yingdi Yua1a688f2014-02-06 18:09:22 -08001106
Yingdi Yu42f66462013-10-31 17:38:22 -07001107 if (!msg.IsInitialized())
1108 {
1109 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1110 abort();
1111 }
1112 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1113
Yingdi Yua1a688f2014-02-06 18:09:22 -08001114 delete[] buf;
Yingdi Yu42f66462013-10-31 17:38:22 -07001115
Yingdi Yua1a688f2014-02-06 18:09:22 -08001116 m_lastMsgTime = time::now();
Yingdi Yu42f66462013-10-31 17:38:22 -07001117
Yingdi Yua1a688f2014-02-06 18:09:22 -08001118 uint64_t nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
Yingdi Yu42f66462013-10-31 17:38:22 -07001119 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1120 std::vector<Sync::MissingDataInfo> v;
1121 v.push_back(mdi);
1122 {
1123 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1124 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1125 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1126 }
1127}
1128
1129void
1130ChatDialog::openInviteListDialog()
1131{
1132 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1133 m_inviteListDialog->show();
1134}
1135
1136void
1137ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1138{
Yingdi Yua1a688f2014-02-06 18:09:22 -08001139 Name inviteeNamespace(invitee.toStdString());
1140 shared_ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
Yingdi Yu42f66462013-10-31 17:38:22 -07001141 sendInvitation(inviteeItem, isIntroducer);
1142}
1143
Yingdi Yu42372442013-11-06 18:43:31 -08001144void
1145ChatDialog::closeEvent(QCloseEvent *e)
1146{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001147 if (trayIcon->isVisible())
1148 {
1149 QMessageBox::information(this, tr("Chronos"),
1150 tr("The program will keep running in the "
1151 "system tray. To terminate the program"
1152 "choose <b>Quit</b> in the context memu"
1153 "of the system tray entry."));
1154 hide();
1155 e->ignore();
1156 }
1157}
1158
1159void
1160ChatDialog::quit()
1161{
Yingdi Yu42372442013-11-06 18:43:31 -08001162 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001163 emit closeChatDialog(m_chatroomPrefix);
1164}
1165
1166
1167
Yingdi Yu42f66462013-10-31 17:38:22 -07001168
Yingdi Yu04842232013-10-23 14:03:09 -07001169#if WAF
1170#include "chatdialog.moc"
1171#include "chatdialog.cpp.moc"
1172#endif