I've been hitting a couple of related problems with using TreeView in
contexts where the model is virtual (whether an actual subclass of VirtualTreeModel or not). Both problems are to do with keeping the actual displayed tree in synch with the computed model. Problem 1: For concreteness image that we are talking about a TreeView showing a tree of classes, where the model is a ClassHierarchyModel, and has a filter set such that only classes that have < 2 instvars are shown (I actually hit this in different circumstances, of course). If you add or remove an instvar to a class there is no way of getting the TreeView to update properly in the current image. You can issue TreeView>>refreshContents but that will close the existing tree, which is only rarely acceptable. It certainly isn't acceptable in my real application. The superficial problem is that the ClassHierarchyModel doesn't know that anything has changed, and so does not issue the normal update events. However it isn't possible to code around that because of the real underlying problem, which is that the ClassHierarchyModel (a VirtualTreeModel) is unable *can't tell* what has changed -- it has no record of what it used to "include" before the change to the class, and so it can't tell that a class which used to satisfy the filter no longer does. What it needed (I think) is a way of asking the *View* to re-synchronise itself with the model. (We could make it a method on the model, but that would mean that it had to be aware of the View). It would need to check its "handleObjectMap" to ensure that all the elements that the TreeView thought were present were still present in the model, and likewise that elements present in the model that it had "missed" were added. Problem 2: The implementation of TreeView>>onItem:addedInParent: assumes that it can go back to the model to get a list of the siblings of the newly added item. That implementation can break in the case where the underlying tree has added more than one item at a time. Consider this case: The underlying tree adds two subnodes, #Fred and #George to parent node #Sarah (sexual stereotyping creeping in here). It fires an event to say what it has changed. The listening code discovers that #Fred and #George have been added, so it attempts to update the TreeView. Say it starts with #Fred, one way or another it will end up invoking: TreeView>>onItem: #Fred addedInParent: #Sarah. The tree view then asks the model for the siblings of #Fred and gets back #Fred and #George. If #Fred happens to follow #George in the sibling list, then it'll attempt to convert #George to the "previousSiblingHandle" (which will answer nil, since the TreeView has not yet been told about #George). The call to TreeView>>basicAddAll:inHandle:afterHandle: will then fail with a walkback. I think that the relevant bit of TreeView>>onItem:addedInParent: should read something like: ---------------- parentTvItem isStateExpandedOnce ifTrue: [ | siblings previousSiblingIndex previousSiblingHandle | siblings := self model childrenOf: aParentObject. previousSiblingIndex := (self model keyOfNode: anObject in: siblings ifAbsent: [^siblings errorNotFound: anObject])-1. "there may be more siblings than we yet know about" [previousSiblingIndex > 0 and: [(previousSiblingHandle := self handleFromObject: (siblings at: previousSiblingIndex) ifAbsent: [nil]) isNil]] whileTrue: [previousSiblingIndex := previousSiblingIndex - 1]. previousSiblingHandle isNil ifTrue: [previousSiblingHandle := TVI_FIRST]. self basicAddAll: (Array with: anObject) inHandle: parentHandle afterHandle: previousSiblingHandle] ---------------- -- chris |
Free forum by Nabble | Edit this page |