;;;; Notepad.stklos 					-- Notepad widget
;;;;
;;;; Copyright © 1997-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
;;;; 
;;;; Permission to use, copy, modify, distribute,and license this
;;;; software and its documentation for any purpose is hereby granted,
;;;; 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.
;;;; This software is provided ``AS IS'' without express or implied
;;;; warranty.
;;;;
;;;;           Author: Erick Gallesio [eg@unice.fr]
;;;;    Creation date:  1-Feb-1997 11:43
;;;; Last file update:  3-Sep-1999 20:14 (eg)


(require "Tk-classes")

(select-module STklos+Tk)
(export select-tab unselect-tab current-tab)

(define-class <Notepad> (<Tk-composite-widget>)
  (%cache		; A small label used to cache the bottom of tabs
   %page		; The frame which contains the page associated to a tab
   %tabs		; All the tabs (first one is a fake one)
   (%current   :init-form 	#f)
   (class      :init-keyword   :class
	       :init-form      "Notepad")
   (width      :accessor  	width
	       :allocation	:propagated
	       :propagate-to	(%page))
   (height     :accessor  	height
	       :allocation	:propagated
	       :propagate-to	(%page))
   (background :accessor 	background
	       :init-keyword 	:background
	       :allocation	:virtual
	       :slot-ref	(lambda (o) (background (slot-ref o '%cache)))
	       :slot-set!	(lambda (o v)
				  (for-each (lambda (x) (slot-set! x 'background v))
					    (list* (slot-ref o 'frame)
						   (slot-ref o '%cache)
						   (slot-ref o '%page)
						   (slot-ref o '%tabs)))))
   (foreground :accessor 	foreground
	       :init-keyword 	:foreground
	       :allocation	:virtual
	       :slot-ref	(lambda (o) (foreground (car (slot-ref o '%tabs))))
	       :slot-set!	(lambda (o v)
				  (for-each (lambda (x) (slot-set! x 'foreground v))
					    (slot-ref o '%tabs))))))

(define-method initialize-composite-widget ((self <Notepad>) initargs frame)
  (let* ((w (get-keyword :width  initargs 500))
	 (h (get-keyword :height initargs 300))
	 (f (make <Frame> :parent frame :border-width 2 :relief "raised"
		  	  :width w :height h))
	 (c (make <Frame> :parent frame :border-width 0 :height 4)))
    (pack f :side 'bottom :fill 'both :expand #t)
    (pack 'propagate f #f)
    ;; Create a first button in the frame. This button is never mapped but serves 
    ;; as a prototype for next tabs
    (slot-set! self '%tabs   (list (make <Button> :parent frame)))
    (slot-set! self '%cache  c)
    (slot-set! self '%page   f)
    (slot-set! self 'Id	     (slot-ref frame 'Id))
    ;; Add some space on the left of the frame to have the first button 
    ;; not directly on the left side of the widget
;   (pack (make <Frame> :width 10 :height 3 :parent self) :side 'left)
    (bind self "<Configure>" (lambda () (place-cache self)))))


(define-method current-tab ((self <Notepad>))
  (slot-ref self '%current))


(define-method place-cache ((self <Notepad>))
  ;; Place the cache window (associated to the <Configure> event)
  (let* ((cache   (slot-ref self '%cache))
	 (current (slot-ref self '%current))
	 (bd      (border-width current)))
    (update 'idle)
    (place cache :in current :bordermode "inside"
	   :x -1 :y  (- (winfo 'height current) (* 2 bd))
	   :width (- (winfo 'width  current) (* 2 bd) -1) :height (+ (* 2 bd) 1))
    (raise cache)))

;=============================================================================
;
; 				<Notepad-Tab> class
;
;=============================================================================

(define-class <Notepad-Tab> (<Button>)
  ((action :accessor action :init-form list :init-keyword :action)
   (page   :accessor page   :init-form #f)))
  

(define-method initialize ((self <Notepad-Tab>) initargs)
  (let* ((parent  (get-keyword :parent initargs #f))
	 (current (slot-ref parent '%current)))
    (unless (is-a? parent <Notepad>)
      (error "Parent of tab ~S is not a <Notepad>" self))
    (next-method)
    (slot-set! self 'command (lambda () (select-tab self)))
    (slot-set! self 'active-background (background self))
    (slot-set! self 'highlight-thickness 0)
    (slot-set! parent '%tabs (append (slot-ref parent '%tabs) (list self)))
    (pack self :side 'left :fill 'y)
    ( (if current unselect-tab select-tab) self)
    (event 'generate parent "<Configure>"))) ; useful if the pad is bigger 
					     ; than previous ones

(define-method unselect-tab ((self <Notepad-Tab>))
  ;; Change graphical attributes of unselected tabs
  (pack 'forget (page self))
  (slot-set! self 'relief "sunken")
  (slot-set! self 'foreground  (slot-ref self 'disabled-foreground)))


(define-method select-tab ((self <Notepad-Tab>))
  (let* ((parent  (slot-ref self 'parent))
	 (proto   (car (slot-ref parent '%tabs)))
	 (current (slot-ref parent '%current)))
    ;; Change grapical attributes of selected tabs
    (slot-set! self 'relief "raised")
    (slot-set! self 'foreground (slot-ref proto 'foreground))
    ;; Change the current item
    (if (and current (not (eqv? current self)))
	(unselect-tab current))
    (slot-set! parent '%current self)
    (event 'generate parent "<Configure>")
    ;; Call the associated action
    ((slot-ref self 'action) (slot-ref parent '%page) self)))


(provide "Notepad")

#|
   Example: 

    ;; Fist define the actions associated to the tab
    ;;
    (define (Host parent tab)
      (unless (page tab)      	; First call. Create the interface
	(let* ((f  (make <Frame>  :parent parent :border-width 3 
			 :background "darkgray" :relief "groove"))
	       (b1 (make <Labeled-Entry> :parent f :title "Host: ")) 
	       (b2 (make <Labeled-Entry> :parent f :title "Port: ")))
	  (pack b1 b2 :fill 'x :padx 10 :pady 10)
	  (set! (page tab) f)))
      (pack (page tab) :padx 10 :pady 10 :fill "both" :expand #t))

    (define (Mess parent tab)
      (unless (page tab)      	; First call. Create the interface
	(let ((m (make <Message> :parent parent :border-width 3 
		       :relief "groove" :background "darkgray" :aspect 300
		       :justify "center" :font "10x20" 
		       :text "This is a simple demonstration.")))
	  (pack m :expand #t :fill "both")
	  (set! (page tab) m)))
      (pack (page tab) :padx 10 :pady 10 :fill "both" :expand #t))


    ;; Let's go
    ;;
    (define f (make <NotePad> :width 450 :height 150))
    (pack f :expand #t :fill "both" :padx 2 :pady 2)

    (make <Notepad-Tab> :parent f :text "host1"             :action Host)
    (make <Notepad-Tab> :parent f :text "host2"             :action Host)
    (make <Notepad-Tab> :parent f :text "Multi-line\nlabel" :action Mess)
    (make <Notepad-Tab> :parent f :bitmap "questhead"       :width 30 :action Host)
|#
