[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: the mysterious wake of copy-instance

In article <9312062250.AA16305@merle.acns.nwu.edu>, christian e. crone

> i need to be able to make copies of view hierarchies (e.g., views inside of
> views inside of views) whether on-screen or off.  it is fairly obvious that
> macl's copy-instance cannot be directly applied, since it only changes the
> top level pointers.  

		[Discussion of the consequences of this.]

> but, why is it incorrect to recursively do the following --

	[Remove and save subviews, copy the containing view, add copies of the
		to the new copy, replace the subviews in their original views]

> anyway, implemented as follows:
> (defmethod rec-copy-instance ((view view))
>    (let ((new-view nil)
>            (subview-list nil))
>       (do-subviews (subview view)
>          (setq subview-list (cons subview subview-list))        ;; step 1
>          (remove-subviews view subview))                        ;; step 2
>       (setq new-view (copy-instance view))                      ;; step 3
>       (dolist (subview subview-list)
>          (add-subviews new-view (rec-copy-instance subview))    ;; 4 & 5, p 2
>          (add-subviews view subview))                           ;; 5, part 1
>       new-view))
> yields the result:

		[The copied view _and_ the original view contain _both_ the original
			and their copies.]

The problem here is that the slot that defines a view's subviews is the
view-subviews slot, whose value is a vector; the subviews are elements of
that vector. The add-subviews method (by calling the set-view-container
method) destructively modifies that vector by vector-push-extending the new
subview into it.

The copy-instance in the above code, as noted, only carries over the
top-level pointers to the instance's slots, so that both new-view and view
have the 
_same_ view-subviews vector. The add-subviews actions, therefore,
effectively add the copies and the original subviews into _both_ view and
new-view at the same time.

Giving the copy of the emptied-out view a fresh view-subviews vector seems
to fix things; it also eliminates the need to remove and restore the
original view's subviews:

(defmethod rec-copy-instance ((view named-view))
   (let ((new-view nil)
         (subview-list (subviews view)))
      (setq new-view (copy-instance view))                
      (setf (slot-value new-view 'view-subviews) 
            (make-array 1 :adjustable t :fill-pointer 0))
      (dolist (subview subview-list)
         (add-subviews new-view (rec-copy-instance subview)))

With, for demonstration:

(defclass named-view (view)
  ((name :initarg :name :accessor name)))

(defmethod contents ((n named-view))
  (when (subviews n)
    (format *standard-output* "~A contains ~{~A ~}~&" 
          (name n) (mapcar #'name (subviews n)))
    (mapcar #'contents (subviews n)))

? (setf Baltimore (make-instance 'named-view :name "Baltimore"))
? (setf Maryland (make-instance 'named-view :name "Maryland"))
? (setf Massachusetts (make-instance 'named-view :name "Massachusetts"))
#<NAMED-VIEW #x4CD029>
? (setf USA (make-instance 'named-view :name "USA"))
#<NAMED-VIEW #x4CD121>
? (add-subviews Maryland Baltimore)
? (add-subviews USA Maryland Massachusetts)
? (contents USA)
USA contains Maryland Massachusetts 
Maryland contains Baltimore 
? (setf America (rec-copy-instance USA))
#<NAMED-VIEW #x4CD349>
? (contents America)
USA contains Maryland Massachusetts 
Maryland contains Baltimore 
? (contents USA)
USA contains Maryland Massachusetts 
Maryland contains Baltimore 
? (remove-subviews USA Maryland)
? (contents USA)
USA contains Massachusetts 
? (contents America)
USA contains Maryland Massachusetts 
Maryland contains Baltimore 

=== John R. Gersh             John_Gersh@aplmail.jhuapl.edu                
=== The Johns Hopkins University Applied Physics Laboratory
=== Laurel, MD 20723                        +1(301)953-5503