;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;		      hotspots.scm   			;
;							;
;     Provides Hotspot-functions for HTML-Imagemaps     ;
;							;
;	  Simon Budig, <Simon.Budig@unix-ag.org>	;
;	    http://www.home.unix-ag.org/simon/		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; This Script-Fu provides two functions: 

; "script-fu-selection-to-hotspot" interprets the current selection as a
; Hotspot. It stores information about how to interpret the selection-bounds,
; and what HREF and ALT options are assigned to the Hotspot.
; The selection-bounds can be interpreted in two ways:
; 1. As a rectangle (straight-forward).
; 2. As a circle. Since HTML doesnt support elliptical hotspots
;    the minimum of (width|height)/2 will be interpreted as the radius.

; "script-fu-save-hotspots" saves the previously added hotspots
; as a HTML-fragment in a file. If no filename is given ("") the map
; will be written to stdout - useful for cut'n'paste.
; This file is ready to be included in the HTML-file, where the imagemap
; should be placed.
; Then add a <IMG SRC="./my/pic" USEMAP="#MY_MAP_NAME"> at the right
; place and the imagemap is complete.

; - It is a nice feature, that the hotspots will respect a rescaling/resizing
;   of the Image, since the coordinates of the hotspots are calculated,
;   when saving the hotspots.
; 
; - To check, which area is covered by a hotspot, open the Layers&Channels
;   Dialog and make the associated channel visible. The hotspot-area
;   will appear lighter. This is always correct for unscaled Images,
;   but is incorrect, if a circular hotspot has been rescaled. Then
;   the hotspot will appear elliptical. The inner circle will be used
;   for the hotspot in the imagemap.
;
; - To remove a hotspot simply remove the associated channel.
;
; - The informations about the hotspots will be preserved, when you save
;   the image as an XCF-File.
;
; - HTML supports polygonal bounded hotspots. This is IMHO hard to implement.
;   I dont want to program a trace-program to restore the selected polygon,
;   especially not in scheme. So this option has to wait for a plug-in.

; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or  
; (at your option) any later version.
; 
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
; 
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

; OK - Here we go:

(define (script-fu-selection-to-hotspot
			inImage inLayer inURL inALT inType inKeepSel)
  (gimp-undo-push-group-start inImage)
  (let* (
	   ; Store the current selection and calculate some coords
	   (orig-selection (car (gimp-selection-save inImage)))
	   (dummy (gimp-selection-sharpen inImage))
	   (selection (gimp-selection-bounds inImage))
	   (center-x (/ (+ (nth 1 selection) (nth 3 selection)) 2))
	   (center-y (/ (+ (nth 2 selection) (nth 4 selection)) 2))
	   ; In Imagemaps are only real circles allowed, so we have to
	   ; calculate the min.
	   (radius (/ (min (- (nth 3 selection) (nth 1 selection))
			   (- (nth 4 selection) (nth 2 selection)))
		      2))
	   ; Select the really used hotspot
	   (dummy (if (= inType TRUE)
			 (gimp-rect-select
			     inImage (nth 1 selection) (nth 2 selection)
			     (- (nth 3 selection) (nth 1 selection))
			     (- (nth 4 selection) (nth 2 selection))
			     2 0 0)
			 (gimp-ellipse-select
			     inImage (- center-x radius) (- center-y radius)
			     (* 2 radius) (* 2 radius)  2 0 0 0)
		     )
	   )
	   ; Save selected Hotspot
	   (hotspotchannel (car (gimp-selection-save inImage)))
        )
        ; Store Type, URL and ALT-Text in the Channel-Name:
        ; "Hotspot (Rectangle|Circle): <URL>{***}<ALT-Text>"
        ; (It is impossible to enter URLs with an {***} - but they are rare :-)
        (gimp-channel-set-name  hotspotchannel
				(string-append  "Hotspot ("
						(if (= inType TRUE)
						    "Rectangle"
						    "Circle") 
						"): " 
						inURL
						"{***}"
						inALT
				)
	)
	; If the user doesnt want to see the real selected Hotspot, restore
	; the original selection
	(if (= TRUE inKeepSel) (gimp-selection-load inImage orig-selection))
	(gimp-image-unset-active-channel inImage)
	(gimp-image-remove-channel inImage orig-selection)
  )
  (gimp-undo-push-group-end inImage)
  (gimp-displays-flush inImage)
)

; Register the function with the GIMP:

(script-fu-register
    "script-fu-selection-to-hotspot"
    "<Image>/Select/Save as Hotspot"
    "Mark selection as hotspot"
    "Simon Budig"
    "1998 Simon Budig, <Simon.Budig@unix-ag.org>"
    "23. May 1998"
    "*"
    SF-IMAGE "The Image" 0
    SF-DRAWABLE "The Layer" 0
    SF-VALUE "URL" "\"http://www.gimp.org/\""
    SF-VALUE "ALT-Text" "\"Gimp-Homepage\""
    SF-TOGGLE "Type: Rectangle (TRUE) / Circle (FALSE)" TRUE
    SF-TOGGLE "Keep Selection" FALSE
)

; the save-function...

(define (script-fu-save-hotspots inImage inLayer inMapName inFile)
  (gimp-undo-push-group-start inImage)
  (let* (
	   ; Save current selection, open file, initialize some Vars...
   	   (orig-selection (car (gimp-selection-save inImage)))
	   (outFile (if (> (string-length inFile) 0) (fopen inFile "w") ()))
	   (chanArray (cadr (gimp-image-get-channels inImage)))
	   (chanNr (car (gimp-image-get-channels inImage)))
	   (chanName "")
	   (chanArea "")
	   (chanURL "")
	   (chanALT "")
	   (chanCoords "")
	   (chanType 0)
	   (chanIndex 0)
	   (chanCurr 0)
	   (selection ())
        )
        (fwrite (string-append "\n<MAP NAME=\"" inMapName "\">\n") outFile)
        (fwrite (string-append "<!-- Map generated by Gimp from "
			       (car (gimp-image-get-filename inImage))
			       " -->\n"))
	; For each channel
	(while (< chanIndex chanNr)
	       ; gain information about the current channel
	       (set! chanType 0)
	       (set! chanCurr (aref chanArray chanIndex))
	       (set! chanName (car (gimp-channel-get-name chanCurr)))
	       ; Check if it is a Hotspot-channel (Well, most probably)
	       (if (and (= 0 (string-search "Hotspot (" chanName))
			(not (= () (string-search "{***}" chanName))))
		   (begin
		     ; select the hotspot-area and get the selection-bounds
		     (gimp-selection-load inImage chanCurr)
		     (set! selection (gimp-selection-bounds inImage))
		     ; if it is a rectangular hotspot...
		     (if (= 8 (string-search "(Rectangle): " chanName))
			 (begin (set! chanType 1)
				; extract URL from Channel-name
				(set! chanURL
				      (substring chanName 21
						 (string-search	"{***}"
								chanName))
				)
				; extract ALT-Text from Channel-name
				(set! chanALT 
				      (substring chanName
						 (+ 5 (string-search "{***}"
								     chanName))
						 (string-length chanName))
				)
				; extract Rect-Coords from selection-bounds
				(set! chanCoords
				      (string-append
				       (number->string (nth 1 selection))
				       ","
				       (number->string (nth 2 selection))
				       ","
				       (number->string (nth 3 selection))
				       ","
				       (number->string (nth 4 selection))
				       )
				      )
				)
			 ()
		     )
		     ; if it is a circular hotspot...
		     (if (= 8 (string-search "(Circle): " chanName))
			 (begin (set! chanType 2)
				; extract URL from Channel-name
				(set! chanURL
				      (substring chanName 18
						 (string-search "{***}"
								chanName)))
				(set! chanALT
				      (substring chanName
						 (+ 5 (string-search "{***}"
								     chanName))
						 (string-length chanName))
				)
				; extract Circle-Coords from selection-bounds,
				; get the minimal radius, because HTML can
				; only handle round cicles...
				(set! chanCoords
				      (string-append 
				       (number->string
					(/ (+ (nth 1 selection)
					      (nth 3 selection))
					   2))
				       ","
				       (number->string
					(/ (+ (nth 2 selection)
					      (nth 4 selection))
					   2))
				       ","
				       (number->string
					(/ (min (- (nth 3 selection)
						   (nth 1 selection))
						(- (nth 4 selection)
						   (nth 2 selection)))
					   2)
				       )
				      )
				)
			 )
			 ()
		     )
		     ; if it was a selection, write the AREA-Tag to the file
		     (if (not (= chanType 0))
			 (fwrite (string-append
				  "<AREA SHAPE=\""
				  (if (= 1 chanType)
				      "RECT"
				      "CIRCLE"
				      )
				  "\"\n     COORDS=\""
				  chanCoords
				  ; if there was an URL...
				  (if (> (string-length chanURL) 0)
				      "\"\n       HREF=\""
				      "")
				  chanURL
				  ; if there was an ALT-Text...
				  (if (> (string-length chanALT) 0) 
				      "\"\n        ALT=\"" 
				      "")
				  chanALT
				  "\">\n"
				  )
				 outFile
			 )
			 ()
			 )
		     )
		   ()
		   )
	       ; Next Channel...
	       (set! chanIndex (+ chanIndex 1))
	)
	; Finish the file and restore the previous selection
	(fwrite "</MAP>\n\n" outFile)
	(if (> (string-length inFile) 0) (fclose outFile) ())
	(gimp-selection-load inImage orig-selection)
	(gimp-image-unset-active-channel inImage)
	(gimp-image-remove-channel inImage orig-selection)
  )
  (gimp-undo-push-group-end inImage)       
  (gimp-displays-flush inImage)
)

; Register the function with the GIMP:

(script-fu-register
    "script-fu-save-hotspots"
    "<Image>/File/Save Hotspots"
    "Save marked hotspots to disk"
    "Simon Budig"
    "1998 Simon Budig, <Simon.Budig@unix-ag.org>"
    "23. May 1998"
    "*"
    SF-IMAGE "The Image" 0
    SF-DRAWABLE "The Layer" 0
    SF-VALUE "Map-name" "\"IMAGEMAP\""
    SF-VALUE "Filename, (\"\" = stdout)" "\"\""
)

