TreeView and virtual TreeModel problems

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

TreeView and virtual TreeModel problems

Chris Uppal-3
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