# # # patch "src/model/Inventory.cpp" # from [c76f068669214a19c97df858405a735099390a9f] # to [053e2fd159965c9f1980d0f9e1cc320f355d9f3c] # # patch "src/model/Inventory.h" # from [c13128083107dde46436ed0ad304b20de6576f52] # to [dd3d583eaa911aa15533ded0974a4f16f57481c9] # # patch "src/model/InventoryItem.cpp" # from [20b98c2c356cce9221ba958332b0791c44d97306] # to [db545d46860c08301a915444c36ec62955497f8d] # # patch "src/model/InventoryItem.h" # from [47cb4afae97d66984194dcb26e3f69bb605e5083] # to [5bb9365545fa0ca8d1d7e913d4a7ec37d23560e3] # # patch "src/view/InventoryView.cpp" # from [5f14786855eed3da97bcb3a048a2b3f1a4c3137f] # to [2eb9f86bd01b1f5cc192e77bce8e70ea87dfe2b3] # # patch "src/view/InventoryView.h" # from [f9a37ec91310327b6d4b3f0d9ea9b9bf75e29da8] # to [148aa72111fb093f7387aaab496d55c8f8bc82df] # # patch "src/view/WorkspaceWindow.cpp" # from [6a1d8aa85544a2727ce324ed317b6637e1b5e3f4] # to [dfa4d23546d733bc6c73503d9aee32dabcd70a86] # ============================================================ --- src/model/Inventory.cpp c76f068669214a19c97df858405a735099390a9f +++ src/model/Inventory.cpp 053e2fd159965c9f1980d0f9e1cc320f355d9f3c @@ -26,25 +26,16 @@ Inventory::Inventory(QObject * parent) #include Inventory::Inventory(QObject * parent) - : QAbstractItemModel(parent), AutomateCommand(0), - workspacePath(), modelWasCreated(false) + : QAbstractItemModel(parent), AutomateCommand(0), workspacePath(), + readDirLevelsAhead(1) { - rootItem = new PseudoItem("__root", PseudoItem::Root); + rootItem = new ModelItem("__root__"); + rootItem->setParent(rootItem); connect( - this, SIGNAL(modelCreated()), + this, SIGNAL(modelReset()), this, SLOT(setWorkspacePath()) ); - - connect( - this, SIGNAL(rowsInserted(const QModelIndex & , int , int )), - this, SLOT(noteRowsInserted(const QModelIndex & , int , int )) - ); - - connect( - this, SIGNAL(rowsRemoved(const QModelIndex & , int , int )), - this, SLOT(noteRowsRemoved(const QModelIndex & , int , int )) - ); } Inventory::~Inventory() @@ -57,6 +48,41 @@ void Inventory::setWorkspacePath(const W workspacePath = ws; } +void Inventory::refresh() +{ + readInventory(QString()); +} + +void Inventory::fetchMore(const QModelIndex & index) +{ + if (!index.isValid()) + return; + + ModelItem * item = static_cast + (index.internalPointer()); + I(item); + + InventoryItem * invitem = dynamic_cast + (item); + + // not an inventory item + if (!invitem) return; + + // mtn currently allows to restrict on ignored, but not on unknown + // paths, check that! + if (invitem->hasStatus(InventoryItem::Unknown)) return; + + // 'inventory' can only be restricted with existing file paths + // and not with invalid file/directory combinations or missing paths + // restricting to single files makes no sense for us here either + if (invitem->getFSType() != InventoryItem::Directory) return; + + // do not query again here if we already expanded this directory + if (invitem->isExpanded()) return; + + readInventory(invitem->getPath()); +} + void Inventory::readInventory(const QString & path) { I(!workspacePath.isEmpty()); @@ -67,9 +93,8 @@ void Inventory::readInventory(const QStr cmd << path; } - // FIXME: bump to 1 in order to be able to determine - // if a node has subnodes - QStringList opts = QStringList() << "depth" << QString::number(0); + QStringList opts = + QStringList() << "depth" << QString::number(readDirLevelsAhead); MonotoneTask task(cmd, opts); AutomateCommand::enqueueWorkspaceTask(workspacePath, task); @@ -99,20 +124,17 @@ void Inventory::processTaskResult(const foreach (Stanza st, stanzas) { InventoryItem * item = new InventoryItem(st); - if (item->isDirectory()) - { - PseudoItem * cdUp = new PseudoItem("..", PseudoItem::CdUp); - item->appendChild(cdUp); - } QString path = item->getPath(); + // if this item replaces an existing item, delete it and all possible + // children underknees if (itemMap.contains(path)) { InventoryItem * oldItem = itemMap.value(path); - L(QString("querying parent index of %1").arg(path)); - QModelIndex parentIndex = indexFromItem(oldItem->parent(), 0); + QModelIndex index = indexFromItem(oldItem, 0); + I(index.isValid()); int row = oldItem->row(); - removeRowsRecursive(parentIndex, row, row); + removeRowsRecursive(index.parent(), row, 1); } ModelItem * parentItem; @@ -123,7 +145,13 @@ void Inventory::processTaskResult(const else { QString baseDir = item->getBaseDirectory(); - // FIXME: this will probably make problems with restricted renames + // FIXME: this could eventually make problems if inventory is + // executed with a path which was renamed. The inventory output + // then contains another stanza of a path which was not yet + // read in, so we have no plan where to tack this on. + // A possible resolution would be to enqueue a new inventory + // request with the baseDir right here, delete the node and + // continue with the next one I(itemMap.contains(baseDir)); parentItem = itemMap.value(baseDir); } @@ -133,26 +161,35 @@ void Inventory::processTaskResult(const 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); endInsertRows(); } - if (!modelWasCreated) + // if the inventory's workspace root was read (i.e. inventory was called + // without a path), we've practically resetted the complete view, so we + // make sure modelReset() is signalled by calling reset() + if (task.getArguments().size() == 1) { - modelWasCreated = true; reset(); - emit modelCreated(); } } QModelIndex Inventory::indexFromItem(const ModelItem * item, int col) const { QModelIndex parent; - if (item->parent() != item) + if (!item->isRoot()) { - indexFromItem(item->parent(), col); + parent = indexFromItem(item->parent(), col); } - return index(item->row(), col, parent); + QModelIndex idx = index(item->row(), col, parent); + I(item->isRoot() || idx.isValid()); + return idx; } void Inventory::setWorkspacePath() @@ -404,23 +441,29 @@ QMap Inventory return unaccountedRenames; } -void Inventory::removeRowsRecursive(const QModelIndex & parent, int start, int end) +void Inventory::removeRowsRecursive(const QModelIndex & parent, int startRow, int count) { - I(parent.isValid()); + I(startRow >= 0); + I(parent.isValid() || parent.parent() == QModelIndex()); + I(count > 0 && count <= rowCount(parent)); - for (int row = start; row <= end; row++) + // 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--) { - QModelIndex child = index(row, 0, parent); + QModelIndex child = index(startRow, 0, parent); I(child.isValid()); // delete children recursively int count = rowCount(child); if (count > 0) { - removeRowsRecursive(child, 0, count - 1); + removeRowsRecursive(child, 0, count); } - beginRemoveRows(parent, row, row); + beginRemoveRows(parent, startRow, startRow); ModelItem * item = static_cast(child.internalPointer()); InventoryItem * invitem = qobject_cast(item); @@ -433,17 +476,3 @@ void Inventory::removeRowsRecursive(cons } } -void Inventory::noteRowsInserted(const QModelIndex & parent, int start, int end) -{ - if (!parent.isValid()) return; - ModelItem * item = static_cast(parent.internalPointer()); - L(QString("Item %1, rows %2 to %3 inserted").arg(item->getLabel()).arg(start).arg(end)); -} - -void Inventory::noteRowsRemoved(const QModelIndex & parent, int start, int end) -{ - if (!parent.isValid()) return; - ModelItem * item = static_cast(parent.internalPointer()); - L(QString("Item %1, rows %2 to %3 removed").arg(item->getLabel()).arg(start).arg(end)); -} - ============================================================ --- src/model/Inventory.h c13128083107dde46436ed0ad304b20de6576f52 +++ src/model/Inventory.h dd3d583eaa911aa15533ded0974a4f16f57481c9 @@ -45,23 +45,23 @@ public slots: public slots: void setWorkspacePath(const WorkspacePath &); - void readInventory(const QString & path = QString()); + void refresh(); + void fetchMore(const QModelIndex &); private: void processTaskResult(const MonotoneTask &); QModelIndex indexFromItem(const ModelItem *, int) const; void removeRowsRecursive(const QModelIndex &, int, int); + void readInventory(const QString &); ModelItem * rootItem; QString branchName; QMap itemMap; WorkspacePath workspacePath; - bool modelWasCreated; + int readDirLevelsAhead; private slots: void setWorkspacePath(); - void noteRowsInserted( const QModelIndex & , int , int ); - void noteRowsRemoved( const QModelIndex & , int , int ); signals: void modelCreated(); ============================================================ --- src/model/InventoryItem.cpp 20b98c2c356cce9221ba958332b0791c44d97306 +++ src/model/InventoryItem.cpp db545d46860c08301a915444c36ec62955497f8d @@ -23,7 +23,7 @@ #include #include -ModelItem::ModelItem(const QString & l) : parentItem(0), label(l), expanded(false) +ModelItem::ModelItem(const QString & l) : parentItem(0), label(l) {} ModelItem::ModelItem(const ModelItem * other) @@ -146,16 +146,11 @@ QVariant ModelItem::data(int column, int return QVariant(); } -void ModelItem::setExpanded() +bool ModelItem::isRoot() const { - expanded = true; + return this == parentItem; } -bool ModelItem::isExpanded() const -{ - return expanded; -} - const int InventoryItem::RenameSource = 1; const int InventoryItem::RenameTarget = 2; const int InventoryItem::Added = 4; @@ -169,7 +164,8 @@ InventoryItem::InventoryItem(const Stanz const int InventoryItem::AttributesChanged = 1024; InventoryItem::InventoryItem(const Stanza & stanza) - : ModelItem(), status(0) + : ModelItem(), fs_type(Undefined), old_type(Undefined), new_type(Undefined), + status(0) { foreach (StanzaEntry en, stanza) { @@ -498,3 +494,27 @@ QString InventoryItem::getRenameInfo() c return strings.join(", "); } +bool InventoryItem::isExpanded() const +{ + // non-directory nodes are obviously always expanded + if (!isDirectory()) return true; + + // if we have no children, this was not yet expanded + if (childCount() == 0) return false; + + // go through all the children and check if they have been expanded yet + foreach (ModelItem * child, children) + { + InventoryItem * invitem = dynamic_cast(child); + // skip non-inventory items + if (!invitem) continue; + // expanded directories have at least one child, the pseudo cdup item! + if (invitem->isDirectory() && invitem->childCount() == 0) + { + return false; + } + } + + return true; +} + ============================================================ --- src/model/InventoryItem.h 47cb4afae97d66984194dcb26e3f69bb605e5083 +++ src/model/InventoryItem.h 5bb9365545fa0ca8d1d7e913d4a7ec37d23560e3 @@ -48,33 +48,26 @@ public: ModelItem * child(int) const; int childCount() const; int row() const; + bool isRoot() const; - bool isExpanded() const; - void setExpanded(); - protected: ModelItem * parentItem; QList children; QString label; - bool expanded; }; class PseudoItem : public ModelItem { Q_OBJECT public: - enum Type { CdUp = 1, Root }; + enum Type { Undefined = 1, CdUp}; PseudoItem(const QString & l, Type t) : ModelItem(l), type(t) - { - if (type == Root) - parentItem = this; - } + {} PseudoItem(const PseudoItem * other) : ModelItem(other), type(other->type) {} bool isCdUp() const { return type == CdUp; } - bool isRoot() const { return type == Root; } private: Type type; @@ -84,7 +77,10 @@ public: { Q_OBJECT public: - enum FileType { File = 1, Directory, None }; + // "Undefined" is no state we get back from mtn, this is rather the + // default for nodes which are not tracked by mtn, thus have no old_node + // and no new_node stanza + enum FileType { Undefined = 1, File, Directory, None }; InventoryItem(const Stanza &); InventoryItem(const InventoryItem *); @@ -115,6 +111,8 @@ public: bool isTracked() const; bool hasChanged() const; + bool isExpanded() const; + int getStatusRecursive() const; bool hasChangedRecursive() const; ============================================================ --- src/view/InventoryView.cpp 5f14786855eed3da97bcb3a048a2b3f1a4c3137f +++ src/view/InventoryView.cpp 2eb9f86bd01b1f5cc192e77bce8e70ea87dfe2b3 @@ -193,7 +193,7 @@ void InventoryView::setModel(QSortFilter { disconnect( oldModel, SIGNAL(modelReset()), - this, SLOT(delegateModelReset()) + this, SLOT(resetViewAfterModelReset()) ); } @@ -201,19 +201,12 @@ void InventoryView::setModel(QSortFilter connect( newModel, SIGNAL(modelReset()), - this, SLOT(delegateModelReset()) + this, SLOT(resetViewAfterModelReset()) ); } -void InventoryView::delegateModelReset() +void InventoryView::resetViewAfterModelReset() { - // give the view another event cycle to reload the new indices - //QTimer::singleShot(0, this, SLOT(modelReset())); - modelReset(); -} - -void InventoryView::modelReset() -{ QModelIndex index = model()->index(0, 0, QModelIndex()); if (!index.isValid()) { ============================================================ --- src/view/InventoryView.h f9a37ec91310327b6d4b3f0d9ea9b9bf75e29da8 +++ src/view/InventoryView.h 148aa72111fb093f7387aaab496d55c8f8bc82df @@ -80,8 +80,7 @@ private slots: InventoryViewDelegate invViewDelegate; private slots: - void delegateModelReset(); - void modelReset(); + void resetViewAfterModelReset(); void changeDirectory(const QModelIndex &); void itemClicked(const QModelIndex & index); ============================================================ --- src/view/WorkspaceWindow.cpp 6a1d8aa85544a2727ce324ed317b6637e1b5e3f4 +++ src/view/WorkspaceWindow.cpp dfa4d23546d733bc6c73503d9aee32dabcd70a86 @@ -165,12 +165,12 @@ void WorkspaceWindow::setup() menuBar, SIGNAL(expandTree(bool)), treeView, SLOT(expandAllNodes(bool)) ); -/* + connect( treeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(readInventory(const QModelIndex &)) ); -*/ + connect( treeView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(readInventory(const QModelIndex &)) @@ -208,12 +208,12 @@ void WorkspaceWindow::setup() connect( dialogManager, SIGNAL(revisionCommitted(const QString &)), - invModel, SLOT(readInventory()) + invModel, SLOT(refresh()) ); connect( menuBar, SIGNAL(reloadWorkspace()), - invModel, SLOT(readInventory()) + invModel, SLOT(refresh()) ); connect( @@ -259,7 +259,7 @@ void WorkspaceWindow::load(const QString attrModel->setWorkspacePath(workspacePath); // read the base set of the inventory - invModel->readInventory(); + invModel->refresh(); // initialize the dialog manager reinterpret_cast(dialogManager)->init(workspacePath); @@ -358,21 +358,6 @@ void WorkspaceWindow::readInventory(cons { QModelIndex sourceIndex = static_cast (index.model())->mapToSource(index); - ModelItem * item = static_cast - (sourceIndex.internalPointer()); - InventoryItem * invitem = dynamic_cast - (item); - - if (!invitem) return; - - // 'inventory' can only be restricted with existing file paths - // and not with invalid file/directory combinations or missing paths - // restricting to single files makes no sense for us here either - if (invitem->getFSType() != InventoryItem::Directory) return; - - // do not query again here if we already expanded this directory - if (invitem->isExpanded()) return; - - invModel->readInventory(invitem->getPath()); + invModel->fetchMore(sourceIndex); }