Merge branch 'network-sync'
authorMarcus Eggenberger <egs@quassel-irc.org>
Sat, 12 Jul 2008 13:21:49 +0000 (15:21 +0200)
committerMarcus Eggenberger <egs@quassel-irc.org>
Sat, 12 Jul 2008 13:21:49 +0000 (15:21 +0200)
1  2 
src/client/networkmodel.cpp
src/common/network.cpp
src/common/network.h

  #include "util.h" // get rid of this (needed for isChannelName)
  
  /*****************************************
 -*  Fancy Buffer Items
 +*  Network Items
  *****************************************/
 -BufferItem::BufferItem(BufferInfo bufferInfo, AbstractTreeItem *parent)
 -  : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent),
 -    _bufferInfo(bufferInfo),
 -    _bufferName(bufferInfo.bufferName()),
 -    _activity(Buffer::NoActivity),
 -    _ircChannel(0)
 +NetworkItem::NetworkItem(const NetworkId &netid, AbstractTreeItem *parent)
 +  : PropertyMapItem(QList<QString>() << "networkName" << "currentServer" << "nickCount", parent),
 +    _networkId(netid)
  {
 -  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
 -  if(bufferType() == BufferInfo::QueryBuffer)
 -    flags |= Qt::ItemIsDropEnabled;
 +  setFlags(Qt::ItemIsEnabled);
 +}
  
 -  if(bufferType() == BufferInfo::StatusBuffer) {
 -    NetworkItem *networkItem = qobject_cast<NetworkItem *>(parent);
 -    connect(networkItem, SIGNAL(dataChanged()), this, SIGNAL(dataChanged()));
 +QVariant NetworkItem::data(int column, int role) const {
 +  switch(role) {
 +  case NetworkModel::NetworkIdRole:
 +    return qVariantFromValue(_networkId);
 +  case NetworkModel::ItemTypeRole:
 +    return NetworkModel::NetworkItemType;
 +  case NetworkModel::ItemActiveRole:
 +    return isActive();
 +  default:
 +    return PropertyMapItem::data(column, role);
    }
 -  setFlags(flags);
  }
  
 -quint64 BufferItem::id() const {
 -  return qHash(bufferInfo().bufferId());
 +BufferItem *NetworkItem::bufferItem(const BufferInfo &bufferInfo) {
 +  BufferItem *bufferItem = qobject_cast<BufferItem *>(childById(qHash(bufferInfo.bufferId())));
 +  if(bufferItem)
 +    return bufferItem;
 +  
 +  switch(bufferInfo.type()) {
 +  case BufferInfo::StatusBuffer:
 +    bufferItem = new StatusBufferItem(bufferInfo, this);
 +    break;
 +  case BufferInfo::ChannelBuffer:
 +    bufferItem = new ChannelBufferItem(bufferInfo, this);
 +    break;
 +  case BufferInfo::QueryBuffer:
 +    bufferItem = new QueryBufferItem(bufferInfo, this);
 +    break;
 +  default:
 +    bufferItem = new BufferItem(bufferInfo, this);
 +  }
 +
 +  newChild(bufferItem);
 +  return bufferItem;
  }
  
 -// bool BufferItem::isStatusBuffer() const {
 -//   return bufferType() == BufferInfo::StatusBuffer;
 -// }
 +void NetworkItem::attachNetwork(Network *network) {
 +  if(!network)
 +    return;
 +  
 +  _network = network;
  
 -bool BufferItem::isActive() const {
 -  if(bufferType() == BufferInfo::ChannelBuffer)
 -    return _ircChannel;
 -  else
 -    return qobject_cast<NetworkItem *>(parent())->isActive();
 +  connect(network, SIGNAL(networkNameSet(QString)),
 +        this, SLOT(setNetworkName(QString)));
 +  connect(network, SIGNAL(currentServerSet(QString)),
 +        this, SLOT(setCurrentServer(QString)));
 +  connect(network, SIGNAL(ircChannelAdded(IrcChannel *)),
 +        this, SLOT(attachIrcChannel(IrcChannel *)));
 +  connect(network, SIGNAL(connectedSet(bool)),
 +        this, SIGNAL(dataChanged()));
 +  connect(network, SIGNAL(destroyed()),
 +        this, SIGNAL(dataChanged()));
 +  
 +  emit dataChanged();
 +}
 +
 +void NetworkItem::attachIrcChannel(IrcChannel *ircChannel) {
 +  ChannelBufferItem *channelItem;
 +  for(int i = 0; i < childCount(); i++) {
 +    channelItem = qobject_cast<ChannelBufferItem *>(child(i));
 +    if(!channelItem)
 +      continue;
 +
 +    if(channelItem->bufferName().toLower() == ircChannel->name().toLower()) {
 +      channelItem->attachIrcChannel(ircChannel);
 +      break;
 +    }
 +  }
 +}
 +
 +void NetworkItem::setNetworkName(const QString &networkName) {
 +  Q_UNUSED(networkName);
 +  emit dataChanged(0);
 +}
 +
 +void NetworkItem::setCurrentServer(const QString &serverName) {
 +  Q_UNUSED(serverName);
 +  emit dataChanged(1);
 +}
 +
 +
 +QString NetworkItem::toolTip(int column) const {
 +  Q_UNUSED(column);
 +
 +  QStringList toolTip(QString("<b>%1</b>").arg(networkName()));
 +  toolTip.append(QString("Server: %1").arg(currentServer()));
 +  toolTip.append(QString("Users: %1").arg(nickCount()));
 +
 +  return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
 +}
 +
 +/*****************************************
 +*  Fancy Buffer Items
 +*****************************************/
 +BufferItem::BufferItem(const BufferInfo &bufferInfo, AbstractTreeItem *parent)
 +  : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent),
 +    _bufferInfo(bufferInfo),
 +    _activity(Buffer::NoActivity)
 +{
 +  setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
  }
  
  void BufferItem::setActivityLevel(Buffer::ActivityLevel level) {
@@@ -180,7 -104,6 +180,7 @@@ QVariant BufferItem::data(int column, i
  }
  
  bool BufferItem::setData(int column, const QVariant &value, int role) {
 +  qDebug() << "BufferItem::setData(int column, const QVariant &value, int role):" << this << column << value << role;
    switch(role) {
    case NetworkModel::BufferActivityRole:
      setActivityLevel((Buffer::ActivityLevel)value.toInt());
    return true;
  }
  
 +void BufferItem::setBufferName(const QString &name) {
 +  _bufferInfo = BufferInfo(_bufferInfo.bufferId(), _bufferInfo.networkId(), _bufferInfo.type(), _bufferInfo.groupId(), name);
 +  emit dataChanged(0);
 +}
  
 -void BufferItem::attachIrcChannel(IrcChannel *ircChannel) {
 -  if(!ircChannel)
 -    return;
 +QString BufferItem::toolTip(int column) const {
 +  Q_UNUSED(column);
 +  return tr("<p> %1 - %2 </p>").arg(bufferInfo().bufferId().toInt()).arg(bufferName());
 +}
 +
 +/*
 +void BufferItem::setLastMsgInsert(QDateTime msgDate) {
 +  if(msgDate.isValid() && msgDate > _lastMsgInsert)
 +    _lastMsgInsert = msgDate;
 +}
 +*/
 +/*
 +// FIXME emit dataChanged()
 +bool BufferItem::setLastSeen() {
 +  if(_lastSeen > _lastMsgInsert)
 +    return false;
 +  
 +  _lastSeen = _lastMsgInsert;
 +  BufferSettings(bufferInfo().bufferId()).setLastSeen(_lastSeen);
 +  return true;
 +}
 +
 +QDateTime BufferItem::lastSeen() {
 +  return _lastSeen;
 +}
 +*/
 +
 +/*****************************************
 +*  StatusBufferItem
 +*****************************************/
 +StatusBufferItem::StatusBufferItem(const BufferInfo &bufferInfo, NetworkItem *parent)
 +  : BufferItem(bufferInfo, parent)
 +{
 +  Q_ASSERT(parent);
 +  connect(parent, SIGNAL(dataChanged()), this, SIGNAL(dataChanged()));
 +}
 +
 +QString StatusBufferItem::toolTip(int column) const {
 +  Q_UNUSED(column);
 +  QStringList toolTip;
 +
 +  QString netName = Client::network(bufferInfo().networkId())->networkName();
 +  toolTip.append(tr("<b>Status buffer of %1</b>").arg(netName));
 +
 +  return tr("<p> %1 </p>").arg(toolTip.join("<br />"));
 +}
 +
 +/*****************************************
 +*  QueryBufferItem
 +*****************************************/
 +QueryBufferItem::QueryBufferItem(const BufferInfo &bufferInfo, NetworkItem *parent)
 +  : BufferItem(bufferInfo, parent)
 +{
 +  setFlags(flags() | Qt::ItemIsDropEnabled);
 +}
 +
 +QString QueryBufferItem::toolTip(int column) const {
 +  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()));
 +  }
 +
 +  return tr("<p> %1 </p>").arg(toolTip.join("<br />"));
 +}
 +
 +/*****************************************
 +*  ChannelBufferItem
 +*****************************************/
 +ChannelBufferItem::ChannelBufferItem(const BufferInfo &bufferInfo, AbstractTreeItem *parent)
 +  : BufferItem(bufferInfo, parent),
 +    _ircChannel(0)
 +{
 +}
 +
 +QString ChannelBufferItem::toolTip(int column) const {
 +  Q_UNUSED(column);
 +  QStringList toolTip;
 +
 +  toolTip.append(tr("<b>Channel %1</b>").arg(bufferName()));
 +  if(isActive()) {
 +    //TODO: add channel modes 
 +    toolTip.append(tr("<b>Users:</b> %1").arg(nickCount()));
 +    if(_ircChannel) {
 +      QString channelMode = _ircChannel->channelModeString(); // channelModeString is compiled on the fly -> thus cache the result
 +      if(!channelMode.isEmpty())
 +      toolTip.append(tr("<b>Mode:</b> %1").arg(channelMode));
 +    }
 +    
 +    BufferSettings s;
 +    bool showTopic = s.value("DisplayTopicInTooltip", QVariant(false)).toBool();
 +    if(showTopic) {
 +      QString _topic = topic();
 +      if(_topic != "") {
 +      _topic.replace(QString("<"), QString("&lt;"));
 +      _topic.replace(QString(">"), QString("&gt;"));
 +      toolTip.append(QString("<font size='-2'>&nbsp;</font>"));
 +      toolTip.append(tr("<b>Topic:</b> %1").arg(_topic));
 +      }
 +    }
 +  } else {
 +    toolTip.append(tr("Not active <br /> Double-click to join"));
 +  }
 +
 +  return tr("<p> %1 </p>").arg(toolTip.join("<br />"));  
 +}
 +
 +void ChannelBufferItem::attachIrcChannel(IrcChannel *ircChannel) {
 +  Q_ASSERT(!_ircChannel && ircChannel);
    
    _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();
  }
  
 -void BufferItem::ircChannelDestroyed() {
 +void ChannelBufferItem::ircChannelDestroyed() {
    Q_CHECK_PTR(_ircChannel);
    disconnect(_ircChannel, 0, this, 0);
    _ircChannel = 0;
    removeAllChilds();
  }
  
 -QString BufferItem::bufferName() const {
 -  if(bufferType() == BufferInfo::StatusBuffer)
 -    return tr("Status Buffer");
 -  else
 -    return _bufferName;
 -}
 -
 -void BufferItem::setBufferName(const QString &name) {
 -  _bufferName = name;
 -  // as long as we need those bufferInfos, we have to update that one aswell.
 -  // pretty ugly though :/
 -  _bufferInfo = BufferInfo(_bufferInfo.bufferId(), _bufferInfo.networkId(), _bufferInfo.type(), _bufferInfo.groupId(), name);
 -  emit dataChanged(0);
 -}
 -
 -QString BufferItem::topic() const {
 -  if(_ircChannel)
 -    return _ircChannel->topic();
 -  else
 -    return QString();
 -}
 -
 -void BufferItem::ircUserDestroyed() {
 +void ChannelBufferItem::ircUserDestroyed() {
    // PRIVATE
    IrcUser *ircUser = static_cast<IrcUser *>(sender());
    removeUserFromCategory(ircUser);
    emit dataChanged(2);
  }
  
 -int BufferItem::nickCount() const {
 -  if(_ircChannel)
 -    return _ircChannel->ircUsers().count();
 -  else
 -    return 0;
 -}
 -
 -void BufferItem::setTopic(const QString &topic) {
 -  Q_UNUSED(topic);
 -  emit dataChanged(1);
 -}
 -
 -void BufferItem::join(const QList<IrcUser *> &ircUsers) {
 +void ChannelBufferItem::join(const QList<IrcUser *> &ircUsers) {
    addUsersToCategory(ircUsers);
  
    foreach(IrcUser *ircUser, ircUsers) {
    emit dataChanged(2);
  }
  
 -void BufferItem::addUserToCategory(IrcUser *ircUser) {
 +void ChannelBufferItem::addUserToCategory(IrcUser *ircUser) {
    addUsersToCategory(QList<IrcUser *>() << ircUser);
  }
  
 -void BufferItem::addUsersToCategory(const QList<IrcUser *> &ircUsers) {
 +void ChannelBufferItem::addUsersToCategory(const QList<IrcUser *> &ircUsers) {
    Q_ASSERT(_ircChannel);
  
    QHash<UserCategoryItem *, QList<IrcUser *> > categories;
 +
 +  int categoryId = -1;
 +  UserCategoryItem *categoryItem = 0;
 +  
    foreach(IrcUser *ircUser, ircUsers) {
 -    UserCategoryItem *categoryItem;
 -    int categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
 -    if(!(categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId))))) {
 +    categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
 +    categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId)));
 +    if(!categoryItem) {
        categoryItem = new UserCategoryItem(categoryId, this);
        categories[categoryItem] = QList<IrcUser *>();
        newChild(categoryItem);
    }
  }
  
 -void BufferItem::part(IrcUser *ircUser) {
 +void ChannelBufferItem::part(IrcUser *ircUser) {
    if(!ircUser) {
 -    qWarning() << bufferName() << "BufferItem::part(): unknown User" << ircUser;
 +    qWarning() << bufferName() << "ChannelBufferItem::part(): unknown User" << ircUser;
      return;
    }
  
    emit dataChanged(2);
  }
  
 -void BufferItem::removeUserFromCategory(IrcUser *ircUser) {
 +void ChannelBufferItem::removeUserFromCategory(IrcUser *ircUser) {
    if(!_ircChannel) {
      // If we parted the channel there might still be some ircUsers connected.
      // in that case we just ignore the call
      return;
    }
  
 -  bool success = false;
    UserCategoryItem *categoryItem = 0;
    for(int i = 0; i < childCount(); i++) {
      categoryItem = qobject_cast<UserCategoryItem *>(child(i));
 -    if((success = categoryItem->removeUser(ircUser))) {
 +    if(categoryItem->removeUser(ircUser)) {
        if(categoryItem->childCount() == 0)
        removeChild(i);
        break;
      }
    }
 -
 -//   if(!success) {
 -//     qDebug() << "didn't find User:" << ircUser << qHash(ircUser);
 -//     qDebug() << "==== Childlist for Item:" << this << id() << bufferName() << "====";
 -//     for(int i = 0; i < childCount(); i++) {
 -//       categoryItem = qobject_cast<UserCategoryItem *>(child(i));
 -//       categoryItem->dumpChildList();
 -//     }
 -//     qDebug() << "==== End Of Childlist for Item:" << this << id() << bufferName() << "====";
 -//   }
 -//   Q_ASSERT(success);
  }
  
 -void BufferItem::userModeChanged(IrcUser *ircUser) {
 +void ChannelBufferItem::userModeChanged(IrcUser *ircUser) {
    Q_ASSERT(_ircChannel);
  
 -  UserCategoryItem *categoryItem;
    int categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
 -  if((categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId)))) && categoryItem->childById(qHash(ircUser)))
 -    return; // already in the right category;
 -  
 -  removeUserFromCategory(ircUser);
 -  addUserToCategory(ircUser);
 -}
 -
 -QString BufferItem::toolTip(int column) const {
 -  Q_UNUSED(column);
 -  QStringList toolTip;
 -
 -  switch(bufferType()) {
 -    case BufferInfo::StatusBuffer: {
 -      QString netName = Client::network(bufferInfo().networkId())->networkName();
 -      toolTip.append(tr("<b>Status buffer of %1</b>").arg(netName));
 -      break;
 -    }
 -    case BufferInfo::ChannelBuffer:
 -      toolTip.append(tr("<b>Channel %1</b>").arg(bufferName()));
 -      if(isActive()) {
 -        //TODO: add channel modes 
 -        toolTip.append(tr("<b>Users:</b> %1").arg(nickCount()));
 -
 -        BufferSettings s;
 -        bool showTopic = s.value("DisplayTopicInTooltip", QVariant(false)).toBool();
 -        if(showTopic) {
 -          QString _topic = topic();
 -          if(_topic != "") {
 -            _topic.replace(QString("<"), QString("&lt;"));
 -            _topic.replace(QString(">"), QString("&gt;"));
 -            toolTip.append(QString("<font size='-2'>&nbsp;</font>"));
 -            toolTip.append(tr("<b>Topic:</b> %1").arg(_topic));
 -          }
 -        }
 -      if(_ircChannel) {
 -        QString channelMode = _ircChannel->channelModeString(); // channelModeString is compiled on the fly -> thus cache the result
 -        if(!channelMode.isEmpty())
 -          toolTip.append(tr("<b>Mode:</b> %1").arg(channelMode));
 -      }
 -      } else {
 -        toolTip.append(tr("Not active <br /> Double-click to join"));
 -      }
 -      break;
 -    case BufferInfo::QueryBuffer:
 -      toolTip.append(tr("<b>Query with %1</b>").arg(bufferName()));
 -      if(topic() != "") toolTip.append(tr("Away Message: %1").arg(topic()));
 -      break;
 -    default: //this should not happen
 -      toolTip.append(tr("%1 - %2").arg(bufferInfo().bufferId().toInt()).arg(bufferName()));
 -      break;
 -  }
 -
 -  return tr("<p> %1 </p>").arg(toolTip.join("<br />"));
 -}
 -
 -/*
 -void BufferItem::setLastMsgInsert(QDateTime msgDate) {
 -  if(msgDate.isValid() && msgDate > _lastMsgInsert)
 -    _lastMsgInsert = msgDate;
 -}
 -*/
 -/*
 -// FIXME emit dataChanged()
 -bool BufferItem::setLastSeen() {
 -  if(_lastSeen > _lastMsgInsert)
 -    return false;
 -  
 -  _lastSeen = _lastMsgInsert;
 -  BufferSettings(bufferInfo().bufferId()).setLastSeen(_lastSeen);
 -  return true;
 -}
 -
 -QDateTime BufferItem::lastSeen() {
 -  return _lastSeen;
 -}
 -*/
 -/*****************************************
 -*  Network Items
 -*****************************************/
 -NetworkItem::NetworkItem(const NetworkId &netid, AbstractTreeItem *parent)
 -  : PropertyMapItem(QList<QString>() << "networkName" << "currentServer" << "nickCount", parent),
 -    _networkId(netid)
 -{
 -  setFlags(Qt::ItemIsEnabled);
 -}
 -
 -QVariant NetworkItem::data(int column, int role) const {
 -  switch(role) {
 -  case NetworkModel::NetworkIdRole:
 -    return qVariantFromValue(_networkId);
 -  case NetworkModel::ItemTypeRole:
 -    return NetworkModel::NetworkItemType;
 -  case NetworkModel::ItemActiveRole:
 -    return isActive();
 -  default:
 -    return PropertyMapItem::data(column, role);
 +  UserCategoryItem *categoryItem = qobject_cast<UserCategoryItem *>(childById(qHash(categoryId)));
 +    
 +  if(categoryItem) {
 +    if(categoryItem->childById(qHash(ircUser)))
 +      return; // already in the right category;
 +  } else {
 +    categoryItem = new UserCategoryItem(categoryId, this);
 +    newChild(categoryItem);
    }
 -}
 -
 -quint64 NetworkItem::id() const {
 -  return qHash(_networkId);
 -}
 -
 -bool NetworkItem::isActive() const {
 -  if(_network)
 -    return _network->isConnected();
 -  else
 -    return false;
 -}
 -
 -QString NetworkItem::networkName() const {
 -  if(_network)
 -    return _network->networkName();
 -  else
 -    return QString();
 -}
 -
 -QString NetworkItem::currentServer() const {
 -  if(_network)
 -    return _network->currentServer();
 -  else
 -    return QString();
 -}
 -
 -int NetworkItem::nickCount() const {
 -  if(_network)
 -    return _network->ircUsers().count();
 -  else
 -    return 0;
 -}
  
 -void NetworkItem::attachNetwork(Network *network) {
 -  if(!network)
 -    return;
 -  
 -  _network = network;
 -
 -  connect(network, SIGNAL(networkNameSet(QString)),
 -        this, SLOT(setNetworkName(QString)));
 -  connect(network, SIGNAL(currentServerSet(QString)),
 -        this, SLOT(setCurrentServer(QString)));
 -  connect(network, SIGNAL(ircChannelAdded(QString)),
 -        this, SLOT(attachIrcChannel(QString)));
 -  connect(network, SIGNAL(connectedSet(bool)),
 -        this, SIGNAL(dataChanged()));
 -  connect(network, SIGNAL(destroyed()),
 -        this, SIGNAL(dataChanged()));
 -  
 -  emit dataChanged();
 -}
 -
 -void NetworkItem::attachIrcChannel(const QString &channelName) {
 -  IrcChannel *ircChannel = _network->ircChannel(channelName);
 -  if(!ircChannel) {
 -    qWarning() << "NetworkItem::attachIrcChannel(): unkown Channel" << channelName;
 -    return;
 -  }
 -  
 -  BufferItem *bufferItem;
 +  // find the item that needs reparenting
 +  IrcUserItem *ircUserItem = 0;
    for(int i = 0; i < childCount(); i++) {
 -    bufferItem = qobject_cast<BufferItem *>(child(i));
 -    if(bufferItem->bufferName().toLower() == ircChannel->name().toLower()) {
 -      bufferItem->attachIrcChannel(ircChannel);
 +    UserCategoryItem *categoryItem = qobject_cast<UserCategoryItem *>(child(i));
 +    IrcUserItem *userItem = qobject_cast<IrcUserItem *>(categoryItem->childById(qHash(ircUser)));
 +    if(userItem) {
 +      ircUserItem = userItem;
        break;
      }
    }
 -}
  
 -void NetworkItem::setNetworkName(const QString &networkName) {
 -  Q_UNUSED(networkName);
 -  emit dataChanged(0);
 -}
 -
 -void NetworkItem::setCurrentServer(const QString &serverName) {
 -  Q_UNUSED(serverName);
 -  emit dataChanged(1);
 -}
 -
 -
 -QString NetworkItem::toolTip(int column) const {
 -  Q_UNUSED(column);
 -
 -  QStringList toolTip(QString("<b>%1</b>").arg(networkName()));
 -  toolTip.append(QString("Server: %1").arg(currentServer()));
 -  toolTip.append(QString("Users: %1").arg(nickCount()));
 -
 -  return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
 +  if(!ircUserItem) {
 +    qWarning() << "ChannelBufferItem::userModeChanged(IrcUser *): unable to determine old category of" << ircUser;
 +    return;
 +  }
 +  ircUserItem->reParent(categoryItem);
  }
  
 -
  /*****************************************
  *  User Category Items (like @vh etc.)
  *****************************************/
@@@ -463,6 -477,7 +461,6 @@@ UserCategoryItem::UserCategoryItem(int 
    : PropertyMapItem(QStringList() << "categoryName", parent),
      _category(category)
  {
 -
  }
  
  // caching this makes no sense, since we display the user number dynamically
@@@ -487,14 -502,10 +485,14 @@@ void UserCategoryItem::addUsers(const Q
    foreach(IrcUser *ircUser, ircUsers)
      userItems << new IrcUserItem(ircUser, this);
    newChilds(userItems);
 +  emit dataChanged(0);
  }
  
  bool UserCategoryItem::removeUser(IrcUser *ircUser) {
 -  return removeChildById(qHash(ircUser));
 +  bool success = removeChildById(qHash(ircUser));
 +  if(success)
 +    emit dataChanged(0);
 +  return success;
  }
  
  int UserCategoryItem::categoryFromModes(const QString &modes) {
@@@ -577,11 -588,8 +575,11 @@@ QString IrcUserItem::toolTip(int column
    Q_UNUSED(column);
    QStringList toolTip(QString("<b>%1</b>").arg(nickName()));
    if(_ircUser->userModes() != "") toolTip[0].append(QString(" (%1)").arg(_ircUser->userModes()));
 -  if(_ircUser->isAway()) toolTip[0].append(" is away");
 -  if(!_ircUser->awayMessage().isEmpty()) toolTip[0].append(QString(" (%1)").arg(_ircUser->awayMessage()));
 +  if(_ircUser->isAway()) {
 +    toolTip[0].append(" is away");
 +    if(!_ircUser->awayMessage().isEmpty())
 +      toolTip[0].append(QString(" (%1)").arg(_ircUser->awayMessage()));
 +  }
    if(!_ircUser->realName().isEmpty()) toolTip.append(_ircUser->realName());
    if(!_ircUser->ircOperator().isEmpty()) toolTip.append(QString("%1 %2").arg(nickName()).arg(_ircUser->ircOperator()));
    if(!_ircUser->suserHost().isEmpty()) toolTip.append(_ircUser->suserHost());
@@@ -685,8 -693,15 +683,8 @@@ BufferItem *NetworkModel::existsBufferI
  }
  
  BufferItem *NetworkModel::bufferItem(const BufferInfo &bufferInfo) {
 -  BufferItem *bufItem = existsBufferItem(bufferInfo);
 -  if(bufItem == 0) {
 -    NetworkItem *netItem = networkItem(bufferInfo.networkId());
 -    bufItem = new BufferItem(bufferInfo, netItem);
 -    netItem->newChild(bufItem);
 -  }
 -
 -  Q_ASSERT(bufItem);
 -  return bufItem;
 +  NetworkItem *netItem = networkItem(bufferInfo.networkId());
 +  return netItem->bufferItem(bufferInfo);
  }
  
  QStringList NetworkModel::mimeTypes() const {
diff --combined src/common/network.cpp
@@@ -208,7 -208,6 +208,7 @@@ void Network::ircUserDestroyed() 
    QHash<QString, IrcUser *>::iterator ircUserIter = _ircUsers.begin();
    while(ircUserIter != _ircUsers.end()) {
      if(ircUser == *ircUserIter) {
 +      emit deletedIrcUserRemoved(ircUserIter.key());
        ircUserIter = _ircUsers.erase(ircUserIter);
        break;
      }
    }
  }
  
 +void Network::removeDeletedIrcUser(const QString &username) {
 +  // DO NOT CALL THIS SLOT EVER!!!
 +  
 +  // this slots purpose is only to remove deleted users that haven't been synced yet.
 +  // Reason:
 +  // as a user parting a channel results in it's deletion if it is no longer in any known channel
 +  // this action can only be communicated if the slaves are allready in sync.
 +  // so if such a deleted user isn't synced in slave mode, we kill and remove it.
 +
 +  Q_ASSERT(proxy());
 +
 +  if(!_ircUsers.contains(username))
 +    return;
 +
 +  IrcUser *ircUser = _ircUsers[username];
 +
 +  if(ircUser->isInitialized())
 +    return;
 +
 +  _ircUsers.remove(username);
 +  emit ircUserRemoved(username);
 +  emit ircUserRemoved(ircUser);
 +}
 +
  void Network::removeIrcUser(IrcUser *ircuser) {
    QString nick = _ircUsers.key(ircuser);
    if(nick.isNull())
@@@ -538,22 -513,86 +538,86 @@@ QVariantMap Network::initSupports() con
    return supports;
  }
  
- QStringList Network::initIrcUsers() const {
-   QStringList hostmasks;
-   foreach(IrcUser *ircuser, ircUsers()) {
-     hostmasks << ircuser->hostmask();
+ QVariantMap Network::initIrcUsersAndChannels() const {
+   QVariantMap usersAndChannels;
+   QVariantMap users;
+   QVariantMap channels;
+   QHash<QString, IrcUser *>::const_iterator userIter = _ircUsers.constBegin();
+   QHash<QString, IrcUser *>::const_iterator userIterEnd = _ircUsers.constEnd();
+   while(userIter != userIterEnd) {
+     users[userIter.value()->hostmask()] = userIter.value()->toVariantMap();
+     userIter++;
    }
-   return hostmasks;
+   usersAndChannels["users"] = users;
+   QHash<QString, IrcChannel *>::const_iterator channelIter = _ircChannels.constBegin();
+   QHash<QString, IrcChannel *>::const_iterator channelIterEnd = _ircChannels.constEnd();
+   while(channelIter != channelIterEnd) {
+     channels[channelIter.key()] = channelIter.value()->toVariantMap();
+     channelIter++;
+   }
+   usersAndChannels["channels"] = channels;
+   return usersAndChannels;
  }
  
- QStringList Network::initIrcChannels() const {
-   QStringList channels;
-   QHash<QString, IrcChannel *>::const_iterator iter = _ircChannels.constBegin();
-   while(iter != _ircChannels.constEnd()) {
-     channels << iter.value()->name();
-     iter++;
+ void Network::initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels) {
+   Q_ASSERT(proxy());
+   if(!_ircUsers.isEmpty() || !_ircChannels.isEmpty()) {
+     qWarning() << "Network" << networkId() << "received init data for users and channels allthough there allready are known users or channels!";
+     return;
+   }
+     
+   QVariantMap users = usersAndChannels.value("users").toMap();
+   QVariantMap::const_iterator userIter = users.constBegin();
+   QVariantMap::const_iterator userIterEnd = users.constEnd();
+   IrcUser *ircUser = 0;
+   QString hostmask;
+   while(userIter != userIterEnd) {
+     hostmask = userIter.key();
+     ircUser = new IrcUser(hostmask, this);
+     ircUser->fromVariantMap(userIter.value().toMap());
+     ircUser->setInitialized();
+     proxy()->synchronize(ircUser);
+     connect(ircUser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickChanged(QString)));
+     connect(ircUser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
+     _ircUsers[nickFromMask(hostmask).toLower()] = ircUser;
+     emit ircUserAdded(hostmask);
+     emit ircUserAdded(ircUser);
+     emit ircUserInitDone(ircUser);
+     userIter++;
    }
-   return channels;
+   QVariantMap channels = usersAndChannels.value("channels").toMap();
+   QVariantMap::const_iterator channelIter = channels.constBegin();
+   QVariantMap::const_iterator channelIterEnd = channels.constEnd();
+   IrcChannel *ircChannel = 0;
+   QString channelName;
+   while(channelIter != channelIterEnd) {
+     channelName = channelIter.key();
+     ircChannel = new IrcChannel(channelName, this);
+     ircChannel->fromVariantMap(channelIter.value().toMap());
+     ircChannel->setInitialized();
+     proxy()->synchronize(ircChannel);
+       
+     connect(ircChannel, SIGNAL(destroyed()), this, SLOT(channelDestroyed()));
+     _ircChannels[channelName.toLower()] = ircChannel;
+     emit ircChannelAdded(channelName);
+     emit ircChannelAdded(ircChannel);
+     emit ircChannelInitDone(ircChannel);
+     channelIter++;
+   }
+   
  }
  
  void Network::initSetSupports(const QVariantMap &supports) {
    }
  }
  
- void Network::initSetIrcUsers(const QStringList &hostmasks) {
-   if(!_ircUsers.empty())
-     return;
-   foreach(QString hostmask, hostmasks) {
-     newIrcUser(hostmask);
-   }
- }
- void Network::initSetIrcChannels(const QStringList &channels) {
-   if(!_ircChannels.empty())
-     return;
-   foreach(QString channel, channels)
-     newIrcChannel(channel);
- }
  IrcUser *Network::updateNickFromMask(const QString &mask) {
    QString nick(nickFromMask(mask).toLower());
    IrcUser *ircuser;
@@@ -607,13 -631,15 +656,15 @@@ void Network::ircUserNickChanged(QStrin
  void Network::ircUserInitDone() {
    IrcUser *ircuser = static_cast<IrcUser *>(sender());
    Q_ASSERT(ircuser);
+   connect(ircuser, SIGNAL(initDone()), this, SLOT(ircUserInitDone()));
    emit ircUserInitDone(ircuser);
  }
  
  void Network::ircChannelInitDone() {
-   IrcChannel *ircchannel = static_cast<IrcChannel *>(sender());
-   Q_ASSERT(ircchannel);
-   emit ircChannelInitDone(ircchannel);
+   IrcChannel *ircChannel = static_cast<IrcChannel *>(sender());
+   Q_ASSERT(ircChannel);
+   disconnect(ircChannel, SIGNAL(initDone()), this, SLOT(ircChannelInitDone()));
+   emit ircChannelInitDone(ircChannel);
  }
  
  void Network::removeIrcChannel(IrcChannel *channel) {
diff --combined src/common/network.h
@@@ -204,21 -204,22 +204,23 @@@ public slots
  
    inline void addIrcUser(const QString &hostmask) { newIrcUser(hostmask); }
    inline void addIrcChannel(const QString &channel) { newIrcChannel(channel); }
 +  void removeDeletedIrcUser(const QString &username);
    void removeIrcUser(const QString &nick);
    void removeIrcChannel(const QString &channel);
  
    //init geters
    QVariantMap initSupports() const;
    inline QVariantList initServerList() const { return serverList(); }
-   QStringList initIrcUsers() const;
-   QStringList initIrcChannels() const;
+   virtual QVariantMap initIrcUsersAndChannels() const;
+ //   QStringList initIrcUsers() const;
+ //   QStringList initIrcChannels() const;
    
    //init seters
    void initSetSupports(const QVariantMap &supports);
    inline void initSetServerList(const QVariantList &serverList) { setServerList(serverList); }
-   void initSetIrcUsers(const QStringList &hostmasks);
-   void initSetIrcChannels(const QStringList &channels);
+   virtual void initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels);
+ //   void initSetIrcUsers(const QStringList &hostmasks);
+ //   void initSetIrcChannels(const QStringList &channels);
    
    IrcUser *updateNickFromMask(const QString &mask);
  
@@@ -275,7 -276,6 +277,7 @@@ signals
    void ircChannelAdded(const QString &channelname);
    void ircChannelAdded(IrcChannel *);
  
 +  void deletedIrcUserRemoved(const QString &username);
    void ircUserRemoved(const QString &nick);
    void ircChannelRemoved(const QString &channel);