blob: e43aa78ab0aac02d6d69e86ba40ec0810cc57c5d [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 Yu42f66462013-10-31 17:38:22 -070080 m_scene = new DigestTreeScene(this);
81
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)));
125
Yingdi Yu42f66462013-10-31 17:38:22 -0700126
127 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700128}
129
Yingdi Yu42f66462013-10-31 17:38:22 -0700130
Yingdi Yu04842232013-10-23 14:03:09 -0700131ChatDialog::~ChatDialog()
132{
Yingdi Yu42372442013-11-06 18:43:31 -0800133 if(m_sock != NULL)
134 {
135 sendLeave();
136 delete m_sock;
137 m_sock = NULL;
138 }
Yingdi Yuaccbda92013-12-27 08:44:12 +0800139}
140
141void
Yingdi Yu42f66462013-10-31 17:38:22 -0700142ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700143{
Yingdi Yu42372442013-11-06 18:43:31 -0800144 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu64206112013-12-24 11:16:32 +0800145 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toEscapedString()));
Yingdi Yu42f66462013-10-31 17:38:22 -0700146 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
147 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
148 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
149}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700150
Yingdi Yu42f66462013-10-31 17:38:22 -0700151void
152ChatDialog::updateLabels()
153{
154 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
155 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
156 ui->infoLabel->setText(settingDisp);
157 QString prefixDisp;
158 if (m_user.getPrefix().startsWith("/private/local"))
159 {
160 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
161 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
162 }
163 else
164 {
165 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
166 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
167 }
168 ui->prefixLabel->setText(prefixDisp);
169}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700170
Yingdi Yu42f66462013-10-31 17:38:22 -0700171void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800172ChatDialog::initializeSync()
173{
174
175 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
176 m_syncValidator,
177 m_face,
178 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
179 bind(&ChatDialog::processRemoveWrapper, this, _1));
180
181 usleep(100000);
182
183 m_scheduler.scheduleEvent(time::milliseconds(600), bind(&ChatDialog::sendJoin, this));
184
185 if(static_cast<bool>(m_replotEventId))
186 m_scheduler.cancelEvent(m_replotEventId);
187 m_replotEventId = m_scheduler.schedulePeriodicEvent(time::seconds(0), time::milliseconds(FRESHNESS * 1000),
188 bind(&ChatDialog::replot, this));
189 disableTreeDisplay();
190 m_scheduler.scheduleEvent(time::milliseconds(2200), bind(&ChatDialog::enableTreeDisplay, this));
191}
192
193void
Yingdi Yu64206112013-12-24 11:16:32 +0800194ChatDialog::sendInterest(const ndn::Interest& interest,
Yingdi Yua1a688f2014-02-06 18:09:22 -0800195 const OnDataValidated& onValidated,
196 const OnDataValidationFailed& onValidationFailed,
Yingdi Yu64206112013-12-24 11:16:32 +0800197 const OnEventualTimeout& timeoutNotify,
Yingdi Yu7630f642014-01-16 19:13:03 -0800198 int retry /* = 1 */)
Yingdi Yu64206112013-12-24 11:16:32 +0800199{
200 m_face->expressInterest(interest,
Yingdi Yua1a688f2014-02-06 18:09:22 -0800201 bind(&ChatDialog::onTargetData,
202 this, _1, _2, onValidated, onValidationFailed),
203 bind(&ChatDialog::onTargetTimeout,
204 this, _1, retry,
205 onValidated, onValidationFailed, timeoutNotify));
Yingdi Yu64206112013-12-24 11:16:32 +0800206}
207
208void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800209ChatDialog::sendInvitation(shared_ptr<ContactItem> contact, bool isIntroducer)
Yingdi Yu64206112013-12-24 11:16:32 +0800210{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800211#ifdef WITH_SECURITY
212 m_invitationValidator->addTrustAnchor(contact->getSelfEndorseCertificate());
213#endif
Yingdi Yu64206112013-12-24 11:16:32 +0800214
Yingdi Yua1a688f2014-02-06 18:09:22 -0800215 Invitation invitation(contact->getNameSpace(),
216 m_chatroomPrefix.get(-1),
217 m_localPrefix);
218 ndn::Interest interest(invitation.getUnsignedInterestName());
Yingdi Yu64206112013-12-24 11:16:32 +0800219
Yingdi Yua1a688f2014-02-06 18:09:22 -0800220 m_keyChain->signByIdentity(interest, m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -0700221
Yingdi Yua1a688f2014-02-06 18:09:22 -0800222 OnDataValidated onValidated = bind(&ChatDialog::onInviteReplyValidated,
223 this, _1, contact->getNameSpace(), isIntroducer);
Yingdi Yu42f66462013-10-31 17:38:22 -0700224
Yingdi Yua1a688f2014-02-06 18:09:22 -0800225 OnDataValidationFailed onValidationFailed = bind(&ChatDialog::onInviteReplyValidationFailed,
226 this, _1, contact->getNameSpace());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700227
Yingdi Yua1a688f2014-02-06 18:09:22 -0800228 OnEventualTimeout timeoutNotify = bind(&ChatDialog::onInviteReplyTimeout,
229 this, contact->getNameSpace());
Yingdi Yu64206112013-12-24 11:16:32 +0800230
Yingdi Yua1a688f2014-02-06 18:09:22 -0800231 sendInterest(interest, onValidated, onValidationFailed, timeoutNotify);
Yingdi Yu64206112013-12-24 11:16:32 +0800232}
233
234void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800235ChatDialog::onInviteReplyValidated(const shared_ptr<const Data>& data,
236 const Name& identity,
237 bool isIntroducer)
Yingdi Yu64206112013-12-24 11:16:32 +0800238{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800239 string content(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +0800240 if(content == string("nack"))
241 invitationRejected(identity);
242 else
243 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700244}
245
246void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800247ChatDialog::onInviteReplyValidationFailed(const shared_ptr<const Data>& data,
248 const Name& identity)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700249{
Yingdi Yu64206112013-12-24 11:16:32 +0800250 QString msg = QString::fromUtf8("Reply from ") + QString::fromStdString(identity.toUri()) + " cannot be verified!";
251 emit inivationRejection(msg);
252}
253
254
255void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800256ChatDialog::onInviteReplyTimeout(const Name& identity)
Yingdi Yu64206112013-12-24 11:16:32 +0800257{
Yingdi Yu64206112013-12-24 11:16:32 +0800258 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(identity.toUri()) + " times out!";
259 emit inivationRejection(msg);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700260}
261
262void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800263ChatDialog::invitationRejected(const Name& identity)
Yingdi Yu42f66462013-10-31 17:38:22 -0700264{
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800265 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
266 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700267}
268
269void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800270ChatDialog::invitationAccepted(const Name& identity,
271 shared_ptr<const Data> data,
272 const string& inviteePrefix,
273 bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700274{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800275 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yua1a688f2014-02-06 18:09:22 -0800276
277#ifdef WITH_SECURITY
278 SignatureSha256WithRsa sig(data->getSignature());
279 const Name & keyLocatorName = sig.getKeyLocator().getName();
280 shared_ptr<IdentityCertificate> dskCertificate = m_invitationValidator->getValidatedDskCertificate(keyLocatorName);
Yingdi Yu6df61252014-01-21 11:05:11 -0800281 m_syncPolicy->addSyncDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800282 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800283#endif
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700284}
285
Yingdi Yu64206112013-12-24 11:16:32 +0800286void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800287ChatDialog::publishIntroCert(const IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700288{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800289#ifdef WITH_SECURITY
Yingdi Yu64206112013-12-24 11:16:32 +0800290 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
291 dskCertificate.getPublicKeyName(),
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800292 m_keyChain->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yu64206112013-12-24 11:16:32 +0800293 dskCertificate.getNotBefore(),
294 dskCertificate.getNotAfter(),
295 dskCertificate.getPublicKeyInfo(),
296 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800297 ndn::Name certName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu64206112013-12-24 11:16:32 +0800298 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800299 m_keyChain->sign(syncIntroCertificate, certName);
300 m_face->put(syncIntroCertificate);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800301#endif
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700302}
303
Yingdi Yu64206112013-12-24 11:16:32 +0800304void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800305ChatDialog::addChatDataRule(const Name& prefix,
306 const IdentityCertificate& identityCertificate,
Yingdi Yu64206112013-12-24 11:16:32 +0800307 bool isIntroducer)
Yingdi Yua1a688f2014-02-06 18:09:22 -0800308{
309#ifdef WITH_SECURITY
310 m_syncValidator->addSyncDataRule(prefix, identityCertificate, isIntroducer);
311#endif
312}
Yingdi Yu42372442013-11-06 18:43:31 -0800313
314void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800315ChatDialog::addTrustAnchor(const EndorseCertificate& cert)
Yingdi Yu42f66462013-10-31 17:38:22 -0700316{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800317#ifdef WITH_SECURITY
318 m_syncValidator->addTrustAnchor(cert);
319#endif
Yingdi Yu42f66462013-10-31 17:38:22 -0700320}
321
Yingdi Yua1a688f2014-02-06 18:09:22 -0800322
Yingdi Yu42f66462013-10-31 17:38:22 -0700323void
324ChatDialog::returnPressed()
325{
326 QString text = ui->lineEdit->text();
327 if (text.isEmpty())
328 return;
329
330 ui->lineEdit->clear();
331
332 if (text.startsWith("boruoboluomi"))
333 {
334 summonReaper ();
335 // reapButton->show();
336 fitView();
337 return;
338 }
339
340 if (text.startsWith("minimanihong"))
341 {
342 // reapButton->hide();
343 fitView();
344 return;
345 }
346
347 SyncDemo::ChatMessage msg;
348 formChatMessage(text, msg);
349
350 appendMessage(msg);
351
352 sendMsg(msg);
353
354 fitView();
355}
356
357void
358ChatDialog::treeButtonPressed()
359{
360 if (ui->treeViewer->isVisible())
361 {
362 ui->treeViewer->hide();
363 ui->treeButton->setText("Show ChronoSync Tree");
364 }
365 else
366 {
367 ui->treeViewer->show();
368 ui->treeButton->setText("Hide ChronoSync Tree");
369 }
370
371 fitView();
372}
373
374void ChatDialog::disableTreeDisplay()
375{
376 ui->treeButton->setEnabled(false);
377 ui->treeViewer->hide();
378 fitView();
379}
380
381void ChatDialog::enableTreeDisplay()
382{
383 ui->treeButton->setEnabled(true);
384 // treeViewer->show();
385 // fitView();
386}
387
388void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800389ChatDialog::processTreeUpdateWrapper(const vector<Sync::MissingDataInfo>& v, Sync::SyncSocket *sock)
Yingdi Yu42f66462013-10-31 17:38:22 -0700390{
391 emit treeUpdated(v);
392 _LOG_DEBUG("<<< Tree update signal emitted");
393}
394
395void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800396ChatDialog::processRemoveWrapper(string prefix)
Yingdi Yu42f66462013-10-31 17:38:22 -0700397{
398 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
399}
400
401void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800402ChatDialog::processTreeUpdate(const vector<Sync::MissingDataInfo>& v)
Yingdi Yu42f66462013-10-31 17:38:22 -0700403{
404 _LOG_DEBUG("<<< processing Tree Update");
405
406 if (v.empty())
407 {
408 return;
409 }
410
411 // reflect the changes on digest tree
412 {
413 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
414 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
415 }
416
417 int n = v.size();
418 int totalMissingPackets = 0;
419 for (int i = 0; i < n; i++)
420 {
421 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
422 }
423
424 for (int i = 0; i < n; i++)
425 {
426 if (totalMissingPackets < 4)
427 {
428 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
429 {
430 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
431 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
432 }
433 }
434 else
435 {
436 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
437 }
438 }
439
440 // adjust the view
441 fitView();
442
443}
444
445void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800446ChatDialog::processDataWrapper(const shared_ptr<const Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700447{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800448 emit dataReceived(data, true, false);
449 _LOG_DEBUG("<<< " << data->getName() << " fetched");
Yingdi Yu42f66462013-10-31 17:38:22 -0700450}
451
452void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800453ChatDialog::processDataNoShowWrapper(const shared_ptr<const Data>& data)
454{ emit dataReceived(data, false, false); }
Yingdi Yu42f66462013-10-31 17:38:22 -0700455
456void
Yingdi Yua1a688f2014-02-06 18:09:22 -0800457ChatDialog::processData(shared_ptr<const Data> data, bool show, bool isHistory)
Yingdi Yu42f66462013-10-31 17:38:22 -0700458{
459 SyncDemo::ChatMessage msg;
460 bool corrupted = false;
Yingdi Yua1a688f2014-02-06 18:09:22 -0800461 if (!msg.ParseFromArray(data->getContent().value(), data->getContent().value_size()))
Yingdi Yu42f66462013-10-31 17:38:22 -0700462 {
Yingdi Yua1a688f2014-02-06 18:09:22 -0800463 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << data->getName() << ". what is happening?");
Yingdi Yu42f66462013-10-31 17:38:22 -0700464 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
465 msg.set_from("inconnu");
466 msg.set_type(SyncDemo::ChatMessage::OTHER);
467 corrupted = true;
468 }
469
Yingdi Yu42f66462013-10-31 17:38:22 -0700470 // display msg received from network
471 // we have to do so; this function is called by ccnd thread
472 // so if we call appendMsg directly
473 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
474 // the "cannonical" way to is use signal-slot
475 if (show && !corrupted)
476 {
477 appendMessage(msg, isHistory);
478 }
479
480 if (!isHistory)
481 {
482 // update the tree view
Yingdi Yua1a688f2014-02-06 18:09:22 -0800483 std::string stdStrName = data->getName().toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700484 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
485 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
486 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
487 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
488 {
489 processRemove(prefix.c_str());
490 }
491 else
492 {
493 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
494 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
495 }
496 }
497 fitView();
498}
499
500void
501ChatDialog::processRemove(QString prefix)
502{
503 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
504
505 bool removed = m_scene->removeNode(prefix);
506 if (removed)
507 {
508 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
509 m_scene->plot(m_sock->getRootDigest().c_str());
510 }
511}
512
513void
514ChatDialog::sendJoin()
515{
516 m_joined = true;
517 SyncDemo::ChatMessage msg;
518 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
519 sendMsg(msg);
520 boost::random::random_device rng;
521 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
522 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800523 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval), bind(&ChatDialog::sendHello, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700524}
525
526void
527ChatDialog::sendHello()
528{
Yingdi Yua1a688f2014-02-06 18:09:22 -0800529 int64_t now = time::now();
530 int elapsed = (now - m_lastMsgTime) / 1000000000;
Yingdi Yu42f66462013-10-31 17:38:22 -0700531 if (elapsed >= m_randomizedInterval / 1000)
532 {
533 SyncDemo::ChatMessage msg;
534 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
535 sendMsg(msg);
536 boost::random::random_device rng;
537 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
538 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800539 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval), bind(&ChatDialog::sendHello, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700540 }
541 else
542 {
Yingdi Yua1a688f2014-02-06 18:09:22 -0800543 m_scheduler.scheduleEvent(time::milliseconds(m_randomizedInterval - elapsed * 1000),
544 bind(&ChatDialog::sendHello, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700545 }
546}
547
548void
549ChatDialog::sendLeave()
550{
551 SyncDemo::ChatMessage msg;
552 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
553 sendMsg(msg);
554 usleep(500000);
555 m_sock->remove(m_user.getPrefix().toStdString());
556 usleep(5000);
557 m_joined = false;
558 _LOG_DEBUG("Sync REMOVE signal sent");
559}
560
561void
562ChatDialog::replot()
563{
564 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
565 m_scene->plot(m_sock->getRootDigest().c_str());
566 fitView();
567}
568
569void
570ChatDialog::summonReaper()
571{
572 Sync::SyncLogic &logic = m_sock->getLogic ();
573 map<string, bool> branches = logic.getBranchPrefixes();
574 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
575
576 m_zombieList.clear();
577
578 QMapIterator<QString, DisplayUserPtr> it(roster);
579 map<string, bool>::iterator mapIt;
580 while(it.hasNext())
581 {
582 it.next();
583 DisplayUserPtr p = it.value();
584 if (p != DisplayUserNullPtr)
585 {
586 mapIt = branches.find(p->getPrefix().toStdString());
587 if (mapIt != branches.end())
588 {
589 mapIt->second = true;
590 }
591 }
592 }
593
594 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
595 {
596 // this is zombie. all active users should have been marked true
597 if (! mapIt->second)
598 {
599 m_zombieList.append(mapIt->first.c_str());
600 }
601 }
602
603 m_zombieIndex = 0;
604
605 // start reaping
606 reap();
607}
608
609void
610ChatDialog::reap()
611{
612 if (m_zombieIndex < m_zombieList.size())
613 {
614 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
615 m_sock->remove(prefix);
616 _LOG_DEBUG("Reaped: prefix = " << prefix);
617 m_zombieIndex++;
618 // reap again in 10 seconds
Yingdi Yua1a688f2014-02-06 18:09:22 -0800619 m_scheduler.scheduleEvent(time::milliseconds(10000),
620 bind(&ChatDialog::reap, this));
Yingdi Yu42f66462013-10-31 17:38:22 -0700621 }
622}
623
624void
625ChatDialog::updateRosterList(QStringList staleUserList)
626{
627 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
628 QStringList rosterList = m_scene->getRosterList();
629 m_rosterModel->setStringList(rosterList);
630 QString user;
631 QStringListIterator it(staleUserList);
632 while(it.hasNext())
633 {
634 std::string nick = it.next().toStdString();
635 if (nick.empty())
636 continue;
637
638 SyncDemo::ChatMessage msg;
639 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
640 msg.set_from(nick);
641 appendMessage(msg);
642 }
643}
644
645void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800646ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
647{
Yingdi Yu9f657242013-11-10 12:25:43 -0800648 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800649 QString randString = getRandomString();
650 bool needWrite = false;
651 bool needFresh = false;
652
653 QString oldPrefix = m_user.getPrefix();
654 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
655 m_user.setOriginPrefix(originPrefix);
656
657 m_localPrefix = ndn::Name(originPrefix.toStdString());
658 m_localChatPrefix = m_localPrefix;
659 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
660 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
661 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
662 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
663 needWrite = true;
664 needFresh = true;
665 }
666
667 if (needWrite) {
668 updateLabels();
669 }
670
671 if (needFresh && m_sock != NULL)
672 {
673
674 {
675 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
676 m_scene->clearAll();
677 m_scene->plot("Empty");
678 }
679
Yingdi Yu0a953c32013-11-10 10:32:18 -0800680 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800681
682 // keep the new prefix
683 QString newPrefix = m_user.getPrefix();
684 // send leave for the old
685 m_user.setPrefix(oldPrefix);
686 // there is no point to send leave if we haven't joined yet
687 if (m_joined)
688 {
689 sendLeave();
690 }
691 // resume new prefix
692 m_user.setPrefix(newPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800693 delete m_sock;
694 m_sock = NULL;
695
696 try
697 {
698 usleep(100000);
699 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yua1a688f2014-02-06 18:09:22 -0800700 m_syncValidator,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800701 m_face,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800702 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
703 bind(&ChatDialog::processRemoveWrapper, this, _1));
704 usleep(100000);
Yingdi Yua1a688f2014-02-06 18:09:22 -0800705 m_scheduler.scheduleEvent(time::milliseconds(600), bind(&ChatDialog::sendJoin, this));
706 if(static_cast<bool>(m_replotEventId))
707 m_scheduler.cancelEvent(m_replotEventId);
708 m_replotEventId = m_scheduler.schedulePeriodicEvent(time::seconds(0), time::milliseconds(FRESHNESS * 1000),
709 bind(&ChatDialog::replot, this));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800710 disableTreeDisplay();
Yingdi Yua1a688f2014-02-06 18:09:22 -0800711 m_scheduler.scheduleEvent(time::milliseconds(2200), bind(&ChatDialog::enableTreeDisplay, this));
712 }catch(Face::Error& e){
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800713 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
714 }
715 }
716 else if (needFresh && m_sock == NULL)
717 {
718 initializeSync();
719 }
720 else if (m_sock == NULL)
721 {
722 initializeSync();
723 }
724 else
725 {
726// #ifdef __DEBUG
727// std::cout << "Just changing nicks, we're good. " << std::endl;
728// #endif
729 }
730
731 fitView();
732}
733
734void
Yingdi Yua0594092013-11-06 22:07:38 -0800735ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
736{
737 switch (reason)
738 {
739 case QSystemTrayIcon::Trigger:
740 case QSystemTrayIcon::DoubleClick:
741 break;
742 case QSystemTrayIcon::MiddleClick:
743 // showMessage();
744 break;
745 default:;
746 }
747}
748
749
750void
751ChatDialog::messageClicked()
752{
753 this->showMaximized();
754}
755
756
757void
758ChatDialog::createActions()
759{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800760 minimizeAction = new QAction(tr("Mi&nimize"), this);
761 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800762
Yingdi Yu07b5b092013-11-07 17:00:54 -0800763 maximizeAction = new QAction(tr("Ma&ximize"), this);
764 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800765
Yingdi Yu07b5b092013-11-07 17:00:54 -0800766 restoreAction = new QAction(tr("&Restore"), this);
767 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800768
769 // settingsAction = new QAction(tr("Settings"), this);
770 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
771
772 // settingsAction->setMenuRole (QAction::PreferencesRole);
773
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800774 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
775 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800776
Yingdi Yu07b5b092013-11-07 17:00:54 -0800777 quitAction = new QAction(tr("Quit"), this);
778 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800779}
780
781void
782ChatDialog::createTrayIcon()
783{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800784 trayIconMenu = new QMenu(this);
785 trayIconMenu->addAction(minimizeAction);
786 trayIconMenu->addAction(maximizeAction);
787 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800788 // trayIconMenu->addSeparator();
789 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800790 trayIconMenu->addSeparator();
791 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800792 trayIconMenu->addSeparator();
793 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800794
795 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800796 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800797
798 QIcon icon(":/images/icon_small.png");
799 trayIcon->setIcon(icon);
800 setWindowIcon(icon);
801 trayIcon->setToolTip("ChronoChat System Tray Icon");
802 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800803}
804
805
806void
Yingdi Yu42f66462013-10-31 17:38:22 -0700807ChatDialog::resizeEvent(QResizeEvent *e)
808{
809 fitView();
810}
811
812void
813ChatDialog::showEvent(QShowEvent *e)
814{
815 fitView();
816}
817
818void
819ChatDialog::fitView()
820{
821 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
822 QRectF rect = m_scene->itemsBoundingRect();
823 m_scene->setSceneRect(rect);
824 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
825}
826
827void
828ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
829 msg.set_from(m_user.getNick().toStdString());
830 msg.set_to(m_user.getChatroom().toStdString());
831 msg.set_data(text.toUtf8().constData());
Yingdi Yua1a688f2014-02-06 18:09:22 -0800832 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
Yingdi Yu42f66462013-10-31 17:38:22 -0700833 msg.set_timestamp(seconds);
834 msg.set_type(SyncDemo::ChatMessage::CHAT);
835}
836
837void
838ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
839{
840 msg.set_from(m_user.getNick().toStdString());
841 msg.set_to(m_user.getChatroom().toStdString());
Yingdi Yua1a688f2014-02-06 18:09:22 -0800842 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
Yingdi Yu42f66462013-10-31 17:38:22 -0700843 msg.set_timestamp(seconds);
844 msg.set_type(type);
845}
846
847void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800848ChatDialog::updateLocalPrefix()
849{
Yingdi Yu64206112013-12-24 11:16:32 +0800850 ndn::Name interestName("/local/ndn/prefix");
851 ndn::Interest interest(interestName);
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800852 interest.setInterestLifetime(1000);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800853
Yingdi Yu64206112013-12-24 11:16:32 +0800854 m_face->expressInterest(interest,
855 bind(&ChatDialog::onLocalPrefix, this, _1, _2),
Yingdi Yua1a688f2014-02-06 18:09:22 -0800856 bind(&ChatDialog::onLocalPrefixTimeout, this, _1));
857}
858
859
860void
861ChatDialog::onLocalPrefix(const ndn::Interest& interest,
862 ndn::Data& data)
863{
864 string dataString(reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size());
865 QString originPrefix = QString::fromStdString (dataString).trimmed ();
866 string trimmedString = originPrefix.toStdString();
867 m_newLocalPrefix = Name(trimmedString);
868
869 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
870 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
871
872 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
873 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
874}
875
876void
877ChatDialog::onLocalPrefixTimeout(const ndn::Interest& interest)
878{
879 m_newLocalPrefix = m_localPrefix;
880
Yingdi Yu9f657242013-11-10 12:25:43 -0800881 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -0800882 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -0800883 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
884
885 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
886 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800887}
888
889static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
890
891QString
892ChatDialog::getRandomString()
893{
894 std::string randStr;
895 boost::random::random_device rng;
896 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
897 for (int i = 0; i < 10; i ++)
898 {
899 randStr += chars2[index_dist(rng)];
900 }
901 return randStr.c_str();
902}
903
904void
Yingdi Yua0594092013-11-06 22:07:38 -0800905ChatDialog::changeEvent(QEvent *e)
906{
907 switch(e->type())
908 {
909 case QEvent::ActivationChange:
910 if (isActiveWindow())
911 {
912 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
913 }
914 break;
915 default:
916 break;
917 }
918}
919
920void
Yingdi Yu42f66462013-10-31 17:38:22 -0700921ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
922{
923 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
924
925 if (msg.type() == SyncDemo::ChatMessage::CHAT)
926 {
927
928 if (!msg.has_data())
929 {
930 return;
931 }
932
933 if (msg.from().empty() || msg.data().empty())
934 {
935 return;
936 }
937
938 if (!msg.has_timestamp())
939 {
940 return;
941 }
942
943 // if (m_history.size() == MAX_HISTORY_ENTRY)
944 // {
945 // m_history.dequeue();
946 // }
947
948 // m_history.enqueue(msg);
949
950 QTextCharFormat nickFormat;
951 nickFormat.setForeground(Qt::darkGreen);
952 nickFormat.setFontWeight(QFont::Bold);
953 nickFormat.setFontUnderline(true);
954 nickFormat.setUnderlineColor(Qt::gray);
955
956 QTextCursor cursor(ui->textEdit->textCursor());
957 cursor.movePosition(QTextCursor::End);
958 QTextTableFormat tableFormat;
959 tableFormat.setBorder(0);
960 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
961 QString from = QString("%1 ").arg(msg.from().c_str());
962 QTextTableCell fromCell = table->cellAt(0, 0);
963 fromCell.setFormat(nickFormat);
964 fromCell.firstCursorPosition().insertText(from);
965
966 time_t timestamp = msg.timestamp();
967 printTimeInCell(table, timestamp);
968
969 QTextCursor nextCursor(ui->textEdit->textCursor());
970 nextCursor.movePosition(QTextCursor::End);
971 table = nextCursor.insertTable(1, 1, tableFormat);
972 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
973 if (!isHistory)
974 {
975 showMessage(from, QString::fromUtf8(msg.data().c_str()));
976 }
977 }
978
979 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
980 {
981 QTextCharFormat nickFormat;
982 nickFormat.setForeground(Qt::gray);
983 nickFormat.setFontWeight(QFont::Bold);
984 nickFormat.setFontUnderline(true);
985 nickFormat.setUnderlineColor(Qt::gray);
986
987 QTextCursor cursor(ui->textEdit->textCursor());
988 cursor.movePosition(QTextCursor::End);
989 QTextTableFormat tableFormat;
990 tableFormat.setBorder(0);
991 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
992 QString action;
993 if (msg.type() == SyncDemo::ChatMessage::JOIN)
994 {
995 action = "enters room";
996 }
997 else
998 {
999 action = "leaves room";
1000 }
1001
1002 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1003 QTextTableCell fromCell = table->cellAt(0, 0);
1004 fromCell.setFormat(nickFormat);
1005 fromCell.firstCursorPosition().insertText(from);
1006
1007 time_t timestamp = msg.timestamp();
1008 printTimeInCell(table, timestamp);
1009 }
1010
1011 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1012 bar->setValue(bar->maximum());
1013}
1014
1015QString
1016ChatDialog::formatTime(time_t timestamp)
1017{
1018 struct tm *tm_time = localtime(&timestamp);
1019 int hour = tm_time->tm_hour;
1020 QString amOrPM;
1021 if (hour > 12)
1022 {
1023 hour -= 12;
1024 amOrPM = "PM";
1025 }
1026 else
1027 {
1028 amOrPM = "AM";
1029 if (hour == 0)
1030 {
1031 hour = 12;
1032 }
1033 }
1034
1035 char textTime[12];
1036 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1037 return QString(textTime);
1038}
1039
1040void
1041ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1042{
1043 QTextCharFormat timeFormat;
1044 timeFormat.setForeground(Qt::gray);
1045 timeFormat.setFontUnderline(true);
1046 timeFormat.setUnderlineColor(Qt::gray);
1047 QTextTableCell timeCell = table->cellAt(0, 1);
1048 timeCell.setFormat(timeFormat);
1049 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1050}
1051
1052void
1053ChatDialog::showMessage(QString from, QString data)
1054{
1055 if (!isActiveWindow())
1056 {
Yingdi Yua0594092013-11-06 22:07:38 -08001057 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1058 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001059 }
1060}
1061
1062void
1063ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1064{
1065 // send msg
1066 size_t size = msg.ByteSize();
1067 char *buf = new char[size];
1068 msg.SerializeToArray(buf, size);
Yingdi Yua1a688f2014-02-06 18:09:22 -08001069
Yingdi Yu42f66462013-10-31 17:38:22 -07001070 if (!msg.IsInitialized())
1071 {
1072 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1073 abort();
1074 }
1075 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1076
Yingdi Yua1a688f2014-02-06 18:09:22 -08001077 delete[] buf;
Yingdi Yu42f66462013-10-31 17:38:22 -07001078
Yingdi Yua1a688f2014-02-06 18:09:22 -08001079 m_lastMsgTime = time::now();
Yingdi Yu42f66462013-10-31 17:38:22 -07001080
Yingdi Yua1a688f2014-02-06 18:09:22 -08001081 uint64_t nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
Yingdi Yu42f66462013-10-31 17:38:22 -07001082 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1083 std::vector<Sync::MissingDataInfo> v;
1084 v.push_back(mdi);
1085 {
1086 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1087 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1088 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1089 }
1090}
1091
1092void
1093ChatDialog::openInviteListDialog()
1094{
1095 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1096 m_inviteListDialog->show();
1097}
1098
1099void
1100ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1101{
Yingdi Yua1a688f2014-02-06 18:09:22 -08001102 Name inviteeNamespace(invitee.toStdString());
1103 shared_ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
Yingdi Yu42f66462013-10-31 17:38:22 -07001104 sendInvitation(inviteeItem, isIntroducer);
1105}
1106
Yingdi Yu42372442013-11-06 18:43:31 -08001107void
1108ChatDialog::closeEvent(QCloseEvent *e)
1109{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001110 if (trayIcon->isVisible())
1111 {
1112 QMessageBox::information(this, tr("Chronos"),
1113 tr("The program will keep running in the "
1114 "system tray. To terminate the program"
1115 "choose <b>Quit</b> in the context memu"
1116 "of the system tray entry."));
1117 hide();
1118 e->ignore();
1119 }
1120}
1121
1122void
1123ChatDialog::quit()
1124{
Yingdi Yu42372442013-11-06 18:43:31 -08001125 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001126 emit closeChatDialog(m_chatroomPrefix);
1127}
1128
1129
1130
Yingdi Yu42f66462013-10-31 17:38:22 -07001131
Yingdi Yu04842232013-10-23 14:03:09 -07001132#if WAF
1133#include "chatdialog.moc"
1134#include "chatdialog.cpp.moc"
1135#endif