blob: a68c63f444939792b93bf9eaad20e555e74a6bee [file] [log] [blame]
Yingdi Yu7989eb22013-10-31 17:38:22 -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 *
8 * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
9 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
10 */
11
Yingdi Yu0b0a7362014-08-05 16:31:30 -070012#include "digest-tree-scene.hpp"
Alexander Afanasyevf829f4d2013-05-07 15:59:36 -070013
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070014#include <QtGui>
Alexander Afanasyevf829f4d2013-05-07 15:59:36 -070015
16#ifndef Q_MOC_RUN
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070017#include <vector>
18#include <iostream>
19#include <assert.h>
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070020#include <boost/lexical_cast.hpp>
Zhenkai Zhu21d75f92012-06-04 21:23:34 -070021#include <memory>
Alexander Afanasyevf829f4d2013-05-07 15:59:36 -070022#endif
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070023
Yingdi Yueb692ac2015-02-10 18:46:18 -080024namespace chronochat {
Yingdi Yu0b0a7362014-08-05 16:31:30 -070025
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070026static const double Pi = 3.14159265358979323846264338327950288419717;
Yingdi Yud45777b2014-10-16 23:54:11 -070027static const int NODE_SIZE = 40;
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070028
Zhenkai Zhu7f52e1b2012-10-09 11:45:36 -070029//DisplayUserPtr DisplayUserNullPtr;
Zhenkai Zhud13acd02012-06-04 15:25:20 -070030
Yingdi Yu348f5ea2014-03-01 14:47:25 -080031DigestTreeScene::DigestTreeScene(QWidget *parent)
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -070032 : QGraphicsScene(parent)
33{
Yingdi Yud45777b2014-10-16 23:54:11 -070034 m_previouslyUpdatedUser = DisplayUserNullPtr;
Zhenkai Zhu6fcdee42012-05-30 17:02:49 -070035}
36
37void
Yingdi Yueb692ac2015-02-10 18:46:18 -080038DigestTreeScene::processSyncUpdate(const std::vector<chronochat::NodeInfo>& nodeInfos,
Yingdi Yud45777b2014-10-16 23:54:11 -070039 const QString& digest)
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070040{
Yingdi Yud45777b2014-10-16 23:54:11 -070041 m_rootDigest = digest;
Yingdi Yu5a5ff202014-03-17 10:03:02 -070042
Yingdi Yud45777b2014-10-16 23:54:11 -070043 bool rePlot = false;
44
45 // Update roster info
Yingdi Yu1cc45d92015-02-09 14:19:54 -080046 for (size_t i = 0; i < nodeInfos.size(); i++) {
Yingdi Yud45777b2014-10-16 23:54:11 -070047 Roster_iterator it = m_roster.find(nodeInfos[i].sessionPrefix);
Yingdi Yu0b0a7362014-08-05 16:31:30 -070048 if (it == m_roster.end()) {
Yingdi Yufa0b6a02014-04-30 14:26:42 -070049 rePlot = true;
Yingdi Yud45777b2014-10-16 23:54:11 -070050
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070051 DisplayUserPtr p(new DisplayUser());
Yingdi Yud45777b2014-10-16 23:54:11 -070052 p->setPrefix(nodeInfos[i].sessionPrefix);
53 p->setSeq(nodeInfos[i].seqNo);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070054 m_roster.insert(p->getPrefix(), p);
55 }
Yingdi Yu0b0a7362014-08-05 16:31:30 -070056 else {
Yingdi Yud45777b2014-10-16 23:54:11 -070057 it.value()->setSeq(nodeInfos[i].seqNo);
Zhenkai Zhud1c5a972012-06-05 14:07:41 -070058 }
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070059 }
60
Yingdi Yud45777b2014-10-16 23:54:11 -070061 if (rePlot)
62 // If new nodes exist, we need to re-arrange node
63 plot(m_rootDigest);
Yingdi Yu0b0a7362014-08-05 16:31:30 -070064 else {
Yingdi Yud45777b2014-10-16 23:54:11 -070065 // No new node, update seqNo & digest
Yingdi Yu1cc45d92015-02-09 14:19:54 -080066 for (size_t i = 0; i < nodeInfos.size(); i++) {
Yingdi Yud45777b2014-10-16 23:54:11 -070067 Roster_iterator it = m_roster.find(nodeInfos[i].sessionPrefix);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070068 if (it != m_roster.end()) {
69 DisplayUserPtr p = it.value();
70 QGraphicsTextItem *item = p->getSeqTextItem();
Zhenkai Zhud13acd02012-06-04 15:25:20 -070071 QGraphicsRectItem *rectItem = p->getInnerRectItem();
Yingdi Yud45777b2014-10-16 23:54:11 -070072 std::string s = boost::lexical_cast<std::string>(p->getSeqNo());
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070073 item->setPlainText(s.c_str());
Zhenkai Zhud13acd02012-06-04 15:25:20 -070074 QRectF textBR = item->boundingRect();
75 QRectF rectBR = rectItem->boundingRect();
Yingdi Yu0b0a7362014-08-05 16:31:30 -070076 item->setPos(rectBR.x() + (rectBR.width() - textBR.width())/2,
77 rectBR.y() + (rectBR.height() - textBR.height())/2);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070078 }
79 }
Yingdi Yud45777b2014-10-16 23:54:11 -070080 m_displayRootDigest->setPlainText(digest);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -070081 }
82}
83
84void
Yingdi Yud45777b2014-10-16 23:54:11 -070085DigestTreeScene::updateNick(QString sessionPrefix, QString nick)
Zhenkai Zhu86df7412012-09-27 16:30:20 -070086{
Yingdi Yud45777b2014-10-16 23:54:11 -070087 Roster_iterator it = m_roster.find(sessionPrefix);
88 if (it != m_roster.end()) {
89 DisplayUserPtr p = it.value();
90 if (nick != p->getNick()) {
91 p->setNick(nick);
92 QGraphicsTextItem *nickItem = p->getNickTextItem();
93 QGraphicsRectItem *nickRectItem = p->getNickRectItem();
94 nickItem->setPlainText(p->getNick());
95 QRectF rectBR = nickRectItem->boundingRect();
96 QRectF nickBR = nickItem->boundingRect();
97 nickItem->setPos(rectBR.x() + (rectBR.width() - nickBR.width())/2, rectBR.y() + 5);
98 }
99 }
100}
101
102void
103DigestTreeScene::messageReceived(QString sessionPrefix)
104{
105 Roster_iterator it = m_roster.find(sessionPrefix);
106 if (it != m_roster.end()) {
107 DisplayUserPtr p = it.value();
108
109 reDrawNode(p, Qt::red);
110
111 if (m_previouslyUpdatedUser != DisplayUserNullPtr && m_previouslyUpdatedUser != p) {
112 reDrawNode(m_previouslyUpdatedUser, Qt::darkBlue);
113 }
114
115 m_previouslyUpdatedUser = p;
116 }
117}
118
119void
120DigestTreeScene::clearAll()
121{
122 clear();
123 m_roster.clear();
124}
125
126void
127DigestTreeScene::removeNode(const QString sessionPrefix)
128{
129 m_roster.remove(sessionPrefix);
130 plot(m_rootDigest);
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700131}
132
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700133QStringList
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700134DigestTreeScene::getRosterList()
135{
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700136 QStringList rosterList;
137 RosterIterator it(m_roster);
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700138 while (it.hasNext()) {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700139 it.next();
140 DisplayUserPtr p = it.value();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700141 if (p != DisplayUserNullPtr) {
Zhenkai Zhu9036e032012-09-27 20:59:33 -0700142 rosterList << "- " + p->getNick();
143 }
144 }
145 return rosterList;
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700146}
147
Yingdi Yud45777b2014-10-16 23:54:11 -0700148QStringList
149DigestTreeScene::getRosterPrefixList()
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700150{
Yingdi Yud45777b2014-10-16 23:54:11 -0700151 QStringList prefixList;
152 RosterIterator it(m_roster);
153 while (it.hasNext()) {
154 it.next();
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700155 DisplayUserPtr p = it.value();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700156 if (p != DisplayUserNullPtr) {
Yingdi Yud45777b2014-10-16 23:54:11 -0700157 prefixList << "- " + p->getPrefix();
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700158 }
159 }
Yingdi Yud45777b2014-10-16 23:54:11 -0700160 return prefixList;
161}
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700162
Yingdi Yud45777b2014-10-16 23:54:11 -0700163void
164DigestTreeScene::plot(QString rootDigest)
165{
166 clear();
Zhenkai Zhu6082ede2012-09-27 17:28:46 -0700167
Yingdi Yud45777b2014-10-16 23:54:11 -0700168 shared_ptr<TreeLayout> layout(new OneLevelTreeLayout());
169 layout->setSiblingDistance(100);
170 layout->setLevelDistance(100);
Zhenkai Zhu716fe852012-10-08 18:27:55 -0700171
Yingdi Yud45777b2014-10-16 23:54:11 -0700172 std::vector<TreeLayout::Coordinate> childNodesCo(m_roster.size());
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700173 layout->setOneLevelLayout(childNodesCo);
Yingdi Yud45777b2014-10-16 23:54:11 -0700174 plotEdge(childNodesCo, NODE_SIZE);
175 plotNode(childNodesCo, rootDigest, NODE_SIZE);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700176
Yingdi Yud45777b2014-10-16 23:54:11 -0700177 m_previouslyUpdatedUser = DisplayUserNullPtr;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700178}
179
180void
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700181DigestTreeScene::plotEdge(const std::vector<TreeLayout::Coordinate> &childNodesCo, int nodeSize)
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700182{
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700183 int n = childNodesCo.size();
184 for (int i = 0; i < n; i++) {
185 double x1 = 0.0, y1 = 0.0;
186 double x2 = childNodesCo[i].x, y2 = childNodesCo[i].y;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700187 QPointF src(x1 + nodeSize/2, y1 + nodeSize/2);
188 QPointF dest(x2 + nodeSize/2, y2 + nodeSize/2);
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700189 QLineF line(src, dest);
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700190 double angle = ::acos(line.dx() / line.length());
191
192 double arrowSize = 10;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700193 QPointF sourceArrowP0 = src + QPointF((nodeSize/2 + 10) * line.dx() / line.length(),
194 (nodeSize/2 +10) * line.dy() / line.length());
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700195 QPointF sourceArrowP1 = sourceArrowP0 + QPointF(cos(angle + Pi / 3 - Pi/2) * arrowSize,
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700196 sin(angle + Pi / 3 - Pi/2) * arrowSize);
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700197 QPointF sourceArrowP2 = sourceArrowP0 + QPointF(cos(angle + Pi - Pi / 3 - Pi/2) * arrowSize,
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700198 sin(angle + Pi - Pi / 3 - Pi/2) * arrowSize);
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700199
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700200 addLine(QLineF(sourceArrowP0, dest), QPen(Qt::black));
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700201 addPolygon(QPolygonF() << sourceArrowP0<< sourceArrowP1 <<
202 sourceArrowP2, QPen(Qt::black), QBrush(Qt::black));
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700203 }
204}
205
206void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700207DigestTreeScene::plotNode(const std::vector<TreeLayout::Coordinate>& childNodesCo,
208 QString digest, int nodeSize)
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700209{
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700210 RosterIterator it(m_roster);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700211 int n = childNodesCo.size();
212 int rim = 3;
Zhenkai Zhu71b42cb2012-06-04 09:42:53 -0700213
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700214 // plot root node
215 QRectF rootBoundingRect(0, 0, nodeSize, nodeSize);
216 QRectF rootInnerBoundingRect(rim, rim, nodeSize - rim * 2, nodeSize - rim * 2);
217 addRect(rootBoundingRect, QPen(Qt::black), QBrush(Qt::darkRed));
218 addRect(rootInnerBoundingRect, QPen(Qt::black), QBrush(Qt::lightGray));
Zhenkai Zhuc5470612012-06-05 12:28:59 -0700219 QRectF digestRect(- 5.5 * nodeSize , - nodeSize, 12 * nodeSize, 30);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700220 addRect(digestRect, QPen(Qt::darkCyan), QBrush(Qt::darkCyan));
Zhenkai Zhu27df8d42012-06-05 12:04:04 -0700221
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700222 QGraphicsTextItem *digestItem = addText(digest);
223 QRectF digestBoundingRect = digestItem->boundingRect();
Zhenkai Zhu27df8d42012-06-05 12:04:04 -0700224 digestItem->setDefaultTextColor(Qt::black);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700225 digestItem->setFont(QFont("Cursive", 12, QFont::Bold));
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700226 digestItem->setPos(- 4.5 * nodeSize + (12 * nodeSize - digestBoundingRect.width()) / 2,
227 - nodeSize + 5);
Yingdi Yud45777b2014-10-16 23:54:11 -0700228 m_displayRootDigest = digestItem;
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700229
230 // plot child nodes
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700231 for (int i = 0; i < n; i++) {
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700232 if (it.hasNext())
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700233 it.next();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700234 else
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700235 abort();
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700236
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700237 double x = childNodesCo[i].x;
238 double y = childNodesCo[i].y;
239 QRectF boundingRect(x, y, nodeSize, nodeSize);
240 QRectF innerBoundingRect(x + rim, y + rim, nodeSize - rim * 2, nodeSize - rim * 2);
241 DisplayUserPtr p = it.value();
242 QGraphicsRectItem *rectItem = addRect(boundingRect, QPen(Qt::black), QBrush(Qt::darkBlue));
243 p->setRimRectItem(rectItem);
244
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700245 QGraphicsRectItem *innerRectItem = addRect(innerBoundingRect,
246 QPen(Qt::black),
247 QBrush(Qt::lightGray));
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700248 p->setInnerRectItem(innerRectItem);
249
Yingdi Yud45777b2014-10-16 23:54:11 -0700250 std::string s = boost::lexical_cast<std::string>(p->getSeqNo());
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700251 QGraphicsTextItem *seqItem = addText(s.c_str());
252 seqItem->setFont(QFont("Cursive", 12, QFont::Bold));
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700253 QRectF seqBoundingRect = seqItem->boundingRect();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700254 seqItem->setPos(x + nodeSize / 2 - seqBoundingRect.width() / 2,
255 y + nodeSize / 2 - seqBoundingRect.height() / 2);
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700256 p->setSeqTextItem(seqItem);
257
258 QRectF textRect(x - nodeSize / 2, y + nodeSize, 2 * nodeSize, 30);
259 QGraphicsRectItem *nickRectItem = addRect(textRect, QPen(Qt::darkCyan), QBrush(Qt::darkCyan));
260 p->setNickRectItem(nickRectItem);
261 QGraphicsTextItem *nickItem = addText(p->getNick());
262 QRectF textBoundingRect = nickItem->boundingRect();
263 nickItem->setDefaultTextColor(Qt::white);
264 nickItem->setFont(QFont("Cursive", 12, QFont::Bold));
265 nickItem->setPos(x + nodeSize / 2 - textBoundingRect.width() / 2, y + nodeSize + 5);
266 p->setNickTextItem(nickItem);
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700267 }
Zhenkai Zhu21d75f92012-06-04 21:23:34 -0700268
Zhenkai Zhuf474a0a2012-05-30 15:06:29 -0700269}
270
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700271void
272DigestTreeScene::reDrawNode(DisplayUserPtr p, QColor rimColor)
273{
274 QGraphicsRectItem *rimItem = p->getRimRectItem();
275 rimItem->setBrush(QBrush(rimColor));
276 QGraphicsRectItem *innerItem = p->getInnerRectItem();
277 innerItem->setBrush(QBrush(Qt::lightGray));
278 QGraphicsTextItem *seqTextItem = p->getSeqTextItem();
Yingdi Yud45777b2014-10-16 23:54:11 -0700279 std::string s = boost::lexical_cast<std::string>(p->getSeqNo());
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700280 seqTextItem->setPlainText(s.c_str());
281 QRectF textBR = seqTextItem->boundingRect();
282 QRectF innerBR = innerItem->boundingRect();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700283 seqTextItem->setPos(innerBR.x() + (innerBR.width() - textBR.width())/2,
284 innerBR.y() + (innerBR.height() - textBR.height())/2);
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700285}
286
Yingdi Yueb692ac2015-02-10 18:46:18 -0800287} // namespace chronochat
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700288
Alexander Afanasyevb4b92292013-07-09 13:54:59 -0700289#if WAF
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700290#include "digest-tree-scene.moc"
Yingdi Yu42125862014-08-07 17:04:28 -0700291// #include "digest-tree-scene.cpp.moc"
Alexander Afanasyevb4b92292013-07-09 13:54:59 -0700292#endif