- BufferWidget behaves now like a view
[quassel.git] / src / client / treemodel.cpp
index a2d1183..0dc19b7 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-07 by the Quassel IRC Team                         *
+ *   Copyright (C) 2005-08 by the Quassel Project                          *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *****************************************/
 AbstractTreeItem::AbstractTreeItem(AbstractTreeItem *parent)
   : QObject(parent),
-    _parentItem(parent),
     _flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled)
 {
 }
 
 AbstractTreeItem::~AbstractTreeItem() {
-  foreach(int key, _childItems.keys()) {
-    qDeleteAll(_childItems[key]);
-  }
+  removeAllChilds();
 }
 
 quint64 AbstractTreeItem::id() const {
@@ -45,7 +42,7 @@ quint64 AbstractTreeItem::id() const {
 int AbstractTreeItem::defaultColumn() const {
   // invalid QModelIndexes aka rootNodes get their Childs stuffed into column -1
   // all others to 0
-  if(_parentItem == 0)
+  if(parent() == 0)
     return -1;
   else
     return 0;
@@ -71,17 +68,58 @@ void AbstractTreeItem::removeChild(int column, int row) {
   if(!_childItems.contains(column)
      || _childItems[column].size() <= row)
     return;
+
+  if(column == defaultColumn())
+    emit beginRemoveChilds(row, row);
   
   AbstractTreeItem *treeitem = _childItems[column].value(row);
   _childItems[column].removeAt(row);
   _childHash[column].remove(_childHash[column].key(treeitem));
+  disconnect(treeitem, 0, this, 0);
   treeitem->deleteLater();
+
+  if(column == defaultColumn())
+    emit endRemoveChilds();
 }
 
 void AbstractTreeItem::removeChild(int row) {
   removeChild(defaultColumn(), row);
 }
 
+void AbstractTreeItem::removeChildById(int column, const quint64 &id) {
+  if(!_childHash[column].contains(id))
+    return;
+  
+  AbstractTreeItem *treeItem = _childHash[column][id];
+  int row = _childItems[column].indexOf(treeItem);
+  Q_ASSERT(row >= 0);
+  removeChild(column, row);
+}
+
+void AbstractTreeItem::removeChildById(const quint64 &id) {
+  removeChildById(defaultColumn(), id);
+}
+
+void AbstractTreeItem::removeAllChilds() {
+  if(childCount() == 0)
+    return;
+
+  emit beginRemoveChilds(0, childCount() - 1);
+  AbstractTreeItem *child;
+  foreach(int column, _childItems.keys()) {
+    QList<AbstractTreeItem *>::iterator iter = _childItems[column].begin();
+    while(iter != _childItems[column].end()) {
+      child = *iter;
+      _childHash[column].remove(_childHash[column].key(child));
+      iter = _childItems[column].erase(iter);
+      disconnect(child, 0, this, 0);
+      child->removeAllChilds();
+      child->deleteLater();
+    }
+  }
+  emit endRemoveChilds();
+}
+
 AbstractTreeItem *AbstractTreeItem::child(int column, int row) const {
   if(!_childItems.contains(column)
      || _childItems[column].size() <= row)
@@ -94,7 +132,7 @@ AbstractTreeItem *AbstractTreeItem::child(int row) const {
   return child(defaultColumn(), row);
 }
 
-AbstractTreeItem *AbstractTreeItem::childById(int column, const uint &id) const {
+AbstractTreeItem *AbstractTreeItem::childById(int column, const quint64 &id) const {
   if(!_childHash.contains(column)
      || !_childHash[column].contains(id))
     return 0;
@@ -102,7 +140,7 @@ AbstractTreeItem *AbstractTreeItem::childById(int column, const uint &id) const
     return _childHash[column].value(id);
 }
 
-AbstractTreeItem *AbstractTreeItem::childById(const uint &id) const {
+AbstractTreeItem *AbstractTreeItem::childById(const quint64 &id) const {
   return childById(defaultColumn(), id);
 }
 
@@ -118,31 +156,29 @@ int AbstractTreeItem::childCount() const {
 }
 
 int AbstractTreeItem::column() const {
-  if(!_parentItem)
+  if(!parent())
     return -1;
 
-  QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = _parentItem->_childItems.constBegin();
-  int pos;
-  while(iter != _parentItem->_childItems.constEnd()) {
-    pos = iter.value().indexOf(const_cast<AbstractTreeItem *>(this));
-    if(pos != -1)
+  QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = parent()->_childItems.constBegin();
+  while(iter != parent()->_childItems.constEnd()) {
+    if(iter.value().contains(const_cast<AbstractTreeItem *>(this)))
       return iter.key();
     iter++;
   }
 
   // unable to find us o_O
-  return _parentItem->defaultColumn();
+  return parent()->defaultColumn();
 }
 
 int AbstractTreeItem::row() const {
-  if(!_parentItem)
+  if(!parent())
     return -1;
   else
-    return _parentItem->_childItems[column()].indexOf(const_cast<AbstractTreeItem*>(this));
+    return parent()->_childItems[column()].indexOf(const_cast<AbstractTreeItem*>(this));
 }
 
-AbstractTreeItem *AbstractTreeItem::parent() {
-  return _parentItem;
+AbstractTreeItem *AbstractTreeItem::parent() const {
+  return qobject_cast<AbstractTreeItem *>(QObject::parent());
 }
 
 Qt::ItemFlags AbstractTreeItem::flags() const {
@@ -160,8 +196,27 @@ void AbstractTreeItem::childDestroyed() {
     qWarning() << "AbstractTreeItem::childDestroyed() received null pointer!";
     return;
   }
-  _childItems[item->column()].removeAt(item->row());
-  _childHash[item->column()].remove(_childHash[item->column()].key(item));
+
+  QHash<int, QList<AbstractTreeItem*> >::const_iterator iter = _childItems.constBegin();
+  int column, row = -1;
+  while(iter != _childItems.constEnd()) {
+    row = iter.value().indexOf(item);
+    if(row != -1) {
+      column = iter.key();
+      break;
+    }
+    iter++;
+  }
+
+  if(row == -1) {
+    qWarning() << "AbstractTreeItem::childDestroyed(): unknown Child died:" << item << "parent:" << this;
+    return;
+  }
+  
+  _childItems[column].removeAt(row);
+  _childHash[column].remove(_childHash[column].key(item));
+  emit beginRemoveChilds(row, row);
+  emit endRemoveChilds();
 }
   
 /*****************************************
@@ -233,6 +288,19 @@ TreeModel::TreeModel(const QList<QVariant> &data, QObject *parent)
   : QAbstractItemModel(parent)
 {
   rootItem = new SimpleTreeItem(data, 0);
+
+  connect(rootItem, SIGNAL(dataChanged(int)),
+         this, SLOT(itemDataChanged(int)));
+  
+  connect(rootItem, SIGNAL(newChild(AbstractTreeItem *)),
+         this, SLOT(newChild(AbstractTreeItem *)));
+
+  connect(rootItem, SIGNAL(beginRemoveChilds(int, int)),
+         this, SLOT(beginRemoveChilds(int, int)));
+  
+  connect(rootItem, SIGNAL(endRemoveChilds()),
+         this, SLOT(endRemoveChilds()));
+
 }
 
 TreeModel::~TreeModel() {
@@ -258,7 +326,7 @@ QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) con
     return QModelIndex();
 }
 
-QModelIndex TreeModel::indexById(uint id, const QModelIndex &parent) const {
+QModelIndex TreeModel::indexById(quint64 id, const QModelIndex &parent) const {
   AbstractTreeItem *parentItem; 
   
   if(!parent.isValid())
@@ -311,10 +379,23 @@ int TreeModel::rowCount(const QModelIndex &parent) const {
 }
 
 int TreeModel::columnCount(const QModelIndex &parent) const {
-  if(parent.isValid())
-    return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
-  else
-    return rootItem->columnCount();
+  Q_UNUSED(parent)
+  // since there the Qt Views don't draw more columns than the header has columns
+  // we can be lazy and simply return the count of header columns
+  // actually this gives us more freedom cause we don't have to ensure that a rows parent
+  // has equal or more columns than that row
+  
+//   if(parent.isValid()) {
+//     AbstractTreeItem *child;
+//     if(child = static_cast<AbstractTreeItem *>(parent.internalPointer())->child(parent.column(), parent.row()))
+//       return child->columnCount();
+//     else
+//       return static_cast<AbstractTreeItem*>(parent.internalPointer())->columnCount();
+//   } else {
+//     return rootItem->columnCount();
+//   }
+
+  return rootItem->columnCount();
 }
 
 QVariant TreeModel::data(const QModelIndex &index, int role) const {
@@ -343,19 +424,25 @@ QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int rol
 
 void TreeModel::itemDataChanged(int column) {
   AbstractTreeItem *item = qobject_cast<AbstractTreeItem *>(sender());
-  QModelIndex itemIndex;
+  QModelIndex leftIndex, rightIndex;
 
-  if(item == rootItem) 
-    itemIndex = QModelIndex();
-  else
-    itemIndex = createIndex(item->row(), column, item);
+  if(item == rootItem)
+    return;
 
-  emit dataChanged(itemIndex, itemIndex);
+  if(column == -1) {
+    leftIndex = createIndex(item->row(), 0, item);
+    rightIndex = createIndex(item->row(), item->columnCount()-1, item);
+  } else {
+    leftIndex = createIndex(item->row(), column, item);
+    rightIndex = leftIndex;
+  }
+
+  emit dataChanged(leftIndex, rightIndex);
 }
 
 void TreeModel::appendChild(AbstractTreeItem *parent, AbstractTreeItem *child) {
-  if(parent == 0 or child == 0) {
-    qWarning() << "TreeModel::apendChild(parent, child) parent and child have to be valid pointers!" << parent << child;
+  if(parent == 0 || child == 0) {
+    qWarning() << "TreeModel::appendChild(parent, child) parent and child have to be valid pointers!" << parent << child;
     return;
   }
 
@@ -366,8 +453,29 @@ void TreeModel::appendChild(AbstractTreeItem *parent, AbstractTreeItem *child) {
 
   connect(child, SIGNAL(dataChanged(int)),
          this, SLOT(itemDataChanged(int)));
+  
+  connect(child, SIGNAL(newChild(AbstractTreeItem *)),
+         this, SLOT(newChild(AbstractTreeItem *)));
+
+  connect(child, SIGNAL(beginRemoveChilds(int, int)),
+         this, SLOT(beginRemoveChilds(int, int)));
+  
+  connect(child, SIGNAL(endRemoveChilds()),
+         this, SLOT(endRemoveChilds()));
 }
 
+void TreeModel::newChild(AbstractTreeItem *child) {
+  appendChild(static_cast<AbstractTreeItem *>(sender()), child);
+}
+
+void TreeModel::beginRemoveChilds(int firstRow, int lastRow) {
+  QModelIndex parent = indexByItem(static_cast<AbstractTreeItem *>(sender()));
+  beginRemoveRows(parent, firstRow, lastRow);
+}
+
+void TreeModel::endRemoveChilds() {
+  endRemoveRows();
+}
 
 bool TreeModel::removeRow(int row, const QModelIndex &parent) {
   if(row > rowCount(parent))
@@ -411,6 +519,5 @@ bool TreeModel::removeRows(int row, int count, const QModelIndex &parent) {
 }
 
 void TreeModel::clear() {
-  removeRows(0, rowCount());
-  reset();
+  rootItem->removeAllChilds();
 }