show user coming and leaving
diff --git a/chatbuf.proto b/chatbuf.proto
index c8e4d35..6ea6ea4 100644
--- a/chatbuf.proto
+++ b/chatbuf.proto
@@ -7,7 +7,8 @@
     CHAT = 0;
     HELLO = 1;
     LEAVE = 2;
-    OTHER = 3;
+    JOIN  = 3;
+    OTHER = 4;
   }
   required ChatMessageType type = 3 [default = CHAT];
   optional string data = 4;
diff --git a/chatdialog.cpp b/chatdialog.cpp
index 4a6ae3f..0570985 100644
--- a/chatdialog.cpp
+++ b/chatdialog.cpp
@@ -14,7 +14,7 @@
 static const int HELLO_INTERVAL = 90;  // seconds
 
 ChatDialog::ChatDialog(QWidget *parent)
-  : QDialog(parent), m_sock(NULL), m_lastMsgTime(0), m_sendJoin(true)
+  : QDialog(parent), m_sock(NULL), m_lastMsgTime(0)
 {
   // have to register this, otherwise
   // the signal-slot system won't recognize this type
@@ -53,12 +53,12 @@
   connect(treeButton, SIGNAL(pressed()), this, SLOT(treeButtonPressed()));
   connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool)), this, SLOT(processData(QString, const char *, size_t, bool)));
   connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)), this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
-  connect(this, SIGNAL(removeReceived(QString)), this, SLOT(processRemove(QString)));
+  //connect(this, SIGNAL(removeReceived(QString)), this, SLOT(processRemove(QString)));
   connect(m_timer, SIGNAL(timeout()), this, SLOT(replot()));
   connect(m_scene, SIGNAL(replot()), this, SLOT(replot()));
   connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showNormal()));
   connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
-  connect(m_scene, SIGNAL(rosterChanged()), this, SLOT(updateRosterList()));
+  connect(m_scene, SIGNAL(rosterChanged(QStringList)), this, SLOT(updateRosterList(QStringList)));
 
   // create sync socket
   if(!m_user.getChatroom().isEmpty()) {
@@ -68,7 +68,7 @@
     try 
     {
       m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
-      sendHello();
+      sendJoin();
       m_timer->start(FRESHNESS * 2000);
     }
     catch (Sync::CcnxOperationException ex)
@@ -85,7 +85,16 @@
 {
   if (m_sock != NULL) 
   {
+    SyncDemo::ChatMessage msg;
+    formHelloMessage(msg);
+    msg.set_type(SyncDemo::ChatMessage::LEAVE);
+    sendMsg(msg);
+    usleep(100000);
     m_sock->remove(m_user.getPrefix().toStdString());
+    usleep(100000);
+#ifdef __DEBUG
+    std::cout << "Sync REMOVE signal sent" << std::endl;
+#endif
     delete m_sock;
     m_sock = NULL;
   }
@@ -99,11 +108,21 @@
 }
 
 void
-ChatDialog::updateRosterList()
+ChatDialog::updateRosterList(QStringList staleUserList)
 {
   boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
   QStringList rosterList = m_scene->getRosterList();
   m_rosterModel->setStringList(rosterList);
+  QString user;
+  QStringListIterator it(staleUserList);
+  while(it.hasNext())
+  {
+    SyncDemo::ChatMessage msg;
+    formHelloMessage(msg);
+    msg.set_type(SyncDemo::ChatMessage::LEAVE);
+    msg.set_from(it.next().toStdString());
+    appendMessage(msg);
+  }
 }
 
 void 
@@ -151,77 +170,133 @@
 {
   boost::recursive_mutex::scoped_lock lock(m_msgMutex);
 
-  if (msg.type() != SyncDemo::ChatMessage::CHAT) {
-    return;
-  }
-
-  if (!msg.has_data()) {
-    return;
-  }
-
-  if (msg.from().empty() || msg.data().empty()) {
-    return;
-  }
-
-  if (!msg.has_timestamp())
+  if (msg.type() == SyncDemo::ChatMessage::CHAT) 
   {
-    return;
-  }
-#ifdef __DEBUG
-  std::cout << "Displaying msg from: " << msg.from() << ", data is: " << msg.data() << std::endl;
-#endif
 
-  QTextCharFormat nickFormat;
-  nickFormat.setForeground(Qt::darkGreen);
-  nickFormat.setFontWeight(QFont::Bold);
-  nickFormat.setFontUnderline(true);
-  nickFormat.setUnderlineColor(Qt::gray);
-  QTextCharFormat timeFormat;
-  timeFormat.setForeground(Qt::gray);
-  timeFormat.setFontUnderline(true);
-  timeFormat.setUnderlineColor(Qt::gray);
-
-  QTextCursor cursor(textEdit->textCursor());
-  cursor.movePosition(QTextCursor::End);
-  QTextTableFormat tableFormat;
-  tableFormat.setBorder(0);
-  QTextTable *table = cursor.insertTable(1, 2, tableFormat);
-  QString from = QString("%1 ").arg(msg.from().c_str());
-  QTextTableCell fromCell = table->cellAt(0, 0);
-  fromCell.setFormat(nickFormat);
-  fromCell.firstCursorPosition().insertText(from);
-  QTextTableCell timeCell = table->cellAt(0, 1);
-  timeCell.setFormat(timeFormat);
-  time_t timestamp = msg.timestamp();
-  struct tm *tm_time = localtime(&timestamp);
-  int hour = tm_time->tm_hour;
-  QString amOrPM;
-  if (hour > 12)
-  {
-    hour -= 12;
-    amOrPM = "PM";
-  }
-  else
-  {
-    amOrPM = "AM";
-    if (hour == 0)
+    if (!msg.has_data())
     {
-      hour = 12;
+      return;
     }
+
+    if (msg.from().empty() || msg.data().empty())
+    {
+      return;
+    }
+
+    if (!msg.has_timestamp())
+    {
+      return;
+    }
+
+    QTextCharFormat nickFormat;
+    nickFormat.setForeground(Qt::darkGreen);
+    nickFormat.setFontWeight(QFont::Bold);
+    nickFormat.setFontUnderline(true);
+    nickFormat.setUnderlineColor(Qt::gray);
+    QTextCharFormat timeFormat;
+    timeFormat.setForeground(Qt::gray);
+    timeFormat.setFontUnderline(true);
+    timeFormat.setUnderlineColor(Qt::gray);
+
+    QTextCursor cursor(textEdit->textCursor());
+    cursor.movePosition(QTextCursor::End);
+    QTextTableFormat tableFormat;
+    tableFormat.setBorder(0);
+    QTextTable *table = cursor.insertTable(1, 2, tableFormat);
+    QString from = QString("%1 ").arg(msg.from().c_str());
+    QTextTableCell fromCell = table->cellAt(0, 0);
+    fromCell.setFormat(nickFormat);
+    fromCell.firstCursorPosition().insertText(from);
+    QTextTableCell timeCell = table->cellAt(0, 1);
+    timeCell.setFormat(timeFormat);
+    time_t timestamp = msg.timestamp();
+    struct tm *tm_time = localtime(&timestamp);
+    int hour = tm_time->tm_hour;
+    QString amOrPM;
+    if (hour > 12)
+    {
+      hour -= 12;
+      amOrPM = "PM";
+    }
+    else
+    {
+      amOrPM = "AM";
+      if (hour == 0)
+      {
+        hour = 12;
+      }
+    }
+
+    char textTime[12];
+    sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
+    timeCell.firstCursorPosition().insertText(textTime);
+
+    
+    QTextCursor nextCursor(textEdit->textCursor());
+    nextCursor.movePosition(QTextCursor::End);
+    table = nextCursor.insertTable(1, 1, tableFormat);
+    table->cellAt(0, 0).firstCursorPosition().insertText(msg.data().c_str());
+    showMessage(from, msg.data().c_str());
   }
 
-  char textTime[12];
-  sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
-  timeCell.firstCursorPosition().insertText(textTime);
+  if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
+  {
+    QTextCharFormat nickFormat;
+    nickFormat.setForeground(Qt::gray);
+    nickFormat.setFontWeight(QFont::Bold);
+    nickFormat.setFontUnderline(true);
+    nickFormat.setUnderlineColor(Qt::gray);
+    QTextCharFormat timeFormat;
+    timeFormat.setForeground(Qt::gray);
+    timeFormat.setFontUnderline(true);
+    timeFormat.setUnderlineColor(Qt::gray);
 
-  
-  QTextCursor nextCursor(textEdit->textCursor());
-  nextCursor.movePosition(QTextCursor::End);
-  table = nextCursor.insertTable(1, 1, tableFormat);
-  table->cellAt(0, 0).firstCursorPosition().insertText(msg.data().c_str());
+    QTextCursor cursor(textEdit->textCursor());
+    cursor.movePosition(QTextCursor::End);
+    QTextTableFormat tableFormat;
+    tableFormat.setBorder(0);
+    QTextTable *table = cursor.insertTable(1, 2, tableFormat);
+    QString action;
+    if (msg.type() == SyncDemo::ChatMessage::JOIN)
+    {
+      action = "enters room";
+    }
+    else
+    {
+      action = "leaves room";
+    }
+
+    QString from = QString("%1 %2  ").arg(msg.from().c_str()).arg(action);
+    QTextTableCell fromCell = table->cellAt(0, 0);
+    fromCell.setFormat(nickFormat);
+    fromCell.firstCursorPosition().insertText(from);
+    QTextTableCell timeCell = table->cellAt(0, 1);
+    timeCell.setFormat(timeFormat);
+    time_t timestamp = msg.timestamp();
+    struct tm *tm_time = localtime(&timestamp);
+    int hour = tm_time->tm_hour;
+    QString amOrPM;
+    if (hour > 12)
+    {
+      hour -= 12;
+      amOrPM = "PM";
+    }
+    else
+    {
+      amOrPM = "AM";
+      if (hour == 0)
+      {
+        hour = 12;
+      }
+    }
+
+    char textTime[12];
+    sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
+    timeCell.firstCursorPosition().insertText(textTime);
+  }
+
   QScrollBar *bar = textEdit->verticalScrollBar();
   bar->setValue(bar->maximum());
-  showMessage(from, msg.data().c_str());
 }
 
 void
@@ -321,6 +396,11 @@
 #ifdef __DEBUG
   std::cout <<"<<< updating scene for" << prefix << ": " << msg.from()  << std::endl;
 #endif
+  if (msg.type() == SyncDemo::ChatMessage::LEAVE)
+  {
+    processRemove(prefix.c_str());
+  }
+  else
   {
     boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
     m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
@@ -331,7 +411,10 @@
 void
 ChatDialog::processRemoveWrapper(std::string prefix)
 {
-  emit removeReceived(prefix.c_str());
+#ifdef __DEBUG
+  std::cout << "Sync REMOVE signal received for prefix: " << prefix << std::endl;
+#endif
+  //emit removeReceived(prefix.c_str());
 }
 
 void
@@ -478,16 +561,25 @@
 }
 
 void 
+ChatDialog::sendJoin()
+{
+  SyncDemo::ChatMessage msg;
+  formHelloMessage(msg);
+  msg.set_type(SyncDemo::ChatMessage::JOIN);
+  sendMsg(msg);
+  QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
+}
+
+void 
 ChatDialog::sendHello()
 {
   time_t now = time(NULL);
   int elapsed = now - m_lastMsgTime;
-  if (elapsed >= m_randomizedInterval / 1000 || m_sendJoin)
+  if (elapsed >= m_randomizedInterval / 1000)
   {
     SyncDemo::ChatMessage msg;
     formHelloMessage(msg);
     sendMsg(msg);
-    m_sendJoin = false;
     QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
   }
   else
@@ -517,6 +609,8 @@
     treeViewer->show();
     treeButton->setText("Hide Sync Tree");
   }
+
+  fitView();
 }
 
 void
@@ -574,8 +668,7 @@
     try
     {
       m_sock = new Sync::SyncAppSocket(syncPrefix, bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2), bind(&ChatDialog::processRemoveWrapper, this, _1));
-      m_sendJoin = true;
-      sendHello();
+      sendJoin();
       m_timer->start(FRESHNESS * 2000);
     }
     catch (Sync::CcnxOperationException ex)
diff --git a/chatdialog.h b/chatdialog.h
index b604b02..60e7c4b 100644
--- a/chatdialog.h
+++ b/chatdialog.h
@@ -63,8 +63,9 @@
   void checkSetting();
   void settingUpdated(QString, QString, QString);
   void sendHello();
+  void sendJoin();
   void replot();
-  void updateRosterList();
+  void updateRosterList(QStringList);
 
   // icon related
   void iconActivated(QSystemTrayIcon::ActivationReason reason);
@@ -87,7 +88,6 @@
   int m_randomizedInterval;
   QTimer *m_timer;
   QStringListModel *m_rosterModel;
-  bool m_sendJoin;
 
   // icon related
   QAction *minimizeAction;
diff --git a/digesttreescene.cpp b/digesttreescene.cpp
index 5364f0f..79f5fe6 100644
--- a/digesttreescene.cpp
+++ b/digesttreescene.cpp
@@ -91,9 +91,6 @@
 void
 DigestTreeScene::msgReceived(QString prefix, QString nick)
 {
-#ifdef __DEBUG
-  std::cout << "Finding " << prefix.toStdString() << std::endl;
-#endif
   Roster_iterator it = m_roster.find(prefix);
   if (it != m_roster.end()) 
   {
@@ -107,7 +104,7 @@
       QRectF rectBR = nickRectItem->boundingRect();
       QRectF nickBR = nickItem->boundingRect();
       nickItem->setPos(rectBR.x() + (rectBR.width() - nickBR.width())/2, rectBR.y() + 5);
-      emit rosterChanged();
+      emit rosterChanged(QStringList());
     }
 
     reDrawNode(p, Qt::red);
@@ -119,18 +116,6 @@
 
     previouslyUpdatedUser = p;
   }
-#ifdef __DEBUG
-  else 
-  {
-    std::cout << "Couldn't find prefix, " << prefix.toStdString() << ": let's check"<< std::endl;
-    Roster_iterator ii = m_roster.begin();
-    while (ii != m_roster.end())
-    {
-      std::cout <<"Prefix: " << ii.key().toStdString() << std::endl;
-      ++ii;
-    }
-  }
-#endif
 }
 
 void
@@ -164,6 +149,7 @@
 
   // 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();
@@ -176,6 +162,7 @@
         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);
       }
@@ -191,7 +178,7 @@
   }
 
   // for simpicity here, whenever we replot, we also redo the roster list
-  emit rosterChanged();
+  emit rosterChanged(staleUserList);
 
   int n = m_roster.size();
   std::vector<TreeLayout::Coordinate> childNodesCo(n);
diff --git a/digesttreescene.h b/digesttreescene.h
index 0e2e0f7..33a4973 100644
--- a/digesttreescene.h
+++ b/digesttreescene.h
@@ -39,7 +39,7 @@
 
 signals:
   void replot();
-  void rosterChanged();
+  void rosterChanged(QStringList);
 
 private slots:
   void emitReplot();