gui: Display the trust tree

Change-Id: Id48227b0f2e903905d7c8eb9795d038d6562794c
diff --git a/src/digest-tree-scene.cpp b/src/digest-tree-scene.cpp
new file mode 100644
index 0000000..14f95bd
--- /dev/null
+++ b/src/digest-tree-scene.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "digest-tree-scene.h"
+
+#include <QtGui>
+
+#ifndef Q_MOC_RUN
+#include <vector>
+#include <iostream>
+#include <assert.h>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+#endif
+
+static const double Pi = 3.14159265358979323846264338327950288419717;
+
+//DisplayUserPtr DisplayUserNullPtr;
+
+DigestTreeScene::DigestTreeScene(QWidget *parent)
+  : QGraphicsScene(parent)
+{
+  previouslyUpdatedUser = DisplayUserNullPtr;
+}
+
+void
+DigestTreeScene::processUpdate(const std::vector<Sync::MissingDataInfo> &v, QString digest)
+{
+  int n = v.size();
+  bool rePlot = false; 
+  for (int i = 0; i < n; i++) 
+  {
+    Roster_iterator it = m_roster.find(v[i].prefix.c_str());
+    if (it == m_roster.end()) 
+    {
+      rePlot = true; 
+      DisplayUserPtr p(new DisplayUser());
+      time_t tempTime = time(NULL) - FRESHNESS + 1;
+      p->setReceived(tempTime);
+      p->setPrefix(v[i].prefix.c_str());
+      p->setSeq(v[i].high);
+      m_roster.insert(p->getPrefix(), p);
+    }
+    else 
+    {
+      it.value()->setSeq(v[i].high);
+    }
+  }
+
+  if (rePlot) 
+  {
+    plot(digest);
+    QTimer::singleShot(2100, this, SLOT(emitReplot()));
+  }
+  else 
+  {
+    for (int i = 0; i < n; i++) 
+    {
+      Roster_iterator it = m_roster.find(v[i].prefix.c_str());
+      if (it != m_roster.end()) {
+        DisplayUserPtr p = it.value();
+        QGraphicsTextItem *item = p->getSeqTextItem();
+        QGraphicsRectItem *rectItem = p->getInnerRectItem();
+        std::string s = boost::lexical_cast<std::string>(p->getSeqNo().getSeq());
+        item->setPlainText(s.c_str());
+        QRectF textBR = item->boundingRect();
+        QRectF rectBR = rectItem->boundingRect();
+        item->setPos(rectBR.x() + (rectBR.width() - textBR.width())/2, rectBR.y() + (rectBR.height() - textBR.height())/2);
+      }
+    }
+    m_rootDigest->setPlainText(digest);
+  }
+}
+
+void
+DigestTreeScene::emitReplot()
+{
+  emit replot();
+}
+
+QStringList
+DigestTreeScene::getRosterList()
+{
+  QStringList rosterList;
+  RosterIterator it(m_roster);
+  while(it.hasNext())
+  {
+    it.next();
+    DisplayUserPtr p = it.value();
+    if (p != DisplayUserNullPtr)
+    {
+      rosterList << "- " + p->getNick();
+    }
+  }
+  return rosterList;
+}
+
+void
+DigestTreeScene::msgReceived(QString prefix, QString nick)
+{
+  Roster_iterator it = m_roster.find(prefix);
+  if (it != m_roster.end()) 
+    {
+      std::cout << "Updating for prefix = " << prefix.toStdString() << " nick = " << nick.toStdString() << std::endl;
+      DisplayUserPtr p = it.value();
+      p->setReceived(time(NULL));
+      if (nick != p->getNick()) 
+        {
+          std::cout << "old nick = " << p->getNick().toStdString() << std::endl;
+          p->setNick(nick);
+          QGraphicsTextItem *nickItem = p->getNickTextItem();
+          QGraphicsRectItem *nickRectItem = p->getNickRectItem();
+          nickItem->setPlainText(p->getNick());
+          QRectF rectBR = nickRectItem->boundingRect();
+          QRectF nickBR = nickItem->boundingRect();
+          nickItem->setPos(rectBR.x() + (rectBR.width() - nickBR.width())/2, rectBR.y() + 5);
+          emit rosterChanged(QStringList());
+        }
+
+      reDrawNode(p, Qt::red);
+
+      if (previouslyUpdatedUser != DisplayUserNullPtr && previouslyUpdatedUser != p) 
+        {
+          reDrawNode(previouslyUpdatedUser, Qt::darkBlue);
+        }
+
+      previouslyUpdatedUser = p;
+    }
+}
+
+void
+DigestTreeScene::clearAll()
+{
+  clear();
+  m_roster.clear();
+}
+
+bool
+DigestTreeScene::removeNode(const QString prefix)
+{
+  int removedCount = m_roster.remove(prefix);
+  return (removedCount > 0);
+}
+
+void
+DigestTreeScene::plot(QString digest)
+{
+#ifdef _DEBUG
+  std::cout << "Plotting at time: " << time(NULL) << std::endl;
+#endif
+  clear();
+
+  int nodeSize = 40;
+
+  int siblingDistance = 100, levelDistance = 100;
+  std::auto_ptr<TreeLayout> layout(new OneLevelTreeLayout());
+  layout->setSiblingDistance(siblingDistance);
+  layout->setLevelDistance(levelDistance);
+
+  // do some cleaning, get rid of stale member info
+  Roster_iterator it = m_roster.begin();
+  QStringList staleUserList;
+  while (it != m_roster.end())
+  {
+    DisplayUserPtr p = it.value();
+    if (p != DisplayUserNullPtr)
+    {
+      time_t now = time(NULL);
+      if (now - p->getReceived() >= FRESHNESS)
+      {
+#ifdef _DEBUG
+        std::cout << "Removing user: " << p->getNick().toStdString() << std::endl;
+        std::cout << "now - last = " << now - p->getReceived() << std::endl;
+#endif
+        staleUserList << p->getNick();
+        p = DisplayUserNullPtr;
+        it = m_roster.erase(it);
+      }
+      else
+      {
+        if (!m_currentPrefix.startsWith("/private/local") && p->getPrefix().startsWith("/private/local"))
+        {
+#ifdef _DEBUG
+          std::cout << "erasing: " << p->getPrefix().toStdString() << std::endl;
+#endif
+          staleUserList << p->getNick();
+          p = DisplayUserNullPtr;
+          it = m_roster.erase(it);
+          continue;
+        }
+        ++it;
+      }
+    }
+    else
+    {
+      it = m_roster.erase(it);
+    }
+  }
+
+  // for simpicity here, whenever we replot, we also redo the roster list
+  emit rosterChanged(staleUserList);
+
+  int n = m_roster.size();
+
+  std::vector<TreeLayout::Coordinate> childNodesCo(n);
+
+  layout->setOneLevelLayout(childNodesCo);
+
+  plotEdge(childNodesCo, nodeSize);
+  plotNode(childNodesCo, digest, nodeSize);
+
+  previouslyUpdatedUser = DisplayUserNullPtr;
+
+}
+
+void
+DigestTreeScene::plotEdge(const std::vector<TreeLayout::Coordinate> &childNodesCo, int nodeSize)
+{
+  int n = childNodesCo.size();
+  for (int i = 0; i < n; i++) {
+    double x1 = 0.0, y1 = 0.0;
+    double x2 = childNodesCo[i].x, y2 = childNodesCo[i].y;
+    QPointF src(x1 + nodeSize/2, y1 + nodeSize/2);
+    QPointF dest(x2 + nodeSize/2, y2 + nodeSize/2);
+    QLineF line(src, dest);
+    double angle = ::acos(line.dx() / line.length());
+
+    double arrowSize = 10;
+    QPointF sourceArrowP0 = src + QPointF((nodeSize/2 + 10) * line.dx() / line.length(),  (nodeSize/2 +10) * line.dy() / line.length());
+    QPointF sourceArrowP1 = sourceArrowP0 + QPointF(cos(angle + Pi / 3 - Pi/2) * arrowSize,
+                                                    sin(angle + Pi / 3 - Pi/2) * arrowSize);
+    QPointF sourceArrowP2 = sourceArrowP0 + QPointF(cos(angle + Pi - Pi / 3 - Pi/2) * arrowSize,
+                                                         sin(angle + Pi - Pi / 3 - Pi/2) * arrowSize);
+
+    addLine(QLineF(sourceArrowP0, dest), QPen(Qt::black));
+    addPolygon(QPolygonF() << sourceArrowP0<< sourceArrowP1 << sourceArrowP2, QPen(Qt::black), QBrush(Qt::black));
+  }
+}
+
+void
+DigestTreeScene::plotNode(const std::vector<TreeLayout::Coordinate> &childNodesCo, QString digest, int nodeSize)
+{
+  RosterIterator it(m_roster);
+  int n = childNodesCo.size();
+  int rim = 3;
+
+  // plot root node
+  QRectF rootBoundingRect(0, 0, nodeSize, nodeSize);
+  QRectF rootInnerBoundingRect(rim, rim, nodeSize - rim * 2, nodeSize - rim * 2);
+  addRect(rootBoundingRect, QPen(Qt::black), QBrush(Qt::darkRed));
+  addRect(rootInnerBoundingRect, QPen(Qt::black), QBrush(Qt::lightGray));
+  QRectF digestRect(- 5.5 * nodeSize , - nodeSize, 12 * nodeSize, 30);
+  addRect(digestRect, QPen(Qt::darkCyan), QBrush(Qt::darkCyan));
+
+  QGraphicsTextItem *digestItem = addText(digest);
+  QRectF digestBoundingRect = digestItem->boundingRect();
+  digestItem->setDefaultTextColor(Qt::black);
+  digestItem->setFont(QFont("Cursive", 12, QFont::Bold));
+  digestItem->setPos(- 4.5 * nodeSize + (12 * nodeSize - digestBoundingRect.width()) / 2, - nodeSize + 5);
+  m_rootDigest = digestItem;
+
+  // plot child nodes
+  for (int i = 0; i < n; i++)
+  {
+    if (it.hasNext()) 
+    {
+      it.next();
+    }
+    else 
+    {
+      abort();
+    }
+
+    double x = childNodesCo[i].x;
+    double y = childNodesCo[i].y;
+    QRectF boundingRect(x, y, nodeSize, nodeSize);
+    QRectF innerBoundingRect(x + rim, y + rim, nodeSize - rim * 2, nodeSize - rim * 2);
+    DisplayUserPtr p = it.value();
+    QGraphicsRectItem *rectItem = addRect(boundingRect, QPen(Qt::black), QBrush(Qt::darkBlue));
+    p->setRimRectItem(rectItem);
+
+    QGraphicsRectItem *innerRectItem = addRect(innerBoundingRect, QPen(Qt::black), QBrush(Qt::lightGray));
+    p->setInnerRectItem(innerRectItem);
+
+    std::string s = boost::lexical_cast<std::string>(p->getSeqNo().getSeq());
+    QGraphicsTextItem *seqItem = addText(s.c_str());
+    seqItem->setFont(QFont("Cursive", 12, QFont::Bold));
+    QRectF seqBoundingRect = seqItem->boundingRect(); 
+    seqItem->setPos(x + nodeSize / 2 - seqBoundingRect.width() / 2, y + nodeSize / 2 - seqBoundingRect.height() / 2);
+    p->setSeqTextItem(seqItem);
+
+    QRectF textRect(x - nodeSize / 2, y + nodeSize, 2 * nodeSize, 30);
+    QGraphicsRectItem *nickRectItem = addRect(textRect, QPen(Qt::darkCyan), QBrush(Qt::darkCyan));
+    p->setNickRectItem(nickRectItem);
+    QGraphicsTextItem *nickItem = addText(p->getNick());
+    QRectF textBoundingRect = nickItem->boundingRect();
+    nickItem->setDefaultTextColor(Qt::white);
+    nickItem->setFont(QFont("Cursive", 12, QFont::Bold));
+    nickItem->setPos(x + nodeSize / 2 - textBoundingRect.width() / 2, y + nodeSize + 5);
+    p->setNickTextItem(nickItem);
+  }
+
+}
+
+void
+DigestTreeScene::reDrawNode(DisplayUserPtr p, QColor rimColor)
+{
+    QGraphicsRectItem *rimItem = p->getRimRectItem();
+    rimItem->setBrush(QBrush(rimColor));
+    QGraphicsRectItem *innerItem = p->getInnerRectItem();
+    innerItem->setBrush(QBrush(Qt::lightGray));
+    QGraphicsTextItem *seqTextItem = p->getSeqTextItem();
+    std::string s = boost::lexical_cast<std::string>(p->getSeqNo().getSeq());
+    seqTextItem->setPlainText(s.c_str());
+    QRectF textBR = seqTextItem->boundingRect();
+    QRectF innerBR = innerItem->boundingRect();
+    seqTextItem->setPos(innerBR.x() + (innerBR.width() - textBR.width())/2, innerBR.y() + (innerBR.height() - textBR.height())/2);
+}
+
+#if WAF
+#include "digest-tree-scene.moc"
+#include "digest-tree-scene.cpp.moc"
+#endif