# # # patch "src/model/Inventory.cpp" # from [a7f7538449a4d0aae36470ad11b53c76a182ce9e] # to [71bba569c26554a91a44e6cbd150cec395dce7fe] # # patch "src/model/Inventory.h" # from [3a276b842f49014de39fd8426a23199ea715aaee] # to [b47df84df275352abd1e71ca41c128fba53fa0e0] # # patch "src/model/InventoryItem.cpp" # from [db545d46860c08301a915444c36ec62955497f8d] # to [d2953a68b908485df6c4a170d8df7acd039376cf] # ============================================================ --- src/model/Inventory.cpp a7f7538449a4d0aae36470ad11b53c76a182ce9e +++ src/model/Inventory.cpp 71bba569c26554a91a44e6cbd150cec395dce7fe @@ -73,6 +73,10 @@ bool Inventory::canFetchMore(const QMode if (invitem->isExpanded()) return false; + if (lastRequests.size() > 0 && lastRequests.top() == invitem->getPath()) + return false; + + L(QString("fetching more for %1 (items: %2)").arg(invitem->getLabel()).arg(invitem->childCount())); return true; } @@ -116,6 +120,8 @@ void Inventory::readInventory(const QStr cmd << path; } + lastRequests.push(path); + QStringList opts; // if requested, just read one directory level ahead @@ -146,15 +152,47 @@ void Inventory::processTaskResult(const return; } + QString queriedPath = ""; + if (task.getArguments().size() > 1) + { + queriedPath = task.getArguments().at(1); + } + + int queriedDepth = -1; + if (task.getOptions().size() > 1) + { + queriedDepth = task.getOptions().at(1).toInt(); + if (!queriedPath.isEmpty()) + { + queriedDepth += queriedPath.split("/").size() + 1; + } + } + BasicIOParser parser(task.getOutputUtf8()); I(parser.parse()); StanzaList stanzas = parser.getStanzas(); + QString lastBaseDir; + QModelIndexList parentsWithChildren; foreach (Stanza st, stanzas) { InventoryItem * item = new InventoryItem(st); QString path = item->getPath(); + int itemDepth = 0; + if (!path.isEmpty()) + { + itemDepth = path.split("/").size() + 1; + } + + // add a cdup item for this item if we either queried everything + // already or the current directory level corresponds to the queried one + if (item->isDirectory() && (queriedDepth == -1 || queriedDepth < itemDepth)) + { + PseudoItem * cdUp = new PseudoItem("..", PseudoItem::CdUp); + item->appendChild(cdUp); + parentsWithChildren.append(indexFromItem(item, 0)); + } // if this item replaces an existing item, delete it and all possible // children underknees if (itemMap.contains(path)) @@ -163,7 +201,7 @@ void Inventory::processTaskResult(const QModelIndex index = indexFromItem(oldItem, 0); I(index.isValid()); int row = oldItem->row(); - removeRowsRecursive(index.parent(), row, 1); + removeRowsRecursive(index.parent(), row, row); } ModelItem * parentItem; @@ -186,17 +224,15 @@ void Inventory::processTaskResult(const } QModelIndex parentIndex = indexFromItem(parentItem, 0); - int newRowNumber = parentItem->childCount(); - - beginInsertRows(parentIndex, newRowNumber, newRowNumber); itemMap.insert(path, item); - // add a cdup item for this parent if the parent has no children yet - if (parentItem->childCount() == 0) - { - PseudoItem * cdUp = new PseudoItem("..", PseudoItem::CdUp); - parentItem->appendChild(cdUp); - } parentItem->appendChild(item); + } + + // we've remembered all the model indexes which got real children + // now inform Qt at a glance what we've just inserted + foreach (QModelIndex index, parentsWithChildren) + { + beginInsertRows(index, 0, rowCount(index)); endInsertRows(); } @@ -214,35 +250,27 @@ void Inventory::processTaskResult(const // a) no side effects // b) I never need it for its original purpose // - QString path; - if (task.getArguments().size() > 1) + if (queriedPath.isEmpty()) { - path = task.getArguments().at(1); - } - else - { // we need to call reset() before we emit dataChanged, // otherwise the view doesn't know anything of the new // index we're going to present her afterwards reset(); - path = ""; } - I(itemMap.contains(path)); - QModelIndex index = indexFromItem(itemMap.value(path), 0); + I(lastRequests.pop() == queriedPath); + I(itemMap.contains(queriedPath)); + QModelIndex index = indexFromItem(itemMap.value(queriedPath), 0); I(index.isValid()); emit dataChanged(index, index); } -QModelIndex Inventory::indexFromItem(const ModelItem * item, int col) const +QModelIndex Inventory::indexFromItem(ModelItem * item, int col) const { - QModelIndex parent; - if (!item->isRoot()) + if (item->isRoot()) { - parent = indexFromItem(item->parent(), col); + return QModelIndex(); } - QModelIndex idx = index(item->row(), col, parent); - I(item->isRoot() || idx.isValid()); - return idx; + return createIndex(item->row(), col, reinterpret_cast(item)); } void Inventory::setWorkspacePath() @@ -500,38 +528,47 @@ QMap Inventory return unaccountedRenames; } -void Inventory::removeRowsRecursive(const QModelIndex & parent, int startRow, int count) +void Inventory::removeRowsRecursive(const QModelIndex & parent, int startRow, int endRow) { I(startRow >= 0); I(parent.isValid() || parent.parent() == QModelIndex()); - I(count > 0 && count <= rowCount(parent)); + I(endRow >= startRow && endRow < rowCount(parent)); - // since we're deleting items as we go, we delete all items on the same - // position until the count is reached, otherwise the 2nd item would - // becomes the 1st item in the next iteration and after approx. half of the - // child indexes we would get invalid indexes returned from index() - for ( ; count > 0; count--) + // we need to ensure that we recursively delete all childrens before we + // actually delete the requested items for parent, otherwise Qt will + // throw too many signals around + QModelIndexList indexesToDelete; + for (int i=startRow; i<=endRow; i++) { - QModelIndex child = index(startRow, 0, parent); + QModelIndex child = index(i, 0, parent); I(child.isValid()); - - // delete children recursively int count = rowCount(child); if (count > 0) { - removeRowsRecursive(child, 0, count); + removeRowsRecursive(child, 0, count - 1); } - beginRemoveRows(parent, startRow, startRow); - ModelItem * item = static_cast(child.internalPointer()); - InventoryItem * invitem = qobject_cast(item); + indexesToDelete.append(child); + } + beginRemoveRows(parent, startRow, endRow); + foreach (QModelIndex index, indexesToDelete) + { + I(index.isValid()); + I(rowCount(index) == 0); + + ModelItem * item = static_cast(index.internalPointer()); + I(item); + + InventoryItem * invitem = qobject_cast(item); if (invitem) + { itemMap.remove(invitem->getPath()); + } item->parent()->removeChild(item); delete item; - endRemoveRows(); } + endRemoveRows(); } ============================================================ --- src/model/Inventory.h 3a276b842f49014de39fd8426a23199ea715aaee +++ src/model/Inventory.h b47df84df275352abd1e71ca41c128fba53fa0e0 @@ -25,6 +25,7 @@ #include "InventoryItem.h" #include +#include class Inventory : public QAbstractItemModel, public AutomateCommand { @@ -54,7 +55,7 @@ private: private: void processTaskResult(const MonotoneTask &); - QModelIndex indexFromItem(const ModelItem *, int) const; + QModelIndex indexFromItem(ModelItem *, int) const; void removeRowsRecursive(const QModelIndex &, int, int); void readInventory(const QString &); @@ -62,6 +63,7 @@ private: QString branchName; QMap itemMap; WorkspacePath workspacePath; + QStack lastRequests; private slots: void setWorkspacePath(); ============================================================ --- src/model/InventoryItem.cpp db545d46860c08301a915444c36ec62955497f8d +++ src/model/InventoryItem.cpp d2953a68b908485df6c4a170d8df7acd039376cf @@ -508,7 +508,8 @@ bool InventoryItem::isExpanded() const InventoryItem * invitem = dynamic_cast(child); // skip non-inventory items if (!invitem) continue; - // expanded directories have at least one child, the pseudo cdup item! + // expanded directories should have at least one child, + // the pseudo cdup item! if (invitem->isDirectory() && invitem->childCount() == 0) { return false;