blob: 6a3f70c962c5db4cf1961fa75bd610ca7a2f92b5 [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
21#include <ndn.cxx/security/identity/identity-manager.h>
Yingdi Yu2ab22e72013-11-10 01:38:21 -080022#include <ndn.cxx/security/policy/no-verify-policy-manager.h>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070023#include <ndn.cxx/security/encryption/basic-encryption-manager.h>
Yingdi Yu42f66462013-10-31 17:38:22 -070024#include <sync-intro-certificate.h>
25#include <boost/random/random_device.hpp>
26#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070027#include "logging.h"
28#endif
29
Yingdi Yu04842232013-10-23 14:03:09 -070030using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070031
Yingdi Yuc4d08d22013-10-23 23:07:29 -070032INIT_LOGGER("ChatDialog");
33
Yingdi Yu42f66462013-10-31 17:38:22 -070034static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
35
36Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
37Q_DECLARE_METATYPE(size_t)
38
39ChatDialog::ChatDialog(ndn::Ptr<ContactManager> contactManager,
40 const ndn::Name& chatroomPrefix,
41 const ndn::Name& localPrefix,
42 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080043 const std::string& nick,
44 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070045 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070046: QDialog(parent)
47 , ui(new Ui::ChatDialog)
48 , m_contactManager(contactManager)
49 , m_chatroomPrefix(chatroomPrefix)
50 , m_localPrefix(localPrefix)
51 , m_defaultIdentity(defaultIdentity)
Yingdi Yub35b8652013-11-07 11:32:40 -080052 , m_invitationPolicyManager(ndn::Ptr<InvitationPolicyManager>(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toUri(), m_defaultIdentity)))
Yingdi Yu42372442013-11-06 18:43:31 -080053 , m_nick(nick)
Yingdi Yu42f66462013-10-31 17:38:22 -070054 , m_sock(NULL)
55 , m_lastMsgTime(0)
56 // , m_historyInitialized(false)
57 , m_joined(false)
58 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070059{
Yingdi Yu42f66462013-10-31 17:38:22 -070060 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
61 qRegisterMetaType<size_t>("size_t");
62
Yingdi Yuc4d08d22013-10-23 23:07:29 -070063 ui->setupUi(this);
64
Yingdi Yu2ab22e72013-11-10 01:38:21 -080065 QString randString = getRandomString();
Yingdi Yu42f66462013-10-31 17:38:22 -070066 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080067 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu2ab22e72013-11-10 01:38:21 -080068 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
Yingdi Yu42f66462013-10-31 17:38:22 -070069
70 m_session = time(NULL);
71 m_scene = new DigestTreeScene(this);
72
73 initializeSetting();
74 updateLabels();
75
76 ui->treeViewer->setScene(m_scene);
77 ui->treeViewer->hide();
78 m_scene->plot("Empty");
79 QRectF rect = m_scene->itemsBoundingRect();
80 m_scene->setSceneRect(rect);
81
82 m_rosterModel = new QStringListModel(this);
83 ui->listView->setModel(m_rosterModel);
84
Yingdi Yua0594092013-11-06 22:07:38 -080085 createActions();
86 createTrayIcon();
87
Yingdi Yu42f66462013-10-31 17:38:22 -070088 m_timer = new QTimer(this);
89
Yingdi Yu42372442013-11-06 18:43:31 -080090 setWrapper(trial);
Yingdi Yu42f66462013-10-31 17:38:22 -070091
92 connect(ui->inviteButton, SIGNAL(clicked()),
93 this, SLOT(openInviteListDialog()));
94 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
95 this, SLOT(sendInvitationWrapper(QString, bool)));
96 connect(ui->lineEdit, SIGNAL(returnPressed()),
97 this, SLOT(returnPressed()));
98 connect(ui->treeButton, SIGNAL(pressed()),
99 this, SLOT(treeButtonPressed()));
100 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
101 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
102 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
103 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
104 connect(m_timer, SIGNAL(timeout()),
105 this, SLOT(replot()));
106 connect(m_scene, SIGNAL(replot()),
107 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800108 connect(trayIcon, SIGNAL(messageClicked()),
109 this, SLOT(showNormal()));
110 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
111 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700112 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
113 this, SLOT(updateRosterList(QStringList)));
114
Yingdi Yu42f66462013-10-31 17:38:22 -0700115
116 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700117}
118
Yingdi Yu42f66462013-10-31 17:38:22 -0700119
Yingdi Yu04842232013-10-23 14:03:09 -0700120ChatDialog::~ChatDialog()
121{
Yingdi Yu42372442013-11-06 18:43:31 -0800122 if(m_sock != NULL)
123 {
124 sendLeave();
125 delete m_sock;
126 m_sock = NULL;
127 }
Yingdi Yua0594092013-11-06 22:07:38 -0800128 m_handler->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700129}
130
131void
Yingdi Yu42372442013-11-06 18:43:31 -0800132ChatDialog::setWrapper(bool trial)
Yingdi Yu04842232013-10-23 14:03:09 -0700133{
Yingdi Yu42f66462013-10-31 17:38:22 -0700134 m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700135
Yingdi Yu42f66462013-10-31 17:38:22 -0700136 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
137 m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700138
Yingdi Yu42f66462013-10-31 17:38:22 -0700139 m_keychain = ndn::Ptr<ndn::security::Keychain>(new ndn::security::Keychain(m_identityManager, m_invitationPolicyManager, NULL));
140
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800141 ndn::Ptr<ndn::security::Keychain> noVerifyKeychain = ndn::Ptr<ndn::security::Keychain>(new ndn::security::Keychain(m_identityManager,
142ndn::Ptr<ndn::security::NoVerifyPolicyManager>::Create(), NULL));
Yingdi Yub29f78c2013-11-09 20:12:31 -0800143 try{
144 m_handler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(m_keychain));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800145 m_localPrefixHandler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(noVerifyKeychain));
Yingdi Yub29f78c2013-11-09 20:12:31 -0800146 }catch(ndn::Error::ndnOperation& e){
147 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
148 }
Yingdi Yu04842232013-10-23 14:03:09 -0700149}
150
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700151void
Yingdi Yu42f66462013-10-31 17:38:22 -0700152ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700153{
Yingdi Yu42372442013-11-06 18:43:31 -0800154 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu42f66462013-10-31 17:38:22 -0700155 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toUri()));
156 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
157 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
158 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
159}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700160
Yingdi Yu42f66462013-10-31 17:38:22 -0700161void
162ChatDialog::updateLabels()
163{
164 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
165 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
166 ui->infoLabel->setText(settingDisp);
167 QString prefixDisp;
168 if (m_user.getPrefix().startsWith("/private/local"))
169 {
170 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
171 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
172 }
173 else
174 {
175 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
176 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
177 }
178 ui->prefixLabel->setText(prefixDisp);
179}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700180
Yingdi Yu42f66462013-10-31 17:38:22 -0700181void
182ChatDialog::sendInvitation(ndn::Ptr<ContactItem> contact, bool isIntroducer)
183{
184 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
185
186 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
187
188 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700189 interestName.append(contact->getNameSpace());
190 interestName.append("chatroom");
191 interestName.append(m_chatroomPrefix.get(-1));
192 interestName.append("inviter-prefix");
193 interestName.append(m_localPrefix);
194 interestName.append("inviter");
195 interestName.append(certificateName);
196
197 string signedUri = interestName.toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700198 ndn::Blob signedBlob(signedUri.c_str(), signedUri.size());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700199
Yingdi Yu42f66462013-10-31 17:38:22 -0700200 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = ndn::DynamicCast<const ndn::signature::Sha256WithRsa>(m_identityManager->signByCertificate(signedBlob, certificateName));
201 const ndn::Blob& sigBits = sha256sig->getSignatureBits();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700202
203 interestName.append(sigBits.buf(), sigBits.size());
Yingdi Yu42372442013-11-06 18:43:31 -0800204 interestName.appendVersion();
205
Yingdi Yu42f66462013-10-31 17:38:22 -0700206 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(interestName));
207 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onInviteReplyVerified,
208 this,
209 _1,
210 contact->getNameSpace(),
211 isIntroducer),
212 boost::bind(&ChatDialog::onInviteTimeout,
213 this,
214 _1,
215 _2,
216 contact->getNameSpace(),
217 7),
218 boost::bind(&ChatDialog::onUnverified,
219 this,
220 _1)));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700221
222 m_handler->sendInterest(interest, closure);
223}
224
225void
Yingdi Yu42f66462013-10-31 17:38:22 -0700226ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
227{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
228
229void
230ChatDialog::addChatDataRule(const ndn::Name& prefix,
231 const ndn::security::IdentityCertificate& identityCertificate,
232 bool isIntroducer)
233{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
234
235void
Yingdi Yua0594092013-11-06 22:07:38 -0800236ChatDialog::publishIntroCert(const ndn::security::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700237{
Yingdi Yu42f66462013-10-31 17:38:22 -0700238 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
Yingdi Yua0594092013-11-06 22:07:38 -0800239 dskCertificate.getPublicKeyName(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700240 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yua0594092013-11-06 22:07:38 -0800241 dskCertificate.getNotBefore(),
242 dskCertificate.getNotAfter(),
243 dskCertificate.getPublicKeyInfo(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700244 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
245 ndn::Name certName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800246 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yu42f66462013-10-31 17:38:22 -0700247 m_identityManager->signByCertificate(syncIntroCertificate, certName);
248 m_handler->putToNdnd(*syncIntroCertificate.encodeToWire());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700249}
250
251void
Yingdi Yu42f66462013-10-31 17:38:22 -0700252ChatDialog::invitationRejected(const ndn::Name& identity)
253{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800254 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800255 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
256 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700257}
258
259void
260ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::Ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700261{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800262 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700263 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = boost::dynamic_pointer_cast<const ndn::signature::Sha256WithRsa> (data->getSignature());
264 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
265 ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yuc90deb12013-11-06 18:51:19 -0800266 m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800267 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700268}
269
270void
Yingdi Yu42f66462013-10-31 17:38:22 -0700271ChatDialog::onInviteReplyVerified(ndn::Ptr<ndn::Data> data, const ndn::Name& identity, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700272{
273 string content(data->content().buf(), data->content().size());
Yingdi Yu97936352013-11-08 14:13:42 -0800274 if(content == string("nack"))
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700275 invitationRejected(identity);
276 else
Yingdi Yu42f66462013-10-31 17:38:22 -0700277 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700278}
279
280void
Yingdi Yu42f66462013-10-31 17:38:22 -0700281ChatDialog::onInviteTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest, const ndn::Name& identity, int retry)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700282{
283 if(retry > 0)
284 {
Yingdi Yu42f66462013-10-31 17:38:22 -0700285 ndn::Ptr<ndn::Closure> newClosure = ndn::Ptr<ndn::Closure>(new ndn::Closure(closure->m_dataCallback,
286 boost::bind(&ChatDialog::onInviteTimeout,
287 this,
288 _1,
289 _2,
290 identity,
291 retry - 1),
292 closure->m_unverifiedCallback,
293 closure->m_stepCount)
294 );
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700295 m_handler->sendInterest(interest, newClosure);
296 }
297 else
298 invitationRejected(identity);
299}
300
301void
Yingdi Yu42f66462013-10-31 17:38:22 -0700302ChatDialog::onUnverified(ndn::Ptr<ndn::Data> data)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700303{}
304
Yingdi Yu42f66462013-10-31 17:38:22 -0700305void
Yingdi Yu42372442013-11-06 18:43:31 -0800306ChatDialog::onTimeout(ndn::Ptr<ndn::Closure> closure,
307 ndn::Ptr<ndn::Interest> interest)
308{}
309
310void
Yingdi Yu42f66462013-10-31 17:38:22 -0700311ChatDialog::initializeSync()
312{
313
314 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
315 m_syncPolicyManager,
316 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
317 bind(&ChatDialog::processRemoveWrapper, this, _1));
318
319 usleep(100000);
320
321 QTimer::singleShot(600, this, SLOT(sendJoin()));
322 m_timer->start(FRESHNESS * 1000);
323 disableTreeDisplay();
324 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
325 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
326 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700327}
328
329void
330ChatDialog::returnPressed()
331{
332 QString text = ui->lineEdit->text();
333 if (text.isEmpty())
334 return;
335
336 ui->lineEdit->clear();
337
338 if (text.startsWith("boruoboluomi"))
339 {
340 summonReaper ();
341 // reapButton->show();
342 fitView();
343 return;
344 }
345
346 if (text.startsWith("minimanihong"))
347 {
348 // reapButton->hide();
349 fitView();
350 return;
351 }
352
353 SyncDemo::ChatMessage msg;
354 formChatMessage(text, msg);
355
356 appendMessage(msg);
357
358 sendMsg(msg);
359
360 fitView();
361}
362
363void
364ChatDialog::treeButtonPressed()
365{
366 if (ui->treeViewer->isVisible())
367 {
368 ui->treeViewer->hide();
369 ui->treeButton->setText("Show ChronoSync Tree");
370 }
371 else
372 {
373 ui->treeViewer->show();
374 ui->treeButton->setText("Hide ChronoSync Tree");
375 }
376
377 fitView();
378}
379
380void ChatDialog::disableTreeDisplay()
381{
382 ui->treeButton->setEnabled(false);
383 ui->treeViewer->hide();
384 fitView();
385}
386
387void ChatDialog::enableTreeDisplay()
388{
389 ui->treeButton->setEnabled(true);
390 // treeViewer->show();
391 // fitView();
392}
393
394void
395ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
396{
397 emit treeUpdated(v);
398 _LOG_DEBUG("<<< Tree update signal emitted");
399}
400
401void
402ChatDialog::processRemoveWrapper(std::string prefix)
403{
404 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
405}
406
407void
408ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
409{
410 _LOG_DEBUG("<<< processing Tree Update");
411
412 if (v.empty())
413 {
414 return;
415 }
416
417 // reflect the changes on digest tree
418 {
419 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
420 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
421 }
422
423 int n = v.size();
424 int totalMissingPackets = 0;
425 for (int i = 0; i < n; i++)
426 {
427 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
428 }
429
430 for (int i = 0; i < n; i++)
431 {
432 if (totalMissingPackets < 4)
433 {
434 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
435 {
436 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
437 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
438 }
439 }
440 else
441 {
442 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
443 }
444 }
445
446 // adjust the view
447 fitView();
448
449}
450
451void
452ChatDialog::processDataWrapper(ndn::Ptr<ndn::Data> data)
453{
454 string name = data->getName().toUri();
455 const char* buf = data->content().buf();
456 size_t len = data->content().size();
457
458 char *tempBuf = new char[len];
459 memcpy(tempBuf, buf, len);
460 emit dataReceived(name.c_str(), tempBuf, len, true, false);
461 _LOG_DEBUG("<<< " << name << " fetched");
462}
463
464void
465ChatDialog::processDataNoShowWrapper(ndn::Ptr<ndn::Data> data)
466{
467 string name = data->getName().toUri();
468 const char* buf = data->content().buf();
469 size_t len = data->content().size();
470
471 char *tempBuf = new char[len];
472 memcpy(tempBuf, buf, len);
473 emit dataReceived(name.c_str(), tempBuf, len, false, false);
474
475 // if (!m_historyInitialized)
476 // {
477 // fetchHistory(name);
478 // m_historyInitialized = true;
479 // }
480}
481
482// void
483// ChatDialog::fetchHistory(std::string name)
484// {
485
486// /****************************/
487// /* TODO: fix following part */
488// /****************************/
489// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
490// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
491// prefix += "/history";
492// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
493// // QString randomString = getRandomString();
494// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
495// // {
496// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
497// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
498// // }
499// }
500
501void
502ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
503{
504 SyncDemo::ChatMessage msg;
505 bool corrupted = false;
506 if (!msg.ParseFromArray(buf, len))
507 {
508 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
509 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
510 msg.set_from("inconnu");
511 msg.set_type(SyncDemo::ChatMessage::OTHER);
512 corrupted = true;
513 }
514
515 delete [] buf;
516 buf = NULL;
517
518 // display msg received from network
519 // we have to do so; this function is called by ccnd thread
520 // so if we call appendMsg directly
521 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
522 // the "cannonical" way to is use signal-slot
523 if (show && !corrupted)
524 {
525 appendMessage(msg, isHistory);
526 }
527
528 if (!isHistory)
529 {
530 // update the tree view
531 std::string stdStrName = name.toStdString();
532 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
533 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
534 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
535 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
536 {
537 processRemove(prefix.c_str());
538 }
539 else
540 {
541 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
542 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
543 }
544 }
545 fitView();
546}
547
548void
549ChatDialog::processRemove(QString prefix)
550{
551 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
552
553 bool removed = m_scene->removeNode(prefix);
554 if (removed)
555 {
556 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
557 m_scene->plot(m_sock->getRootDigest().c_str());
558 }
559}
560
561void
562ChatDialog::sendJoin()
563{
564 m_joined = true;
565 SyncDemo::ChatMessage msg;
566 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
567 sendMsg(msg);
568 boost::random::random_device rng;
569 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
570 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
571 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
572}
573
574void
575ChatDialog::sendHello()
576{
577 time_t now = time(NULL);
578 int elapsed = now - m_lastMsgTime;
579 if (elapsed >= m_randomizedInterval / 1000)
580 {
581 SyncDemo::ChatMessage msg;
582 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
583 sendMsg(msg);
584 boost::random::random_device rng;
585 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
586 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
587 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
588 }
589 else
590 {
591 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
592 }
593}
594
595void
596ChatDialog::sendLeave()
597{
598 SyncDemo::ChatMessage msg;
599 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
600 sendMsg(msg);
601 usleep(500000);
602 m_sock->remove(m_user.getPrefix().toStdString());
603 usleep(5000);
604 m_joined = false;
605 _LOG_DEBUG("Sync REMOVE signal sent");
606}
607
608void
609ChatDialog::replot()
610{
611 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
612 m_scene->plot(m_sock->getRootDigest().c_str());
613 fitView();
614}
615
616void
617ChatDialog::summonReaper()
618{
619 Sync::SyncLogic &logic = m_sock->getLogic ();
620 map<string, bool> branches = logic.getBranchPrefixes();
621 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
622
623 m_zombieList.clear();
624
625 QMapIterator<QString, DisplayUserPtr> it(roster);
626 map<string, bool>::iterator mapIt;
627 while(it.hasNext())
628 {
629 it.next();
630 DisplayUserPtr p = it.value();
631 if (p != DisplayUserNullPtr)
632 {
633 mapIt = branches.find(p->getPrefix().toStdString());
634 if (mapIt != branches.end())
635 {
636 mapIt->second = true;
637 }
638 }
639 }
640
641 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
642 {
643 // this is zombie. all active users should have been marked true
644 if (! mapIt->second)
645 {
646 m_zombieList.append(mapIt->first.c_str());
647 }
648 }
649
650 m_zombieIndex = 0;
651
652 // start reaping
653 reap();
654}
655
656void
657ChatDialog::reap()
658{
659 if (m_zombieIndex < m_zombieList.size())
660 {
661 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
662 m_sock->remove(prefix);
663 _LOG_DEBUG("Reaped: prefix = " << prefix);
664 m_zombieIndex++;
665 // reap again in 10 seconds
666 QTimer::singleShot(10000, this, SLOT(reap()));
667 }
668}
669
670void
671ChatDialog::updateRosterList(QStringList staleUserList)
672{
673 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
674 QStringList rosterList = m_scene->getRosterList();
675 m_rosterModel->setStringList(rosterList);
676 QString user;
677 QStringListIterator it(staleUserList);
678 while(it.hasNext())
679 {
680 std::string nick = it.next().toStdString();
681 if (nick.empty())
682 continue;
683
684 SyncDemo::ChatMessage msg;
685 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
686 msg.set_from(nick);
687 appendMessage(msg);
688 }
689}
690
691void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800692ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
693{
Yingdi Yu9f657242013-11-10 12:25:43 -0800694 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800695 QString randString = getRandomString();
696 bool needWrite = false;
697 bool needFresh = false;
698
699 QString oldPrefix = m_user.getPrefix();
700 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
701 m_user.setOriginPrefix(originPrefix);
702
703 m_localPrefix = ndn::Name(originPrefix.toStdString());
704 m_localChatPrefix = m_localPrefix;
705 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
706 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
707 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
708 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
709 needWrite = true;
710 needFresh = true;
711 }
712
713 if (needWrite) {
714 updateLabels();
715 }
716
717 if (needFresh && m_sock != NULL)
718 {
719
720 {
721 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
722 m_scene->clearAll();
723 m_scene->plot("Empty");
724 }
725
Yingdi Yu0a953c32013-11-10 10:32:18 -0800726 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800727
728 // keep the new prefix
729 QString newPrefix = m_user.getPrefix();
730 // send leave for the old
731 m_user.setPrefix(oldPrefix);
732 // there is no point to send leave if we haven't joined yet
733 if (m_joined)
734 {
735 sendLeave();
736 }
737 // resume new prefix
738 m_user.setPrefix(newPrefix);
739 // Sync::CcnxWrapperPtr handle = Sync::CcnxWrapper::Create();
740 // handle->clearInterestFilter(oldPrefix.toStdString());
741 delete m_sock;
742 m_sock = NULL;
743
744 try
745 {
746 usleep(100000);
747 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
748 m_syncPolicyManager,
749 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
750 bind(&ChatDialog::processRemoveWrapper, this, _1));
751 usleep(100000);
752 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
753 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu0a953c32013-11-10 10:32:18 -0800754 QTimer::singleShot(600, this, SLOT(sendJoin()));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800755 m_timer->start(FRESHNESS * 1000);
756 disableTreeDisplay();
Yingdi Yu0a953c32013-11-10 10:32:18 -0800757 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800758 }catch(ndn::Error::ndnOperation& e){
759 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
760 }
761 }
762 else if (needFresh && m_sock == NULL)
763 {
764 initializeSync();
765 }
766 else if (m_sock == NULL)
767 {
768 initializeSync();
769 }
770 else
771 {
772// #ifdef __DEBUG
773// std::cout << "Just changing nicks, we're good. " << std::endl;
774// #endif
775 }
776
777 fitView();
778}
779
780void
Yingdi Yua0594092013-11-06 22:07:38 -0800781ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
782{
783 switch (reason)
784 {
785 case QSystemTrayIcon::Trigger:
786 case QSystemTrayIcon::DoubleClick:
787 break;
788 case QSystemTrayIcon::MiddleClick:
789 // showMessage();
790 break;
791 default:;
792 }
793}
794
795
796void
797ChatDialog::messageClicked()
798{
799 this->showMaximized();
800}
801
802
803void
804ChatDialog::createActions()
805{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800806 minimizeAction = new QAction(tr("Mi&nimize"), this);
807 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800808
Yingdi Yu07b5b092013-11-07 17:00:54 -0800809 maximizeAction = new QAction(tr("Ma&ximize"), this);
810 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800811
Yingdi Yu07b5b092013-11-07 17:00:54 -0800812 restoreAction = new QAction(tr("&Restore"), this);
813 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800814
815 // settingsAction = new QAction(tr("Settings"), this);
816 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
817
818 // settingsAction->setMenuRole (QAction::PreferencesRole);
819
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800820 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
821 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800822
Yingdi Yu07b5b092013-11-07 17:00:54 -0800823 quitAction = new QAction(tr("Quit"), this);
824 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800825}
826
827void
828ChatDialog::createTrayIcon()
829{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800830 trayIconMenu = new QMenu(this);
831 trayIconMenu->addAction(minimizeAction);
832 trayIconMenu->addAction(maximizeAction);
833 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800834 // trayIconMenu->addSeparator();
835 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800836 trayIconMenu->addSeparator();
837 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800838 trayIconMenu->addSeparator();
839 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800840
841 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800842 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800843
844 QIcon icon(":/images/icon_small.png");
845 trayIcon->setIcon(icon);
846 setWindowIcon(icon);
847 trayIcon->setToolTip("ChronoChat System Tray Icon");
848 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800849}
850
851
852void
Yingdi Yu42f66462013-10-31 17:38:22 -0700853ChatDialog::resizeEvent(QResizeEvent *e)
854{
855 fitView();
856}
857
858void
859ChatDialog::showEvent(QShowEvent *e)
860{
861 fitView();
862}
863
864void
865ChatDialog::fitView()
866{
867 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
868 QRectF rect = m_scene->itemsBoundingRect();
869 m_scene->setSceneRect(rect);
870 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
871}
872
873void
874ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
875 msg.set_from(m_user.getNick().toStdString());
876 msg.set_to(m_user.getChatroom().toStdString());
877 msg.set_data(text.toUtf8().constData());
878 time_t seconds = time(NULL);
879 msg.set_timestamp(seconds);
880 msg.set_type(SyncDemo::ChatMessage::CHAT);
881}
882
883void
884ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
885{
886 msg.set_from(m_user.getNick().toStdString());
887 msg.set_to(m_user.getChatroom().toStdString());
888 time_t seconds = time(NULL);
889 msg.set_timestamp(seconds);
890 msg.set_type(type);
891}
892
893void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800894ChatDialog::updateLocalPrefix()
895{
Yingdi Yu0a953c32013-11-10 10:32:18 -0800896 m_newLocalPrefixReady = false;
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800897 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(ndn::Name("/local/ndn/prefix")));
898 interest->setChildSelector(ndn::Interest::CHILD_RIGHT);
Yingdi Yu0a953c32013-11-10 10:32:18 -0800899 interest->setInterestLifetime(1);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800900
Yingdi Yu0a953c32013-11-10 10:32:18 -0800901 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onLocalPrefix,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800902 this,
903 _1),
Yingdi Yu0a953c32013-11-10 10:32:18 -0800904 boost::bind(&ChatDialog::onLocalPrefixTimeout,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800905 this,
906 _1,
907 _2),
Yingdi Yu0a953c32013-11-10 10:32:18 -0800908 boost::bind(&ChatDialog::onLocalPrefix,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800909 this,
910 _1)));
911
912 m_localPrefixHandler->sendInterest(interest, closure);
Yingdi Yu0a953c32013-11-10 10:32:18 -0800913 while(m_newLocalPrefixReady == false)
914 {
915#if BOOST_VERSION >= 1050000
916 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
917#else
918 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
919#endif
920 }
Yingdi Yu9f657242013-11-10 12:25:43 -0800921 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -0800922 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -0800923 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
924
925 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
926 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800927}
928
929static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
930
931QString
932ChatDialog::getRandomString()
933{
934 std::string randStr;
935 boost::random::random_device rng;
936 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
937 for (int i = 0; i < 10; i ++)
938 {
939 randStr += chars2[index_dist(rng)];
940 }
941 return randStr.c_str();
942}
943
944void
Yingdi Yu0a953c32013-11-10 10:32:18 -0800945ChatDialog::onLocalPrefix(ndn::Ptr<ndn::Data> data)
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800946{
947 string dataString(data->content().buf(), data->content().size());
948 QString originPrefix = QString::fromStdString (dataString).trimmed ();
Yingdi Yu0a953c32013-11-10 10:32:18 -0800949 string trimmedString = originPrefix.toStdString();
950 m_newLocalPrefix = ndn::Name(trimmedString);
951 m_newLocalPrefixReady = true;
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800952}
953
954void
Yingdi Yu0a953c32013-11-10 10:32:18 -0800955ChatDialog::onLocalPrefixTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest)
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800956{
Yingdi Yu0a953c32013-11-10 10:32:18 -0800957 m_newLocalPrefix = m_localPrefix;
958 m_newLocalPrefixReady = true;
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800959}
960
961void
Yingdi Yua0594092013-11-06 22:07:38 -0800962ChatDialog::changeEvent(QEvent *e)
963{
964 switch(e->type())
965 {
966 case QEvent::ActivationChange:
967 if (isActiveWindow())
968 {
969 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
970 }
971 break;
972 default:
973 break;
974 }
975}
976
977void
Yingdi Yu42f66462013-10-31 17:38:22 -0700978ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
979{
980 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
981
982 if (msg.type() == SyncDemo::ChatMessage::CHAT)
983 {
984
985 if (!msg.has_data())
986 {
987 return;
988 }
989
990 if (msg.from().empty() || msg.data().empty())
991 {
992 return;
993 }
994
995 if (!msg.has_timestamp())
996 {
997 return;
998 }
999
1000 // if (m_history.size() == MAX_HISTORY_ENTRY)
1001 // {
1002 // m_history.dequeue();
1003 // }
1004
1005 // m_history.enqueue(msg);
1006
1007 QTextCharFormat nickFormat;
1008 nickFormat.setForeground(Qt::darkGreen);
1009 nickFormat.setFontWeight(QFont::Bold);
1010 nickFormat.setFontUnderline(true);
1011 nickFormat.setUnderlineColor(Qt::gray);
1012
1013 QTextCursor cursor(ui->textEdit->textCursor());
1014 cursor.movePosition(QTextCursor::End);
1015 QTextTableFormat tableFormat;
1016 tableFormat.setBorder(0);
1017 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1018 QString from = QString("%1 ").arg(msg.from().c_str());
1019 QTextTableCell fromCell = table->cellAt(0, 0);
1020 fromCell.setFormat(nickFormat);
1021 fromCell.firstCursorPosition().insertText(from);
1022
1023 time_t timestamp = msg.timestamp();
1024 printTimeInCell(table, timestamp);
1025
1026 QTextCursor nextCursor(ui->textEdit->textCursor());
1027 nextCursor.movePosition(QTextCursor::End);
1028 table = nextCursor.insertTable(1, 1, tableFormat);
1029 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
1030 if (!isHistory)
1031 {
1032 showMessage(from, QString::fromUtf8(msg.data().c_str()));
1033 }
1034 }
1035
1036 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
1037 {
1038 QTextCharFormat nickFormat;
1039 nickFormat.setForeground(Qt::gray);
1040 nickFormat.setFontWeight(QFont::Bold);
1041 nickFormat.setFontUnderline(true);
1042 nickFormat.setUnderlineColor(Qt::gray);
1043
1044 QTextCursor cursor(ui->textEdit->textCursor());
1045 cursor.movePosition(QTextCursor::End);
1046 QTextTableFormat tableFormat;
1047 tableFormat.setBorder(0);
1048 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1049 QString action;
1050 if (msg.type() == SyncDemo::ChatMessage::JOIN)
1051 {
1052 action = "enters room";
1053 }
1054 else
1055 {
1056 action = "leaves room";
1057 }
1058
1059 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1060 QTextTableCell fromCell = table->cellAt(0, 0);
1061 fromCell.setFormat(nickFormat);
1062 fromCell.firstCursorPosition().insertText(from);
1063
1064 time_t timestamp = msg.timestamp();
1065 printTimeInCell(table, timestamp);
1066 }
1067
1068 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1069 bar->setValue(bar->maximum());
1070}
1071
1072QString
1073ChatDialog::formatTime(time_t timestamp)
1074{
1075 struct tm *tm_time = localtime(&timestamp);
1076 int hour = tm_time->tm_hour;
1077 QString amOrPM;
1078 if (hour > 12)
1079 {
1080 hour -= 12;
1081 amOrPM = "PM";
1082 }
1083 else
1084 {
1085 amOrPM = "AM";
1086 if (hour == 0)
1087 {
1088 hour = 12;
1089 }
1090 }
1091
1092 char textTime[12];
1093 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1094 return QString(textTime);
1095}
1096
1097void
1098ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1099{
1100 QTextCharFormat timeFormat;
1101 timeFormat.setForeground(Qt::gray);
1102 timeFormat.setFontUnderline(true);
1103 timeFormat.setUnderlineColor(Qt::gray);
1104 QTextTableCell timeCell = table->cellAt(0, 1);
1105 timeCell.setFormat(timeFormat);
1106 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1107}
1108
1109void
1110ChatDialog::showMessage(QString from, QString data)
1111{
1112 if (!isActiveWindow())
1113 {
Yingdi Yua0594092013-11-06 22:07:38 -08001114 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1115 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001116 }
1117}
1118
1119void
1120ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1121{
1122 // send msg
1123 size_t size = msg.ByteSize();
1124 char *buf = new char[size];
1125 msg.SerializeToArray(buf, size);
1126 if (!msg.IsInitialized())
1127 {
1128 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1129 abort();
1130 }
1131 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1132
1133 delete buf;
1134
1135 m_lastMsgTime = time(NULL);
1136
1137 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
1138 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1139 std::vector<Sync::MissingDataInfo> v;
1140 v.push_back(mdi);
1141 {
1142 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1143 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1144 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1145 }
1146}
1147
1148void
1149ChatDialog::openInviteListDialog()
1150{
1151 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1152 m_inviteListDialog->show();
1153}
1154
1155void
1156ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1157{
1158 ndn::Name inviteeNamespace(invitee.toUtf8().constData());
1159 ndn::Ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1160 sendInvitation(inviteeItem, isIntroducer);
1161}
1162
Yingdi Yu42372442013-11-06 18:43:31 -08001163void
1164ChatDialog::closeEvent(QCloseEvent *e)
1165{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001166 if (trayIcon->isVisible())
1167 {
1168 QMessageBox::information(this, tr("Chronos"),
1169 tr("The program will keep running in the "
1170 "system tray. To terminate the program"
1171 "choose <b>Quit</b> in the context memu"
1172 "of the system tray entry."));
1173 hide();
1174 e->ignore();
1175 }
1176}
1177
1178void
1179ChatDialog::quit()
1180{
Yingdi Yu42372442013-11-06 18:43:31 -08001181 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001182 emit closeChatDialog(m_chatroomPrefix);
1183}
1184
1185
1186
Yingdi Yu42f66462013-10-31 17:38:22 -07001187
Yingdi Yu04842232013-10-23 14:03:09 -07001188#if WAF
1189#include "chatdialog.moc"
1190#include "chatdialog.cpp.moc"
1191#endif