[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
wrote:

> 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
subviews 	
		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
subviews 
			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)))
      new-view))

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)))
  (values))



? (setf Baltimore (make-instance 'named-view :name "Baltimore"))
#<NAMED-VIEW #x4CCE31>
? (setf Maryland (make-instance 'named-view :name "Maryland"))
#<NAMED-VIEW #x4CCF31>
? (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)
NIL
? (add-subviews USA Maryland Massachusetts)
NIL
? (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)
NIL
? (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