;; Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
;; Author(s): Jonathan Berry

;; This software is copyrighted by the DIMACS Center at Rutgers, The State
;; University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
;; RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
;; ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
;; DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
;; DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
;; POSSIBILITY OF SUCH DAMAGE.

;; THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
;; IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
;; NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
;; MODIFICATIONS.

;; The authors hereby grant permission to use, copy, modify, distribute,
;; and license this software and its documentation for any purpose, provided
;; that existing copyright notices are retained in all copies and that this
;; notice is included verbatim in any distributions. No written agreement,
;; license, or royalty fee is required for any of the authorized uses.
;; Modifications to this software may be copyrighted by their authors
;; and need not follow the licensing terms described here, provided that
;; the new terms are clearly indicated on the first page of each file where
;; they apply.

;; Last File Update: 6-Jul-1999
;; 

(require "Tk-classes")
(require "Canvas")
(require "balloon")

;----------------------------------------------------------------------
; my-gensym  		makes new instance-numbered symbols and 
;			internalizes them so they can be used as hash
;			table keys.          
(define (my-gensym str) (string->symbol (symbol->string (gensym str))))
;----------------------------------------------------------------------

;----------------------------------------------------------------------
; *link:vertex-placeholder*:  	global <oval> object used to mark old
;				location of a vertex while user drags
;				the vertex around.
(define *link:vertex-placeholder* #f)
;----------------------------------------------------------------------

;----------------------------------------------------------------------
; *link:canvas-table*:  global hash table used to look up 
;			<graph-view> objects by associated canvas
;
(define *link:canvas-table* (make-hash-table))
;----------------------------------------------------------------------

;----------------------------------------------------------------------
; *link:graph-view-table*:global hash table used to look up <graph-view> 
;			  objects by name
;
(define *link:graph-view-table* (make-hash-table))
;----------------------------------------------------------------------

(define *link:default-graph-view-title* "LINK: graph view")

(define-generic my-destroy)
(define-method my-destroy ( (t <top>) )  (destroy t))
(define-method my-destroy ( (o <destroyed-object>) ) #f) 

;;;
;;; viewing transformations - layout algorithms specify 
;;;			      world coords (bottom left (0,0) upper right (1,1))

(define (world-to-view world-x world-y gv)
	(let* ((view-x-max (slot-ref gv 'xpixels))
	       (view-y-max (slot-ref gv 'ypixels))
	       (x (* world-x view-x-max))
	       (y (- view-y-max (* world-y view-y-max)))
	       (vx (cond ((< x 0) 0)
			((> x view-x-max) view-x-max)
			(#t x)))
	       (vy (cond ((< y 0) 0)
			((> y view-y-max) view-y-max)
			(#t y))))
	     (list vx vy)))

(define (view-to-world view-x view-y gv)
	(let* ((view-x-max (slot-ref gv 'xpixels))
	       (view-y-max (slot-ref gv 'ypixels))
	       (x (/ view-x view-x-max))
	       (y (/ (- view-y-max view-y) view-y-max))
	       (wx (cond ((< x 0.0) 0.0)
			((> x 1.0) 1.0)
			(#t x)))
	       (wy (cond ((< y 0.0) 0.0)
			((> y 1.0) 1.0)
			(#t y))))
	  (list wx wy)))

(define (world-to-view2 world-x world-y view-x-max view-y-max)
	(let* ((x (* world-x view-x-max))
	       (y (- view-y-max (* world-y view-y-max)))
	       (vx (cond ((< x 0) 0)
			((> x view-x-max) view-x-max)
			(#t x)))
	       (vy (cond ((< y 0) 0)
			((> y view-y-max) view-y-max)
			(#t y))))
	     (list vx vy)))

(define (view-to-world2 view-x view-y view-x-max view-y-max)
	(let* ((x (/ view-x view-x-max))
	       (y (/ (- view-y-max view-y) view-y-max))
	       (wx (cond ((< x 0.0) 0.0)
			((> x 1.0) 1.0)
			(#t x)))
	       (wy (cond ((< y 0.0) 0.0)
			((> y 1.0) 1.0)
			(#t y))))
	  (list wx wy)))

(require "graph-edit")
(require "edge")
(require "grid")
(require "graph-menu")


;;;
;;; labels for extracting graphics given graph-objects
;;; (e.g. <vertex-item> given <vertex*>)
;;;
; <label-object> :used only by the C++ animation code
(define-class <label-object> () ((objlabel :getter objlabel 
					   :init-keyword :objlabel))) 
(define-generic get-label)
(define-method  get-label ((v <vertex*>)) (vertex-label v))
(define-method  get-label ((l <label-object>)) (slot-ref l 'objlabel))
(define-method  get-label ((e <edge*>)) (edge-label e))

;;;
;;; vertex and edge movement 
;;;

(define (update-vertex-coords vg view-x view-y)
	 (let* ((gcanv (slot-ref vg 'parent))
		(gv (hash-table-get *link:canvas-table* gcanv))
		(wdth  (slot-ref gv 'xpixels))
		(hgt   (slot-ref gv 'ypixels))
		(sz    (slot-ref vg 'size))
		(txo   (slot-ref vg 'label-offset))
		(xlo   (car txo))
		(ylo   (cadr txo))
		(os    (quotient sz 2))
		(offset (+ os 5))
	    	(x (cond ((< view-x 0) offset)
			 ((> view-x wdth) (- wdth offset))
			 (#t view-x)))
	    	(y (cond ((< view-y 0) offset)
			 ((> view-y hgt) (- hgt offset))
			 (#t view-y))))
            (link:grid-pointer-motion gcanv view-x view-y)
	    (set-vertex-object-coords! (slot-ref vg 'vertex-object) x y sz)
            (slot-set! (slot-ref vg 'txt-item) 'coords 
				(list (+ xlo x) (+ ylo y)))))

(define (find-and-update-vertex-item w view-x view-y)
	 (let* ((gcanv (slot-ref w 'parent))
		(gv (hash-table-get *link:canvas-table* gcanv))
		(vttab (slot-ref gv 'vertex-tag-table))
		(vg (hash-table-get vttab (cid w))))
	  (update-vertex-coords vg view-x view-y)))

(define (update-incoming-segment w x y r verts segs vtab)
	   (let* ((seg1 (list-ref segs (- r 1)))
		  (s1(slot-ref seg1 'line-item))
		  (s1crds (slot-ref s1 'coords))
		  (mid (list (list-ref s1crds 2) (list-ref s1crds 3)))
	   	  (v1 (ref verts (- r 1)))
	   	  (vg1 (hash-table-get vtab v1))
		  (p2 (offset-point w vg1)))
		(cond ((not (smooth s1))            ; star-draw
			(let ((cs (coords s1)))
			   (slot-set! s1 'coords 
				(append (list (car cs) (cadr cs) (caddr cs)
					      (cadddr cs)) 
					p2))))
		       (#t 			   
			(let* ((c1 (slot-ref vg1 'coords))
		  	  (p1 (offset-point vg1 w)))
			(slot-set! s1 'coords (append p1 mid p2)))))))

(define (update-outgoing-segment w x y r verts segs vtab)
	  (let* ((seg2 (list-ref segs r))
		  (s2(slot-ref seg2 'line-item))
		  (s2crds (slot-ref s2 'coords))
		  (mid (list (list-ref s2crds 2) (list-ref s2crds 3)))
	   	  (v2 (ref verts (+ r 1)))
	   	  (vg2 (hash-table-get vtab v2))
		  (p1 (offset-point w vg2)))
		(cond ((not (smooth s2))            ; star-draw
			   (slot-set! s2 'coords 
				(append p1 (list-tail (coords s2) 2))))
		       (#t 			   
			(let*  ((c2 (slot-ref vg2 'coords))
		  		(t2(slot-ref seg2 'txt-item))
		  		(p2 (offset-point vg2 w)))
			   (slot-set! s2 'coords (append p1 mid p2)))))))

(define (update-edges w x y v vtab etab edges)
	(cond ((not (null? (hash-table->list etab)))
	    (map (lambda (e) 
		    (let* ((eg (hash-table-get etab e)) 
			   (verts (vertices e))
			   (sz    (collection-vertex-size verts))
			   (r     (collection-vertex-rank verts v))
			   (segs (slot-ref eg 'seg-list)))
			(if (> r 0)
			   (update-incoming-segment w x y r verts segs vtab))
			(if (< r (- sz 1))
			   (update-outgoing-segment w x y r verts segs vtab))))
	     (set-edge->list edges)))))

(define (layout-vertex-item w world-x world-y)
	 (let* ((gcanv (slot-ref w 'parent))
		(gv (hash-table-get *link:canvas-table* gcanv))
		(v (slot-ref w 'vertex))
		(edges (incident-edges v))
		(etab (slot-ref gv 'edge-table))
		(vtab (slot-ref gv 'vertex-table))
		(vcrds (world-to-view world-x world-y gv))
		(x (car vcrds))
		(y (cadr vcrds)))
	    (update-vertex-coords w x y)))

(define (move-vertex-kbd w world-x world-y)
	 (let* ((gcanv (slot-ref w 'parent))
		(gv (hash-table-get *link:canvas-table* gcanv))
		(v (slot-ref w 'vertex))
		(edges (incident-edges v))
		(etab (slot-ref gv 'edge-table))
		(vtab (slot-ref gv 'vertex-table))
		(vcrds (world-to-view world-x world-y gv))
		(x (car vcrds))
		(y (cadr vcrds)))
	    (update-vertex-coords w x y)
	    (update-edges w x y v vtab etab edges)))

(define (move-vertex-mouse w view-x view-y)
	 (let* ((gcanv (slot-ref w 'parent))
		(gv (hash-table-get *link:canvas-table*
				gcanv))
		(v (slot-ref w 'vertex))
		(edges (incident-edges v))
		(etab (slot-ref gv 'edge-table))
		(vtab (slot-ref gv 'vertex-table))
		(wcrds (view-to-world view-x view-y gv)))
            (set-double-attribute! 'x (car wcrds) v)
            (set-double-attribute! 'y (cadr wcrds) v)
	    (update-vertex-coords w view-x view-y)
	    (update-edges w view-x view-y v vtab etab edges)
))

(define (move-edge-segment tx x-in y-in)
         (let* ((gcanv (slot-ref tx 'parent))
                (gv    (hash-table-get *link:canvas-table* gcanv))
		(wdth  (slot-ref gv 'xpixels))
		(hgt   (slot-ref gv 'ypixels))
                (segtab (slot-ref gv 'segment-table))
                (seg (hash-table-get segtab (slot-ref tx 'cid)))
                (ln  (slot-ref seg 'line-item))
                (crds (slot-ref ln 'coords))
                (p2 (cddddr crds))
		(x (cond ((< x-in 0) 10)
			 ((> x-in wdth) (- wdth 10))
			 (#t x-in)))
		(y (cond ((< y-in 0) 10)
			 ((> y-in hgt) (- hgt 10))
			 (#t y-in))))
	    (slot-set! seg 'coords (list x y))
            ;(slot-set! tx 'coords (list x y))
            ;(slot-set! ln 'coords (list (car crds) (cadr crds) x y
            ;                            (car p2)   (cadr p2)))
	))

(define *link:vertex-placeholder* #f)

(define (start-vertex-drag w x y) 
   (let* ((gcanv (slot-ref w 'parent))
                (gv    (hash-table-get *link:canvas-table* gcanv))
                (vttab (slot-ref gv 'vertex-tag-table))
		(vg (hash-table-get vttab (cid w)))
	        (crds (slot-ref vg 'coords)))
        (set! *link:vertex-placeholder* 
		(make <oval> :parent (slot-ref w 'parent) 
			     :coords (list (- (car crds) 5)
			                   (- (cadr crds) 5)
			                   (+ 5 (car crds))
			                   (+ 5 (cadr crds)))))))

(define (stop-vertex-drag w x y) 
         (let* ((gcanv (slot-ref w 'parent))
                (gv    (hash-table-get *link:canvas-table* gcanv))
                (vttab (slot-ref gv 'vertex-tag-table))
		(vg (hash-table-get vttab (cid w)))
	        (crds (slot-ref vg 'coords)))
           (move-vertex-mouse vg x y)
	   (if (slot-ref gv 'snap-to-grid) (snap-to-grid vg)
	            (update-edges vg (car crds) (cadr crds) 
				     (slot-ref vg 'vertex)
				     (slot-ref gv 'vertex-table)
				     (slot-ref gv 'edge-table) 
				     (incident-edges (slot-ref vg 'vertex)))))
        (my-destroy *link:vertex-placeholder*))

(define (vertex-motion w x y)
        (update-vertex-coords w x y))

(define (graph-object-motion w vx vy)
	    (cond ((member "vertex" (tags w)) 
			(move-vertex-mouse 
			    (hash-table-get 
				(slot-ref (hash-table-get *link:canvas-table* 
					(slot-ref w 'parent))
					'vertex-tag-table) (cid w)) vx vy))
		  (#t (move-edge-segment w vx vy))))

(define (update-extra-canvas-objects gv old-width old-height 
					new-width new-height)
	(let* ((ex-objs (slot-ref gv 'extra-canvas-objects)))
	   (map (lambda (ob)
		  (let*((vc     (slot-ref ob 'coords))
			(wcrds (append  (view-to-world2 (car vc) (cadr vc) 
							old-width old-height)
		            		(view-to-world2 (caddr vc) (cadddr vc) 
							old-width old-height)))
                        (new-vcrds 
				(append 
				  (world-to-view2 (car wcrds) (cadr wcrds) 
						  new-width   new-height)
				  (world-to-view2 (caddr wcrds) (cadddr wcrds)
						  new-width   new-height)))
			)
		      (slot-set! ob 'coords new-vcrds))) ex-objs))
)

(define (create-vertex-item gv vtab vttab v clone)
	  (let*((world-crds (list (find-double-attribute 'x v)
		                  (find-double-attribute 'y v)))
		(view-crds (world-to-view 
				(car world-crds) (cadr world-crds) gv))
		(vcolor (if clone (find-string-attribute 'color v)
				  *link:default-vertex-color*))
		(vsize (if clone (find-attribute 'size v)
				  *link:default-vertex-size*))
		(vlabel (if clone 
				 (find-string-attribute 'label v)
				 (vertex-label v)))
		(vg (make <vertex-item> 
			:parent (slot-ref gv 'graph-canvas)
		        :vertex v
			:graph-view gv
			:color vcolor
			:size vsize
			:vertex-label vlabel
			:coords view-crds)))
	      (if (slot-ref gv 'snap-to-grid) (snap-to-grid vg))
	      (hash-table-put! vtab v vg)
              (let*((tx (slot-ref vg 'txt-item))
                    (vo (slot-ref vg 'vertex-object))
		    (citems (find-items (slot-ref gv 'graph-canvas) 'withtag
                                        (slot-ref vo 'cid))))
                 (map (lambda (x) (hash-table-put! vttab (cid x) vg))
                      citems)
                 (hash-table-put! vttab (slot-ref vo 'cid) vg)
                 (hash-table-put! vttab (slot-ref tx 'cid) vg))
))

(define (create-vertex-items g gv order vtab vttab clone)
       (do ((i 0 (+ i 1)))
	  ((>= i order) #f)
	  (create-vertex-item gv vtab vttab (vertex-ref g i) clone)))

(define (create-vertex-items-from-set gv vset vtab vttab clone)
	(map (lambda (x) (create-vertex-item gv vtab vttab x clone))
		(set-vertex->list vset)))

(define (create-edge-item gv etab e clone)
	(let*  ((ecolor (if clone (find-string-attribute 'color e)
				  *link:default-edge-color*))
		(ewidth (if clone (find-attribute 'width e)
				  *link:default-edge-width*))
		(elabel (if clone (find-string-attribute 'label e)
				  (edge-label e)))
	        (eg (make <edge-item>
		:parent (slot-ref gv 'graph-canvas)
		:graph-view gv
		:color ecolor
		:width ewidth
		:edge-label elabel
		:edge e))
	       (segs (slot-ref eg 'seg-list)))
	  (hash-table-put! etab e eg)
	))

(define (create-edge-items-from-set gv edge-set clone)
   (let  ((g    (slot-ref gv 'graph))
          (etab (slot-ref gv 'edge-table))) 
       (map (lambda (e) 
		(create-edge-item gv etab e clone))
	     (set-edge->list edge-set))))
;tag tables are set in add-edge-segment-item

(define (create-edge-items gv clone)
	(create-edge-items-from-set gv (edges (slot-ref gv 'graph)) clone))


(define-class <graph-view> (<Tk-composite-widget>)
  ((graph-toplevel)  ; the <toplevel> object associated with this <graph-view>
   (graph-canvas)    ; the <canvas> object associated with this <graph-view>
   (vertex-table)    ; given a <vertex*> object, look up its <vertex-item>
   (edge-table)      ; given an <edge*> object, look up its <edge-item>
   (segment-table)   ; given tag of <line> or <text-item>, look up 
		     ; <edge-segment-item>.  Used for dragging edge segments.
   (vertex-tag-table); given tag of <text-item>, look up <vertex-item>.
		     ; Used for selection of vertices.
   (edge-tag-table)  ; given tag of <text-item> or <line>, look up <edge-item>.
		     ; Note that the asymmetry with vertex-tag-table is due
		     ; to the structure of the objects; highlighting the
		     ; <guaded-oval> item of a <vertex-item> is sufficient
		     ; to highlight the vertex.  However, simply highlighting
		     ; the <line> of an <edge-segment-item> is not sufficent
		     ; to highlight a hyperedge. We need to find the<edge-item>
		     ; and update all of its segments
   (name		:accessor name :init-keyword :name)
   (graph		:accessor graph :init-keyword :graph)
   (layout		:accessor layout :init-keyword :layout)
   (extra-objects	:accessor extra-objects)
   (extra-canvas-objects :accessor extra-canvas-objects :init-keyword
				   :extra-canvas-objects)
   (grid-objects	:accessor grid-objects :init-keyword :grid-objects)
   (snap-to-grid	:accessor snap-to-grid :init-keyword :snap-to-grid)
   (xpixels 		:accessor xpixels :init-keyword :xpixels
                        :allocation :virtual
			:slot-ref (lambda (o)
				(let ((c (slot-ref o 'graph-canvas)))
						(slot-ref c 'width)))
			:slot-set! (lambda (o w)
				(let* ((c (slot-ref o 'graph-canvas))
				      (new-w w)
				      (new-h (slot-ref c 'height))
				      (old-w (slot-ref c 'width))
				      (old-h (slot-ref c 'height))
				      (verts
					  (map cdr (hash-table->list 
					 	(slot-ref o 'vertex-table))))
				      (wcrds 
				       	  (map 	
				          	(lambda(x) 
						   (slot-ref x 'world-coords))
					   verts)))
				   (slot-set! c 'width w)
				   ;;; update vertex locations
				   (destroy-edge-items o)
				   (map (lambda (v crds)
					    (layout-vertex-item v (car crds)
							   (cadr crds)))
					verts wcrds)
				   (remove-gridding o)
				   (update-extra-canvas-objects o old-w old-h
								  new-w new-h)
				(create-edge-items o 'clone))))
   (ypixels 		:accessor ypixels :init-keyword :ypixels
                        :allocation :virtual
			:slot-ref (lambda (o)
				(let ((c (slot-ref o 'graph-canvas)))
						(slot-ref c 'height)))
			:slot-set! (lambda (o h)
				(let* ((c (slot-ref o 'graph-canvas))
				      (new-w (slot-ref c 'width))
				      (new-h h)
				      (old-w (slot-ref c 'width))
				      (old-h (slot-ref c 'height))
				      (verts
					  (map cdr (hash-table->list 
					 	(slot-ref o 'vertex-table))))
				      (wcrds 
				       	  (map 	
				          	(lambda(x) 
						   (slot-ref x 'world-coords))
					   verts)))
				   (slot-set! c 'height h)
				   ;;; update vertex locations
				   (destroy-edge-items o)
				   (map (lambda (v crds)
					    (layout-vertex-item v (car crds)
							   (cadr crds)))
					verts wcrds)
				   (remove-gridding o)
				   (update-extra-canvas-objects o old-w old-h
								  new-w new-h)
				   (create-edge-items o 'clone))))
    (title			:accessor title
				:init-keyword :title
				:allocation :propagated
				:propagate-to (graph-toplevel))
))

(define-method initialize ((self <graph-view>) initargs)
  (next-method)
  (let* ((g2         (get-keyword :graph initargs ""))
         (xpixls     (get-keyword :xpixels initargs 385))
         (ypixls     (get-keyword :ypixels initargs 300))
         (layout     (get-keyword :layout initargs 'random))
         (clone      (get-keyword :clone initargs #f))
	 (g          (copy-graph g2 1))  ; 1 -> copy attributes
         (order      (order g))
         (size       (size g)))
    (slot-set! self 'graph g)
    (slot-set! self 'extra-objects '())
    (slot-set! self 'extra-canvas-objects '())
    (slot-set! self 'grid-objects '())
    (slot-set! self 'snap-to-grid #f)
    (slot-set! self 'layout layout) ; not updated yet
    (slot-set! self 'vertex-table (make-hash-table (lambda (x y) 
					(equal? (get-label x) 
						(get-label y)))))
    (slot-set! self 'edge-table (make-hash-table (lambda (x y) 
					(equal? (get-label x) 
						(get-label y)))))
    (slot-set! self 'segment-table (make-hash-table))
    (slot-set! self 'vertex-tag-table (make-hash-table))
    (slot-set! self 'edge-tag-table (make-hash-table))
    (slot-set! self 'graph-toplevel (make <toplevel> 
			:title *link:default-graph-view-title*))
    (wm 'minsize (slot-ref self 'graph-toplevel) xpixls ypixls)
    (slot-set! self 'graph-canvas (make <canvas> 
			:parent (slot-ref self 'graph-toplevel)
			:background "white"
			:width xpixls
			:height ypixls))
    (slot-set! self 'name (my-gensym "graph-view"))
    (hash-table-put! *link:canvas-table* (slot-ref self 'graph-canvas) self)
    (hash-table-put! *link:graph-view-table* (slot-ref self 'name) self)
    (hash-table-put! *link:graph-view-table* (owner g) self)
    (let*((f (make <frame> :parent (slot-ref self 'graph-toplevel)))
	  (l (make <label> :parent f
			   :padx 0 :text "Selection"))
          (l2(make <label> :parent f
			   :padx 0 :text "Movement")))
	   (add-balloon-help (id l) 
		"<left> and <Shift-left>" 10 "yellow")
	   (add-balloon-help (id l2) 
		"vertices, labels: <Control-left>, edges: <left>" 
			10 "yellow")
	   (pack l2 l :side 'left :expand 'false)
	   (pack f :side 'top :expand 'false))
    (let ((c (slot-ref self 'graph-canvas))
	  (vtab (slot-ref self 'vertex-table))
	  (etab (slot-ref self 'edge-table))
	  (stab (slot-ref self 'segment-table))
	  (ettab (slot-ref self 'edge-tag-table))
	  (vttab (slot-ref self 'vertex-tag-table)))
       (pack c :expand 'true :fill 'both)

       (create-vertex-items g self order vtab vttab clone)
       (create-edge-items self clone)

       ;(slot-set! self 'xpixels xpixls) 
       ; don't want to set ypixels here - edges would be redrawn
       ; it will be evaluated when user asks

    (cond ((eq? layout 'random)    (random-layout* g self))
          ((eq? layout 'circular)  (circle-layout* g self))
          ((eq? layout 'spring)    (spring-layout* g self))
          ((eq? layout 'custom) #f)	;; do nothing - coords already set.
          ((eq? layout 'bipartite) (bipartite-layout* g self)))

       (link:extract-induced-subgraph-binding self)
       (link:collapse-subgraph-binding self)
       (link:expand-subgraph-binding self)
       (link:button-3-binding c *link:canvas-table*)
       (link:cntl-button-3-binding c *link:canvas-table*)
       (link:hyperedge-continuation-binding c *link:canvas-table*)
       (link:selection-binding self)
       (link:selection-box-binding c)
       (link:add-selection-binding c vttab ettab)
       (link:selection-release-binding c vttab ettab)
       (link:edge-drag-binding c)
       (link:deletion-binding self)
       (link:vertex-enter-binding c)
       (link:vertex-leave-binding c)
    )
  )
  (define ms (menu-string self))
  (define m (make-menubar (slot-ref self 'graph-toplevel) ms))
  (pack m :before (slot-ref self 'graph-canvas) 
	  :side 'top :fill 'x)
  ;(bind (slot-ref self 'graph-canvas) "<Configure>"   ok when done from STk,
  ;   (lambda () (update-graph-view  self)))	       but infinite from here
)

(define-method update-graph-view ((gv <graph-view>))
	(slot-set! gv 'xpixels (winfo 'width (slot-ref gv 'graph-canvas)))
	(slot-set! gv 'ypixels (winfo 'height (slot-ref gv 'graph-canvas))))

(define (place-edge ei)
        (let* ((e  (edge ei))
               (es (car (slot-ref ei 'seg-list)))
               (xcrd (find-double-attribute 'x e))
               (ycrd (find-double-attribute 'y e))
               (vcrds (world-to-view xcrd ycrd (graph-view ei))))
           (slot-set! es 'coords vcrds)))

(define (place-edges gv)
        (map place-edge (edges gv)))

(define (update-visible-attributes gv)
	(map (lambda(x) (slot-ref x 'color)) (vertices gv))
	(map (lambda(x) (slot-ref x 'size)) (vertices gv))
	(map (lambda(x) (slot-ref x 'width)) (vertices gv))
	(map (lambda(x) (slot-ref x 'vertex-label)) (vertices gv))
	(map (lambda(x) (slot-ref x 'color)) (edges gv))
	(map (lambda(x) (slot-ref x 'width)) (edges gv))
	(map (lambda(x) (slot-ref x 'edge-label)) (edges gv))
	(place-edges gv))

;; outdated  - replaced by remove-vertex-item!
(define (destroy-vertex-item vg gv)
	(let ((h (slot-ref gv 'vertex-table))
	      (v (slot-ref vg 'vertex))) 
		(cond ((hash-table-get h v #f)
			      	(hash-table-remove! h v) 
			      	(my-destroy vg)))))

;; outdated  - replaced by remove-edge-item!
(define (destroy-edge-item eg gv)
	(let ((h (slot-ref gv 'edge-table))
	      (e (slot-ref eg 'edge))) 
		(cond ((hash-table-get h e #f)
			      	(hash-table-remove! h e) 
			      	(my-destroy eg)))))

;; outdated  - replaced by remove-vertex-items!
(define-method destroy-vertex-items ((gv <graph-view>))
    (let ((h (slot-ref gv 'vertex-table))) 
    	(hash-table-for-each h 
		(lambda (k v)
			      (cond ((hash-table-get h k #f)
			      		(hash-table-remove! h k) 
			      		(my-destroy v)))))))

;; outdated  - replaced by remove-edge-items!
(define-method destroy-edge-items ((gv <graph-view>))
    (let ((h (slot-ref gv 'edge-table))) 
    	(hash-table-for-each h 
		(lambda (k v) 
			      (cond ((hash-table-get h k #f)
			      		(hash-table-remove! h k) 
			      		(my-destroy v)))))))


(define-generic graph-view?)
(define-method graph-view? ((v <graph-view>)) #t)
(define-method graph-view? ((v <top>)) #f)

(define-generic vertex-item?)
(define-method vertex-item? ((v <vertex-item>)) #t)
(define-method vertex-item? ((v <top>)) #f)

(define-generic edge-item?)
(define-method edge-item? ((edge <edge-item>)) #t)
(define-method edge-item? ((edge <top>)) #f)

(define (next-vertex-name g)
    (if (= (order g) 0) 1
	(let ((n (string->number (vertex-label (vertex-ref g (- (order g)1))))))
	   (cond ((integer? n) (+ n 1))
		 (#t (my-gensym "v"))))))

;
; add-vertex-item!
; add-edge-item!		called from graph-edit.stklos (button 3 binding)
;
;
(define-generic add-vertex-item!)
(define-method add-vertex-item! ((gv <graph-view>) (vx <number>)
                                                          (vy <number>))
        (let*  ((g (slot-ref gv 'graph))
                (v (add-vertex! (next-vertex-name g) g))
                (wcrds (view-to-world vx vy gv)))
          (set-double-attribute! 'x (car wcrds)  v)
          (set-double-attribute! 'y (cadr wcrds) v)
          (let* ((vg (make <vertex-item> :parent (slot-ref gv 'graph-canvas)
					 :graph-view gv
                                         :vertex v
                                         :coords (list vx vy))))
		(slot-set! vg 'size *link:default-vertex-size*)
		(if (slot-ref gv 'snap-to-grid) (snap-to-grid vg))
                (hash-table-put! (slot-ref gv 'vertex-table) v vg)
                (let*((tx (slot-ref vg 'txt-item))
                      (vo (slot-ref vg 'vertex-object))
		      (citems (find-items (slot-ref gv 'graph-canvas) 'withtag
                                        (slot-ref vo 'cid))))
                 (map (lambda (x) 
                        (hash-table-put! (slot-ref gv 'vertex-tag-table) 
					 (cid x) vg))
                      citems)
                 (hash-table-put! (slot-ref gv 'vertex-tag-table)
                                        (slot-ref vo 'cid) vg)
                 (hash-table-put! (slot-ref gv 'vertex-tag-table)
                                        (slot-ref tx 'cid) vg)
		)
		vg)))

(define-generic add-edge-item!)
(define-method add-edge-item! ((vs <sequence<vertex*>>) (gv <graph-view>))
        (let*  ((g  (slot-ref gv 'graph))
                (e  (cond ((directed? g) (add-edge! vs g))
                          ((mixed? g)    (cond (*link:undirected-in-mixed*
						(add-edge! (set-vertex vs) g))
					       (#t (add-edge! vs g))))
			  (#t (add-edge! (set-vertex vs) g)))))
	  (cond ((not e) #f)
                (#t 
	   (let*((eg (make <edge-item>
                  :parent (slot-ref gv 'graph-canvas)
		  :graph-view gv
                  :edge e))
                (segs (slot-ref eg 'seg-list)))
	  (slot-set! eg 'width *link:default-edge-width*)
          (hash-table-put! (slot-ref gv 'edge-table) e eg)
          (map (lambda (s)
                (let ((tx (slot-ref s 'txt-item))
                      (ln (slot-ref s 'line-item)))
                   (hash-table-put! (slot-ref gv 'segment-table)
                                        (slot-ref tx 'cid) s)
                   (hash-table-put! (slot-ref gv 'segment-table)
                                        (slot-ref ln 'cid) s)
                   (hash-table-put! (slot-ref gv 'edge-tag-table)
                                        (slot-ref tx 'cid) eg)
                   (hash-table-put! (slot-ref gv 'edge-tag-table)
                                        (slot-ref ln 'cid) eg))) 
		segs)eg)))))

(define-method add-edge-item! ((v1 <vertex*>) (v2 <vertex*>) gv)
                 (clear! *link:new-edge-vertices*)
                 (append! v1 *link:new-edge-vertices*)
                 (append! v2 *link:new-edge-vertices*)
                 (add-edge-item! *link:new-edge-vertices* gv))

(define-method add-edge-item! ((v1 <vertex-item>) (v2 <vertex-item>) gv)
	(add-edge-item! (vertex v1) (vertex v2) gv))


(define (remove-from-list e l)	; removes first occurrence of e in l
	(cond ((or (not (list? l)) (null? l)) '())
	      ((equal? e (car l)) (cdr l))
	      (#t (cons (car l) (remove-from-list e (cdr l))))))

(define (insert-in-list e i l)	; insert e as i'th element of l
	(cond ((or (not (list? l)) (null? l)) (list e))
	      ((> i (+ (length l) 1)) l)
	      ((= i 1) (cons e l))
	      (#t (cons (car l) (insert-in-list e (- i 1) (cdr l))))))

(define (remove-edge-segment-item! eg es gv)
	(let   ((tx (slot-ref es 'txt-item))
		(ln (slot-ref es 'line-item)))
	   (hash-table-remove! (slot-ref gv 'segment-table) (slot-ref tx 'cid))
	   (hash-table-remove! (slot-ref gv 'segment-table) (slot-ref ln 'cid))
	   (hash-table-remove! (slot-ref gv 'edge-tag-table)(slot-ref tx 'cid))
	   (hash-table-remove! (slot-ref gv 'edge-tag-table)(slot-ref ln 'cid))
	   (my-destroy es))
	(slot-set! eg 'seg-list (remove-from-list es (slot-ref eg 'seg-list))))

(define-generic remove-edge-item!)
(define-method remove-edge-item! ((eg <edge-item>) (gv <graph-view>))
        (let*  ((g  (slot-ref gv 'graph))
		(e  (slot-ref eg 'edge))
                (segs (slot-ref eg 'seg-list)))
          (hash-table-remove! (slot-ref gv 'edge-table) e)
          (map (lambda (s) (remove-edge-segment-item! eg s gv)) segs)))

(define (remove-edge-items! gv)
	(map (lambda (ei) (remove-edge-item! ei gv))
		(edges gv)))

(define-generic remove-edge-and-edge-item!)
(define-method remove-edge-and-edge-item! ((eg <edge-item>) (gv <graph-view>))
        (let*  ((g  (slot-ref gv 'graph))
		(e  (slot-ref eg 'edge))
                (segs (slot-ref eg 'seg-list)))
          (hash-table-remove! (slot-ref gv 'edge-table) e)
	  (remove-edge! e g)
          (map (lambda (s) (remove-edge-segment-item! eg s gv)) segs)))

(define (remove-edges-and-edge-items! gv)
	(map (lambda (ei) (remove-edge-and-edge-item! ei gv))
		(edges gv)))

(define (last l)	; is there a better way?
	(cond ((null? l) #f)
	      (#t (car (list-tail l (- (length l) 1))))))

(define (shrink-edge-item!  eg vertex gv)
   (if eg
	(let* ((verts (vertices (slot-ref eg 'edge)))
	       (sz    (collection-vertex-size verts))
	       (ecolor (color eg))
	       (ewidth (slot-ref eg 'width))
	       (vtab  (slot-ref gv 'vertex-table))
	       (r     (collection-vertex-rank verts vertex))
	       (segs (slot-ref eg 'seg-list)))
	  (cond ((= sz 2) (remove-edge-and-edge-item! eg gv))
		((and (> r 0) (< r (- sz 1))) 
		   (remove-edge-segment-item! eg (list-ref segs (- r 1)) gv)
		   (remove-edge-segment-item! eg (list-ref segs r) gv)
		   (let ((new-segs (add-edge-segment-item! 
				(hash-table-get vtab (ref verts (- r 1)))
				(hash-table-get vtab (ref verts (+ r 1)))
				(slot-ref gv 'graph-canvas)
				eg ecolor ewidth 
				(edge-label (slot-ref eg 'edge))
				verts r 
				gv
				(slot-ref eg 'seg-list))))
			(slot-set! eg 'seg-list new-segs)))
		((= r 0) (remove-edge-segment-item! eg (car segs) gv))
		((= r (- sz 1)) 
			 (remove-edge-segment-item! eg (last segs) gv))))))

(define-generic remove-vertex-item!)
(define-method remove-vertex-item! ((vg <vertex-item>) (gv <graph-view>))
        (let*  ((g  (slot-ref gv 'graph))
		(v  (slot-ref vg 'vertex))
		(egl (set-edge->list (incident-edges v))))
          (map  (lambda (s) (shrink-edge-item! 
				(hash-table-get (slot-ref gv 'edge-table) s #f)
				v gv))  egl)
          (let ((tx (slot-ref vg 'txt-item))
                (vo (slot-ref vg 'vertex-object)))
             (hash-table-remove! (slot-ref gv 'vertex-tag-table)
                                     (slot-ref tx 'cid))
             (hash-table-remove! (slot-ref gv 'vertex-tag-table)
                                        (slot-ref vo 'cid)))
          (hash-table-remove! (slot-ref gv 'vertex-table) v)
	  (my-destroy vg)))

(define (remove-vertex-items! gv)
	(map (lambda (vi) (remove-vertex-item! vi gv)) (vertices gv)))

(define-generic remove-vertex-and-vertex-item!)
(define-method remove-vertex-and-vertex-item! 
			((vg <vertex-item>) (gv <graph-view>))
        (let*  ((g  (slot-ref gv 'graph))
		(v  (slot-ref vg 'vertex)))
          (map  (lambda (s) (shrink-edge-item! 
				(hash-table-get (slot-ref gv 'edge-table) s #f)
				v gv))
		(set-edge->list (incident-edges v)))
          (let ((tx (slot-ref vg 'txt-item))
                (vo (slot-ref vg 'vertex-object)))
             (hash-table-remove! (slot-ref gv 'vertex-tag-table)
                                     (slot-ref tx 'cid))
             (hash-table-remove! (slot-ref gv 'vertex-tag-table)
                                        (slot-ref vo 'cid)))
          (hash-table-remove! (slot-ref gv 'vertex-table) v)
	  (remove-vertex! v g)
	  (my-destroy vg)))

(define (replace-graph g gv)
        (hash-table-remove! *link:graph-view-table* (owner (graph gv)))
	(remove-edge-items! gv)	
	(remove-vertex-items! gv)
	(slot-set! gv 'graph g)
        (hash-table-put! *link:graph-view-table* (owner g) gv)
	(slot-set! gv 'title "LINK: graph view")
	(create-vertex-items g gv (order g) 
				  (slot-ref gv 'vertex-table)
				  (slot-ref gv 'vertex-tag-table) #t)
	(create-edge-items gv #f))

(define (replace-edge-items gv)
	(remove-edge-items! gv)
	(create-edge-items gv 1))

(define (reset-edge-marks g)
	(map (lambda(x) (set-attribute! 'mark 0 x))
		(set-edge->list (edges g))))

(define (gather-incident-edges vertex)
	(let ((es (mset-edge)))
	   (map (lambda (e) 
		   (cond ((= (find-attribute 'mark e) 0)
			     (insert! e es)
			     (set-attribute! 'mark 1 e))))
		(set-edge->list 
			(incident-edges vertex)))
	   es))

(define (extract-induced-subgraph vertex-item-list gv)
	(let ((subgraph-vertices (set-vertex)))
	   (map (lambda(x) (insert! (slot-ref x 'vertex) subgraph-vertices))
		vertex-item-list)
	   (let ((sg (induced-subgraph (graph gv) subgraph-vertices 1)))
		(set! *link:new-graph-view* 
			(make <graph-view> :graph sg :layout 'custom)))))

(define (extract-edge-induced-subgraph edge-item-list gv) ())

(define (collapse-subgraph vertex-item-list gv)
  (cond((not (and (binary?     (slot-ref gv 'graph)) 
		      (multigraph? (slot-ref gv 'graph))))
	  (error "collapse-subgraph:g must be binary and allow multiple edges"))
       ((not (and (list? vertex-item-list) 
			 (> (length vertex-item-list) 0))) #f)
       (#t
	(reset-edge-marks (graph gv))
	(let ((subgraph-edges (mset-edge))
	      (etab (slot-ref gv 'edge-table))
	      (vtab (slot-ref gv 'vertex-table))
	      (vttab (slot-ref gv 'vertex-tag-table))
	      (subgraph-vertices (set-vertex))
	      (n (length vertex-item-list))
	      (xsum  0)
	      (ysum  0))
	   (map (lambda(x) (insert! (slot-ref x 'vertex) subgraph-vertices))
		vertex-item-list)
	   (map (lambda(x) 
		    (set! subgraph-edges (+ subgraph-edges
			   		    (gather-incident-edges x))))
		(map (lambda(x) (slot-ref x 'vertex)) vertex-item-list))
	   (map (lambda(vg)
		    (let ((v (slot-ref vg 'vertex)))
			(set! xsum (+ xsum (find-double-attribute 'x v)))
			(set! ysum (+ ysum (find-double-attribute 'y v)))))
		vertex-item-list)
	   (map (lambda(e)
		    (let ((eg (hash-table-get etab e #f)))
			(remove-edge-item! eg gv)))
		(set-edge->list subgraph-edges))
	   (map (lambda(vg)
			(remove-vertex-item! vg gv)) 
		vertex-item-list)

	   (let* ((sv (add-subgraph (graph gv) subgraph-vertices)))
	      (set-double-attribute! 'x (/ xsum n) sv)
	      (set-double-attribute! 'y (/ ysum n) sv)
	      (create-vertex-item gv vtab vttab sv #t)
	      (create-edge-items-from-set gv (incident-edges sv) #f)))))
	(set! *link:selected-vertex-items* '()))

(define (expand-subgraph super-vertex gv)
	(if (not (and (binary? (slot-ref gv 'graph)) 
		      (multigraph? (slot-ref gv 'graph))))
	 (err "expand-subgraph:graph must be binary and allow multiple edges")
	)
	(let ((sv (slot-ref super-vertex 'vertex))
	      (etab (slot-ref gv 'edge-table))
	      (vtab (slot-ref gv 'vertex-table))
	      (vttab (slot-ref gv 'vertex-tag-table)))
	   (map (lambda(e)
		    (let ((eg (hash-table-get etab e #f)))
			(remove-edge-item! eg gv))) 
		(set-edge->list (incident-edges 
					(slot-ref super-vertex 'vertex))))
	   (remove-vertex-item! super-vertex gv)  ; JWB 7/6/99

	   (let* ((subgraph-vertices (set-vertex))
		  (subgraph-edges    (mset-edge))
		  (vs (dissolve-subgraph (graph gv) sv)))
	       (reset-edge-marks (graph gv))
	       (map (lambda(x) 
		     (set! subgraph-edges (+ subgraph-edges
					     (gather-incident-edges x))))
		(set-vertex->list vs))
	      (create-vertex-items-from-set gv vs vtab vttab 'clone)
	      (create-edge-items-from-set gv subgraph-edges 'clone))))
	

(define-method vertices ((gv <graph-view>))
	(map cdr (hash-table->list (slot-ref gv 'vertex-table))))

(define-method edges ((gv <graph-view>))
	(map cdr (hash-table->list (slot-ref gv 'edge-table))))

(require "labels")
(require "graphics")
(require "flash")
(require "star-draw")

(define-generic show-labeled-graph)
(define-method show-labeled-graph ((g <graph*>))
	(make <graph-view> :graph g :clone 'clone))

(define-method show-labeled-graph ((g <graph*>) (layout <symbol>))
	(make <graph-view> :graph g :clone 'clone :layout layout))

(define-generic show-graph)
(define-method show-graph ((g <graph*>))
	(let ((gv (make <graph-view> :graph g :clone 'clone :layout 'circular)))
		(reset-graphics gv) gv))

(define-method show-graph ((g <graph*>) (layout <symbol>))
	(let ((gv (make <graph-view> :graph g :clone 'clone :layout layout)))
		(reset-graphics gv) gv))

(define-generic color-edges)

(define-method color-edges ((gv <graph-view>) (es <pair>) (clr <top>))
	(map (lambda(x) 
		(set! (color x) clr))
	     es))

(define-method color-edges ((gv <graph-view>) (es <mset<edge*>>) (clr <top>))
	(map (lambda(x) 
		(let ((edg (hash-table-get (slot-ref gv 'edge-table) x #f))) 
			(if edg (set! (color edg) clr))))
	     (set-edge->list es)))

(define-method color-edges ((g <graph*>) (es <set<edge*>>) (clr <top>))
	(map (lambda(x) (set! (color x) clr))
		(set-edge->list es)))

(define-generic thicken-edges)

(define-method thicken-edges ((gv <graph-view>) (es <pair>) (wdth <top>))
	(map (lambda(x) 
		(set! (width x) wdth))
	     es))

(define-method thicken-edges ((gv <graph-view>) (es <mset<edge*>>) (wdth <top>))
	(map (lambda(x) 
		(let ((edg (hash-table-get (slot-ref gv 'edge-table) x #f))) 
			(if edg (set! (width edg) wdth))))
	     (set-edge->list es)))

(define-method thicken-edges ((g <graph*>) (es <set<edge*>>) (wdth <top>))
	(map (lambda(x) (set! (width x) wdth))
		(set-edge->list es)))



(provide "graph-view")
