;;;;
;;;; M u l t i p a n e d . s t k
;;;;
;;;; Rewritten version of Paned.stk to allow an arbitrary number of panes.
;;;; Modified by Harvey J. Stein <hjstein@math.huji.ac.il>
;;;; Modified again by Erick Gallesio for allowing horizontal and vertical
;;;; placement
;;;;
;;;; Copyright (C) 1993,1994,1995 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
;;;; 
;;;; Permission to use, copy, and/or distribute this software and its
;;;; documentation for any purpose and without fee is hereby granted, provided
;;;; that both the above copyright notice and this permission notice appear in
;;;; all copies and derived works.  Fees for distribution or use of this
;;;; software or derived works may only be charged with express written
;;;; permission of the copyright holder.  
;;;; This software is provided ``as is'' without express or implied warranty.
;;;;
;;;; Idea of this implementation was found in comp.lang.tcl. 
;;;; Original author seems to be James Noble and the version from which this 
;;;; stuff is derivated is Jay Schmidgall (shmdgljd@ghostwind.rchland.ibm.com).

(require "Frame")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; <MultiPaned> class definition
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-class <MultiPaned> (<Tk-composite-widget>)
  (;; Public slots
   (frames       :accessor     frames-of)
   (grips	 :accessor     grips-of)
   (panes        :accessor     panes-of	   :init-keyword :panes)
   (geometry	 :accessor     geometry	   :init-keyword :geometry
		 :allocation   :propagated :propagate-to (frame))
   ;; Virtual slot
   (orientation  :accessor      orientation :allocation :virtual
		 :slot-ref	(lambda (o)
				  (extend-environment (environment o)
				     (if vertical? "vertical" "horizontal")))
		 :slot-set!	(lambda (o v)
				  (extend-environment (environment o)
				     (set! vertical? (equal? v "vertical"))
				     (place-grips o))))
   ;; Private slots
   (environment  :accessor     environment)
   (fractions  	 :accessor     fractions-of)
   (grip-number  :accessor     grip-number-of)
   (drag-start   :accessor     drag-start-of)))

(define-method initialize-composite-widget ((self <MultiPaned>) initargs frame)
  (let* ((panes     (get-keyword :panes initargs 2))
	 (vertical? (equal? "vertical" 
			    (get-keyword :position  initargs "vertical")))
	 (fractions (make-vector panes))
	 (frames    (make-vector panes))
	 (grips     (make-vector (- panes 1)))
	 (ghost	    (make <Frame> :parent frame :border-width 2 :relief "ridge")))

    ;; Build the "container" frames
    (dotimes (i panes)
       (vector-set! fractions i (/ (+ i 1) panes))
       (vector-set! frames    i (make <Frame> :parent frame :border-width 2
				              :relief "raised")))

    ;; Build grips
    (dotimes (i (- panes 1))
       (let ((G (make <Frame> :parent frame :geometry "10x10" :border-width 2
			      :relief "raised" :cursor "crosshair")))
	 (vector-set! grips i G)
	 ;; Associate bindings to the newly created grip
	 (bind G "<Button-1>"        `(start-grip  ,(address-of self) %x %y "%W"))
	 (bind G "<B1-Motion>"       `(motion-grip ,(address-of self) %x %y))
	 (bind G "<ButtonRelease-1>" `(stop-grip   ,(address-of self) %x %y))))

    ;; Initialize slots
    (slot-set! self 'environment (the-environment))
    (slot-set! self 'panes 	 panes)
    (slot-set! self 'frames	 frames)
    (slot-set! self 'grips	 grips)
    (slot-set! self 'Id		 (Id frame))

    ;; Place grips
    (place-grips self)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; <MultiPaned> methods 
;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-method place-grips ((self <MultiPaned>))
  (extend-environment (environment self)
    (let ((f     0)
	  (fp    0))
      ;; Place frames and grips
      (dotimes (i (- panes 1))
	 (set! f (vector-ref fractions i))
	 (if vertical?
	     (begin
	       (place (vector-ref grips i)  :rely 0.95   :relx f  :anchor "center")
	       (place (vector-ref frames i) :y 0 :relh 1 :relx fp :relw (- f fp)))
	      (begin
		(place (vector-ref grips i)  :relx 0.95   :rely f  :anchor "center")
		(place (vector-ref frames i) :x 0 :relw 1 :rely fp :relh (- f fp))))
	  (raise (vector-ref grips i))
	  (set! fp f))
      
      ;; Last frame
      (if vertical?
	  (place (vector-ref frames (- panes 1))
		 :y 0 :relx f :relheight 1 :relwidth (- 1.0 f))
	  (place (vector-ref frames (- panes 1))
		 :x 0 :rely f :relwidth 1 :relheight (- 1.0 f))))))

;;;
;;; Methods for moving grips
;;;
(define-method find-grip ((self <MultiPaned>) w)
  (do ((i 0 (+ 1 i)))
      ((string=? w (widget->string
		    (slot-ref (vector-ref (grips-of self) i) 'id)))
       i)))

(define-method start-grip ((self <MultiPaned>) x y w)
  (extend-environment (environment self)
    (let* ((grip   (find-grip self w))
	   (ghost  (ghost-of self))
	   (fracts (fractions-of self))
	   (fr	 (vector-ref fracts grip)))
      
      (slot-set! self 'grip-number grip)
      (slot-set! self 'drag-start (cons x y))

      ;; Raise ghost
      (place 'forget ghost) ; otherwise old constaints seems to stay valid
      (if vertical?
	  (place ghost :rely 0 :relx fr :anchor "n" :relh 1)
	  (place ghost :relx 0 :rely fr :anchor "w" :relw 1))
      (raise ghost)

      ;; Raise current grip
      (raise (vector-ref (grips-of self)  grip)))))

(define-method motion-grip ((self <MultiPaned>) x y)
  (extend-environment (environment self)
     (let ((fraction (fractions-of self))
	   (gn 	  (grip-number-of self))
	   (ghost	  (ghost-of self)))

       (if vertical?
	   (begin
	     (vector-set! fraction 
			  gn
			  (max 0.0001
			       (min .9999
				    (+ (vector-ref fraction gn)
				       (/ (- x (car (drag-start-of self)))
					  (+ 1 (winfo 'width (frame-of self))))))))
	     (place ghost
		    :rely 0 :relheight 1 :relx (vector-ref fraction gn) :anchor "n")
	  (place (vector-ref (grips-of self) gn)
		 :rely 0.95 :relx (vector-ref fraction gn) :anchor "center"))
	   (begin
	     (vector-set! fraction 
			  gn
		       (max 0.0001
			    (min .9999
				 (+ (vector-ref fraction gn)
				    (/ (- y (cdr (drag-start-of self)))
				       (+ 1 (winfo 'height (frame-of self))))))))
	     (place ghost
		 :relx 0 :relwidth 1 :rely (vector-ref fraction gn) :anchor "w")
	     (place (vector-ref (grips-of self) gn)
		 :relx 0.95 :rely (vector-ref fraction gn) :anchor "center"))))))

(define-method stop-grip ((self <MultiPaned>) x y)
  (let* ((gn 	    (grip-number-of self))
	 (fractions (fractions-of self))
	 (cp        (vector-ref fractions gn)))
    (dotimes (i (- (panes-of self) 1))
      (cond 
       ((and (< i gn) (> (vector-ref fractions i) cp))
		(vector-set! fractions i cp))
       ((and (> i gn) (< (vector-ref fractions i) cp))
		(vector-set! fractions i cp)))))
  (lower (ghost-of self))
  (place-grips self))

(provide "Multipaned")

;;;
;;; Example of usage
;;;
;;; (define p (make <MultiPaned> 
;;; 		:panes 4 
;;; 		:geometry "400x400" 
;;; 		:orientation "horizontal"))
;;; 
;;; (define l0 (make <Label> :text "Lab0" :parent (vector-ref (frames-of p) 0)))
;;; (define l1 (make <Label> :text "Lab1" :parent (vector-ref (frames-of p) 1)))
;;; (define l2 (make <Label> :text "Lab2" :parent (vector-ref (frames-of p) 2)))
;;; (pack l0 l1 l2 :expand #t :fill "both")
;;; (pack p :expand #t)
;;; 
