internal stuff only: added accessors for bufferName, networkId, networkName and buffe...
[quassel.git] / src / client / networkmodel.cpp
index 1a36ac0..dbfa6f0 100644 (file)
@@ -55,8 +55,22 @@ QVariant NetworkItem::data(int column, int role) const {
   }
 }
 
+BufferItem *NetworkItem::findBufferItem(BufferId bufferId) {
+  BufferItem *bufferItem = 0;
+  
+  for(int i = 0; i < childCount(); i++) {
+    bufferItem = qobject_cast<BufferItem *>(child(i));
+    if(!bufferItem)
+      continue;
+    if(bufferItem->bufferId() == bufferId)
+      return bufferItem;
+  }
+  return 0;
+}
+
+
 BufferItem *NetworkItem::bufferItem(const BufferInfo &bufferInfo) {
-  BufferItem *bufferItem = qobject_cast<BufferItem *>(childById(qHash(bufferInfo.bufferId())));
+  BufferItem *bufferItem = findBufferItem(bufferInfo);
   if(bufferItem)
     return bufferItem;
   
@@ -90,6 +104,8 @@ void NetworkItem::attachNetwork(Network *network) {
          this, SLOT(setCurrentServer(QString)));
   connect(network, SIGNAL(ircChannelAdded(IrcChannel *)),
          this, SLOT(attachIrcChannel(IrcChannel *)));
+  connect(network, SIGNAL(ircUserAdded(IrcUser *)),
+         this, SLOT(attachIrcUser(IrcUser *)));
   connect(network, SIGNAL(connectedSet(bool)),
          this, SIGNAL(dataChanged()));
   connect(network, SIGNAL(destroyed()),
@@ -107,6 +123,20 @@ void NetworkItem::attachIrcChannel(IrcChannel *ircChannel) {
 
     if(channelItem->bufferName().toLower() == ircChannel->name().toLower()) {
       channelItem->attachIrcChannel(ircChannel);
+      return;
+    }
+  }
+}
+
+void NetworkItem::attachIrcUser(IrcUser *ircUser) {
+  QueryBufferItem *queryItem = 0;
+  for(int i = 0; i < childCount(); i++) {
+    queryItem = qobject_cast<QueryBufferItem *>(child(i));
+    if(!queryItem)
+      continue;
+
+    if(queryItem->bufferName().toLower() == ircUser->nick().toLower()) {
+      queryItem->attachIrcUser(ircUser);
       break;
     }
   }
@@ -130,6 +160,10 @@ QString NetworkItem::toolTip(int column) const {
   toolTip.append(QString("Server: %1").arg(currentServer()));
   toolTip.append(QString("Users: %1").arg(nickCount()));
 
+  if(_network) {
+    toolTip.append(QString("Lag: %1 msecs").arg(_network->latency()));
+  }
+
   return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
 }
 
@@ -247,21 +281,74 @@ QString StatusBufferItem::toolTip(int column) const {
 *  QueryBufferItem
 *****************************************/
 QueryBufferItem::QueryBufferItem(const BufferInfo &bufferInfo, NetworkItem *parent)
-  : BufferItem(bufferInfo, parent)
+  : BufferItem(bufferInfo, parent),
+    _ircUser(0)
 {
   setFlags(flags() | Qt::ItemIsDropEnabled);
+
+  const Network *net = Client::network(bufferInfo.networkId());
+  if(!net)
+    return;
+
+  IrcUser *ircUser = net->ircUser(bufferInfo.bufferName());
+  if(ircUser)
+    attachIrcUser(ircUser);
+}
+
+QVariant QueryBufferItem::data(int column, int role) const {
+  switch(role) {
+  case NetworkModel::UserAwayRole:
+    return (bool)_ircUser ? _ircUser->isAway() : false;
+  default:
+    return BufferItem::data(column, role);
+  }
 }
 
 QString QueryBufferItem::toolTip(int column) const {
+  // pretty much code duplication of IrcUserItem::toolTip() but inheritance won't solve this... 
   Q_UNUSED(column);
   QStringList toolTip;
 
   toolTip.append(tr("<b>Query with %1</b>").arg(bufferName()));
-  if(topic() != "") {
-    toolTip.append(tr("Away Message: %1").arg(topic()));
+
+  if(_ircUser) {
+    if(_ircUser->userModes() != "") toolTip[0].append(QString(" (%1)").arg(_ircUser->userModes()));
+    if(_ircUser->isAway()) {
+      toolTip[0].append(QString(" (away%1)").arg(!_ircUser->awayMessage().isEmpty() ? (QString(" ") + _ircUser->awayMessage()) : QString()));
+    }
+    if(!_ircUser->realName().isEmpty()) toolTip.append(_ircUser->realName());
+    if(!_ircUser->ircOperator().isEmpty()) toolTip.append(QString("%1 %2").arg(_ircUser->nick()).arg(_ircUser->ircOperator()));
+    if(!_ircUser->suserHost().isEmpty()) toolTip.append(_ircUser->suserHost());
+    if(!_ircUser->whoisServiceReply().isEmpty()) toolTip.append(_ircUser->whoisServiceReply());
+    
+    toolTip.append(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!")+1));
+    
+    if(_ircUser->idleTime().isValid()) {
+      QDateTime now = QDateTime::currentDateTime();
+      QDateTime idle = _ircUser->idleTime();
+      int idleTime = idle.secsTo(now);
+      toolTip.append(tr("idling since %1").arg(secondsToString(idleTime)));
+    }
+    if(_ircUser->loginTime().isValid()) {
+      toolTip.append(tr("login time: %1").arg(_ircUser->loginTime().toString()));
+    }
+    
+    if(!_ircUser->server().isEmpty()) toolTip.append(tr("server: %1").arg(_ircUser->server()));
   }
 
-  return tr("<p> %1 </p>").arg(toolTip.join("<br />"));
+  return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
+}
+
+void QueryBufferItem::attachIrcUser(IrcUser *ircUser) {
+  _ircUser = ircUser;
+  connect(_ircUser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
+  connect(_ircUser, SIGNAL(awaySet(bool)), this, SIGNAL(dataChanged()));
+  emit dataChanged();
+}
+
+void QueryBufferItem::ircUserDestroyed() {
+  _ircUser = 0;
+  emit dataChanged();
 }
 
 /*****************************************
@@ -271,6 +358,13 @@ ChannelBufferItem::ChannelBufferItem(const BufferInfo &bufferInfo, AbstractTreeI
   : BufferItem(bufferInfo, parent),
     _ircChannel(0)
 {
+  const Network *net = Client::network(bufferInfo.networkId());
+  if(!net)
+    return;
+
+  IrcChannel *ircChannel = net->ircChannel(bufferInfo.bufferName());
+  if(ircChannel)
+    attachIrcChannel(ircChannel);
 }
 
 QString ChannelBufferItem::toolTip(int column) const {
@@ -325,10 +419,8 @@ void ChannelBufferItem::attachIrcChannel(IrcChannel *ircChannel) {
   connect(ircChannel, SIGNAL(ircUserModeRemoved(IrcUser *, QString)),
          this, SLOT(userModeChanged(IrcUser *)));
 
-  if(!ircChannel->ircUsers().isEmpty()) {
-    qWarning() << "Channel" << ircChannel->name() << "has already users which is quite surprising :)";
+  if(!ircChannel->ircUsers().isEmpty())
     join(ircChannel->ircUsers());
-  }
   
   emit dataChanged();
 }
@@ -341,23 +433,22 @@ void ChannelBufferItem::ircChannelDestroyed() {
   removeAllChilds();
 }
 
-void ChannelBufferItem::ircUserDestroyed() {
-  // PRIVATE
-  IrcUser *ircUser = static_cast<IrcUser *>(sender());
-  removeUserFromCategory(ircUser);
-  emit dataChanged(2);
-}
-
 void ChannelBufferItem::join(const QList<IrcUser *> &ircUsers) {
   addUsersToCategory(ircUsers);
+  emit dataChanged(2);
+}
 
-  foreach(IrcUser *ircUser, ircUsers) {
-    if(!ircUser)
+UserCategoryItem *ChannelBufferItem::findCategoryItem(int categoryId) {
+  UserCategoryItem *categoryItem = 0;
+  
+  for(int i = 0; i < childCount(); i++) {
+    categoryItem = qobject_cast<UserCategoryItem *>(child(i));
+    if(!categoryItem)
       continue;
-    connect(ircUser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
+    if(categoryItem->categoryId() == categoryId)
+      return categoryItem;
   }
-  
-  emit dataChanged(2);
+  return 0;
 }
 
 void ChannelBufferItem::addUserToCategory(IrcUser *ircUser) {
@@ -374,7 +465,7 @@ void ChannelBufferItem::addUsersToCategory(const QList<IrcUser *> &ircUsers) {
   
   foreach(IrcUser *ircUser, ircUsers) {
     categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
-    categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId)));
+    categoryItem = findCategoryItem(categoryId);
     if(!categoryItem) {
       categoryItem = new UserCategoryItem(categoryId, this);
       categories[categoryItem] = QList<IrcUser *>();
@@ -424,11 +515,12 @@ void ChannelBufferItem::userModeChanged(IrcUser *ircUser) {
   Q_ASSERT(_ircChannel);
 
   int categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
-  UserCategoryItem *categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId)));
+  UserCategoryItem *categoryItem = findCategoryItem(categoryId);
     
   if(categoryItem) {
-    if(categoryItem->childById(qHash(ircUser)))
+    if(categoryItem->findIrcUser(ircUser)) {
       return; // already in the right category;
+    }
   } else {
     categoryItem = new UserCategoryItem(categoryId, this);
     newChild(categoryItem);
@@ -437,8 +529,9 @@ void ChannelBufferItem::userModeChanged(IrcUser *ircUser) {
   // find the item that needs reparenting
   IrcUserItem *ircUserItem = 0;
   for(int i = 0; i < childCount(); i++) {
-    UserCategoryItem *categoryItem = qobject_cast<UserCategoryItem *>(child(i));
-    IrcUserItem *userItem = qobject_cast<IrcUserItem *>(categoryItem->childById(qHash(ircUser)));
+    UserCategoryItem *oldCategoryItem = qobject_cast<UserCategoryItem *>(child(i));
+    Q_ASSERT(oldCategoryItem);
+    IrcUserItem *userItem = oldCategoryItem->findIrcUser(ircUser);
     if(userItem) {
       ircUserItem = userItem;
       break;
@@ -463,6 +556,8 @@ UserCategoryItem::UserCategoryItem(int category, AbstractTreeItem *parent)
   : PropertyMapItem(QStringList() << "categoryName", parent),
     _category(category)
 {
+  setTreeItemFlags(AbstractTreeItem::DeleteOnLastChildRemoved);
+  setObjectName(parent->data(0, Qt::DisplayRole).toString() + "/" + QString::number(category));
 }
 
 // caching this makes no sense, since we display the user number dynamically
@@ -478,8 +573,17 @@ QString UserCategoryItem::categoryName() const {
   }
 }
 
-quint64 UserCategoryItem::id() const {
-  return qHash(_category);
+IrcUserItem *UserCategoryItem::findIrcUser(IrcUser *ircUser) {
+  IrcUserItem *userItem = 0;
+
+  for(int i = 0; i < childCount(); i++) {
+    userItem = qobject_cast<IrcUserItem *>(child(i));
+    if(!userItem)
+      continue;
+    if(userItem->ircUser() == ircUser)
+      return userItem;
+  }
+  return 0;
 }
 
 void UserCategoryItem::addUsers(const QList<IrcUser *> &ircUsers) {
@@ -491,9 +595,12 @@ void UserCategoryItem::addUsers(const QList<IrcUser *> &ircUsers) {
 }
 
 bool UserCategoryItem::removeUser(IrcUser *ircUser) {
-  bool success = removeChildById(qHash(ircUser));
-  if(success)
+  IrcUserItem *userItem = findIrcUser(ircUser);
+  bool success = (bool)userItem;
+  if(success) {
+    removeChild(userItem);
     emit dataChanged(0);
+  }
   return success;
 }
 
@@ -530,30 +637,12 @@ QVariant UserCategoryItem::data(int column, int role) const {
 *****************************************/
 IrcUserItem::IrcUserItem(IrcUser *ircUser, AbstractTreeItem *parent)
   : PropertyMapItem(QStringList() << "nickName", parent),
-    _ircUser(ircUser),
-    _id(qHash(ircUser))
+    _ircUser(ircUser)
 {
-  // we don't need to handle the ircUser's destroyed signal since it's automatically removed
-  // by the IrcChannel::ircUserParted();
-  
-  connect(ircUser, SIGNAL(nickSet(QString)),
-         this, SLOT(setNick(QString)));
-  connect(ircUser, SIGNAL(awaySet(bool)),
-          this, SLOT(setAway(bool)));
-}
-
-QString IrcUserItem::nickName() const {
-  if(_ircUser)
-    return _ircUser->nick();
-  else
-    return QString();
-}
-
-bool IrcUserItem::isActive() const {
-  if(_ircUser)
-    return !_ircUser->isAway();
-  else
-    return false;
+  setObjectName(ircUser->nick());  
+  connect(ircUser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
+  connect(ircUser, SIGNAL(nickSet(QString)), this, SIGNAL(dataChanged()));
+  connect(ircUser, SIGNAL(awaySet(bool)), this, SIGNAL(dataChanged()));
 }
 
 QVariant IrcUserItem::data(int column, int role) const {
@@ -604,15 +693,11 @@ QString IrcUserItem::toolTip(int column) const {
   return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
 }
 
-void IrcUserItem::setNick(QString newNick) {
-  Q_UNUSED(newNick);
-  emit dataChanged(0);
-}
-
-void IrcUserItem::setAway(bool away) {
-  Q_UNUSED(away);
-  emit dataChanged(0);
-}
+// void IrcUserItem::ircUserDestroyed() {
+//   parent()->removeChild(this);
+//   if(parent()->childCount() == 0)
+//     parent()->parent()->removeChild(parent());
+// }
 
 /*****************************************
  * NetworkModel
@@ -620,6 +705,10 @@ void IrcUserItem::setAway(bool away) {
 NetworkModel::NetworkModel(QObject *parent)
   : TreeModel(NetworkModel::defaultHeader(), parent)
 {
+  connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+         this, SLOT(checkForNewBuffers(const QModelIndex &, int, int)));
+  connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
+         this, SLOT(checkForRemovedBuffers(const QModelIndex &, int, int)));
 }
 
 QList<QVariant >NetworkModel::defaultHeader() {
@@ -639,49 +728,78 @@ Buffer *NetworkModel::getBufferByIndex(const QModelIndex &index) const {
 }
 */
 
+int NetworkModel::networkRow(NetworkId networkId) {
+  NetworkItem *netItem = 0;
+  for(int i = 0; i < rootItem->childCount(); i++) {
+    netItem = qobject_cast<NetworkItem *>(rootItem->child(i));
+    if(!netItem)
+      continue;
+    if(netItem->networkId() == networkId)
+      return i;
+  }
+  return -1;
+}
 
-// experimental stuff :)
 QModelIndex NetworkModel::networkIndex(NetworkId networkId) {
-  return indexById(qHash(networkId));
+  int netRow = networkRow(networkId);
+  if(netRow == -1)
+    return QModelIndex();
+  else
+    return indexByItem(qobject_cast<NetworkItem *>(rootItem->child(netRow)));
 }
 
-NetworkItem *NetworkModel::existsNetworkItem(NetworkId networkId) {
-  return qobject_cast<NetworkItem *>(rootItem->childById(networkId.toInt()));
+NetworkItem *NetworkModel::findNetworkItem(NetworkId networkId) {
+  int netRow = networkRow(networkId);
+  if(netRow == -1)
+    return 0;
+  else
+    return qobject_cast<NetworkItem *>(rootItem->child(netRow));
 }
 
 NetworkItem *NetworkModel::networkItem(NetworkId networkId) {
-  NetworkItem *netItem = existsNetworkItem(networkId);
+  NetworkItem *netItem = findNetworkItem(networkId);
 
   if(netItem == 0) {
     netItem = new NetworkItem(networkId, rootItem);
     rootItem->newChild(netItem);
   }
-
-  Q_ASSERT(netItem);
   return netItem;
 }
 
 void NetworkModel::networkRemoved(const NetworkId &networkId) {
-  rootItem->removeChildById(qHash(networkId));
+  int netRow = networkRow(networkId);
+  if(netRow != -1) {
+    rootItem->removeChild(netRow);
+  }
 }
 
 QModelIndex NetworkModel::bufferIndex(BufferId bufferId) {
-  AbstractTreeItem *netItem, *bufferItem;
-  for(int i = 0; i < rootItem->childCount(); i++) {
-    netItem = rootItem->child(i);
-    if((bufferItem = netItem->childById(qHash(bufferId)))) {
-      return indexByItem(bufferItem);
-    }
-  }
-  return QModelIndex();
+  BufferItem *bufferItem = findBufferItem(bufferId);
+  if(bufferItem)
+    return indexByItem(bufferItem);
+  else
+    return QModelIndex();
 }
 
-BufferItem *NetworkModel::existsBufferItem(const BufferInfo &bufferInfo) {
-  QModelIndex bufferIdx = bufferIndex(bufferInfo.bufferId());
-  if(bufferIdx.isValid())
-    return static_cast<BufferItem *>(bufferIdx.internalPointer());
-  else
+BufferItem *NetworkModel::findBufferItem(const BufferInfo &bufferInfo) {
+  NetworkItem *netItem = findNetworkItem(bufferInfo.networkId());
+  if(!netItem)
     return 0;
+
+  BufferItem *bufferItem = netItem->findBufferItem(bufferInfo);
+  return bufferItem;
+}
+
+BufferItem *NetworkModel::findBufferItem(BufferId bufferId) {
+  NetworkItem *netItem;
+  BufferItem *bufferItem;
+  
+  for(int i = 0; i < rootItem->childCount(); i++) {
+    netItem = qobject_cast<NetworkItem *>(rootItem->child(i));
+    if((bufferItem = netItem->findBufferItem(bufferId)))
+      return bufferItem;
+  }
+  return 0;
 }
 
 BufferItem *NetworkModel::bufferItem(const BufferInfo &bufferInfo) {
@@ -765,13 +883,15 @@ bool NetworkModel::dropMimeData(const QMimeData *data, Qt::DropAction action, in
   // no self merges (would kill us)
   if(bufferId == parent.data(BufferIdRole).value<BufferId>())
     return false; 
-  
-  Q_ASSERT(rootItem->childById(qHash(netId)));
-  Q_ASSERT(rootItem->childById(qHash(netId))->childById(qHash(bufferId)));
+
+  NetworkItem *netItem = findNetworkItem(netId);
+  Q_ASSERT(netItem);
+
+  BufferItem *bufferItem = netItem->findBufferItem(bufferId);
+  Q_ASSERT(bufferItem);
 
   // source must be a query too
-  BufferInfo::Type sourceType = (BufferInfo::Type)rootItem->childById(qHash(netId))->childById(qHash(bufferId))->data(0, BufferTypeRole).toInt();
-  if(sourceType != BufferInfo::QueryBuffer)
+  if(bufferItem->bufferType() != BufferInfo::QueryBuffer)
     return false;
     
   // TODO: warn user about buffermerge!
@@ -793,14 +913,9 @@ void NetworkModel::bufferUpdated(BufferInfo bufferInfo) {
 }
 
 void NetworkModel::removeBuffer(BufferId bufferId) {
-  const int numNetworks = rootItem->childCount();
-  if(numNetworks == 0)
-    return;
-
-  for(int i = 0; i < numNetworks; i++) {
-    if(rootItem->child(i)->removeChildById(qHash(bufferId)))
-      break;
-  }
+  BufferItem *bufferItem = findBufferItem(bufferId);
+  if(bufferItem)
+    bufferItem->parent()->removeChild(bufferItem);
 }
 
 /*
@@ -840,3 +955,59 @@ const Network *NetworkModel::networkByIndex(const QModelIndex &index) const {
   return Client::network(networkId);
 }
 
+
+
+void NetworkModel::checkForRemovedBuffers(const QModelIndex &parent, int start, int end) {
+  if(parent.data(ItemTypeRole) == NetworkItemType)
+    return;
+  
+  for(int row = start; row < end; row++) {
+    _bufferItemCache.remove(parent.child(row, 0).data(BufferIdRole).value<BufferId>());
+  }
+}
+
+void NetworkModel::checkForNewBuffers(const QModelIndex &parent, int start, int end) {
+  if(parent.data(ItemTypeRole) == NetworkItemType)
+    return;
+  
+  for(int row = start; row < end; row++) {
+    QModelIndex child = parent.child(row, 0);
+    _bufferItemCache[child.data(BufferIdRole).value<BufferId>()] = static_cast<BufferItem *>(child.internalPointer());
+  }
+}
+
+QString NetworkModel::bufferName(BufferId bufferId) {
+  if(!_bufferItemCache.contains(bufferId))
+    return QString();
+
+  return _bufferItemCache[bufferId]->bufferName();
+}
+
+NetworkId NetworkModel::networkId(BufferId bufferId) {
+  if(!_bufferItemCache.contains(bufferId))
+    return NetworkId();
+
+  NetworkItem *netItem = qobject_cast<NetworkItem *>(_bufferItemCache[bufferId]->parent());
+  if(netItem)
+    return netItem->networkId();
+  else
+    return NetworkId();
+}
+
+QString NetworkModel::networkName(BufferId bufferId) {
+  if(!_bufferItemCache.contains(bufferId))
+    return QString();
+
+  NetworkItem *netItem = qobject_cast<NetworkItem *>(_bufferItemCache[bufferId]->parent());
+  if(netItem)
+    return netItem->networkName();
+  else
+    return QString();
+}
+
+BufferInfo::Type NetworkModel::bufferType(BufferId bufferId) {
+  if(!_bufferItemCache.contains(bufferId))
+    return BufferInfo::InvalidBuffer;
+
+  return _bufferItemCache[bufferId]->bufferType();
+}