#------------------------------------------------------------------------------------------
# utilisation d'un canvas pour la saisie de traces et de textes
# -- version utilisant les machines a etats --
#
# Michel Beaudouin-Lafon
# Nov. 1997
#
# mailto:mbl@lri.fr
# http://www-ihm.lri.fr/~mbl
#------------------------------------------------------------------------------------------
#
# On peut creer des dessins a main levee, et entrer du texte apres avoir pose le curseur
# texte par un click.
# Les marques et les textes peuvent etre deplaces par cliquer-tirer.
# On peut editer un texte en cliquant dedans pour y positionner le curseur texte.
# On peut detruite une ligne de texte en la rayant d'une marque horizontale.
#
# Cette version est fonctionnellement identique a trace.tcl, mais utilise des machines
# a etats. La seule difference est la prise en compte de l'hysterisis pour les deplacements
# dans cette version, ce qui n'est pas fait dans l'autre version.
# 
#------------------------------------------------------------------------------------------

# faire le menage
catch {eval destroy [winfo children .]}

# charger le package des machines a etats et de reconnaissance de trace
source sm.tcl
source mark-utils.tcl

# calcul de la distance (de Manhattan) entre deux points
#
proc Distance {P1 P2} {
	set d 0
	foreach c1 $P1 c2 $P2 {
		set d [expr $d + abs($c2 - $c1)]
	}
	return $d
}

# vecteur P1P2
#
proc Delta {P1 P2} {
	set d {}
	foreach c1 $P1 c2 $P2 {
		lappend d [expr $c2 - $c1]
	}
	return $d
}	

# machine a etats gerant le trace de marques et l'edition de texte
#
StateMachine::define Edit {
	local P1 P2 trace item
	
	state Start {
		when Down at P1 onItem item -> WaitMove
		when Down at P1 -> WaitTrace
		
		# gestion du focus
		when Enter do {
			focus %W
		}
		
		# mise en evidence de l'item sous le curseur
		when Enter onItem item withTag trace do {
			%W itemconfigure $item -width 3 -fill blue
		}
		when Leave onItem item withTag trace do {
			%W itemconfigure $item -width 1 -fill black
		}
		
		when Enter onItem item withTag texte do {
			%W itemconfigure $item -fill blue
		}
		when Leave onItem item withTag texte do {
			%W itemconfigure $item -fill black
		}
		
		# touches du clavier
		when Key do {
			# inserer le caractere dans l'item qui a le focus, a la position de son point d'insertion
			%W insert [%W focus] insert %A
		}
		when Key-Return do {
			# inserer le caractere \n dans l'item qui a le focus, a la position de son point d'insertion
			%W insert [%W focus] insert \n
		}

		# la touche backspace efface le caractere devant le curseur (s'il y en a un)
		#
		when Key-BackSpace do {
			# recuperer l'item qui a le focus
			set txt [%W focus]
			# recuperer la position du point d'insertion de l'item qui a le focus
			set idx [%W index $txt insert]
			# detruire le caractere devant le point d'insertion
			if {$idx > 0} {
				%W dchars $txt [expr $idx - 1]
			}
		}
	}
	
	# etats pour la creation d'un texte ou d'une trace
	#
	state WaitTrace {
		when Move at P2 and {[Distance $P1 $P2] > 5} do {
			# on a assez bouge : creation d'une trace
			eval %W create line $P1 $P2 -tag encre
			set trace [concat $P1 $P2]
			set P1 $P2
		} -> Trace
		when Up at P1 do {
			# creer un nouvel item texte
			set text [eval %W create text $P1 -anchor nw -tags texte]
			# si l'item qui a le focus est vide, le detruire
			# mettre le focus du canvas sur l'item que l'on vient de creer
			SetFocus %W $text
		} -> Start
	}
		
	state Trace {
		when Move at P2 do {
			# suivi du trace
			eval %W create line $P1 $P2 -tag encre
			eval lappend trace $P2
			set P1 $P2
		}
		when Up do {
			# creation de la trace finale
			%W delete encre
			set item [eval %W create line $trace -tag trace]
			# interpreter la trace
			InterpreteTrace %W $item
		} -> Start
	}
	
	# etats pour le deplacement d'items
	#
	state WaitMove {
		when Move at P2 and {[Distance $P1 $P2] > 5} do {
			# deplacement suffisant de la souris : deplacement de l'item
			eval %W move $item [Delta $P1 $P2]
			set P1 $P2
		} -> Move
		when Up at P1 onItem item withTag texte do {
			# mettre le focus sur l'item courant
			SetFocus %W $item
			# positionner le curseur d'insertion a la position de la souris
			%W icursor $item @%x,%y
		} -> Start
	}
	
	state Move {
		when Move at P2 do {
			# deplacement de l'item
			eval %W move $item [Delta $P1 $P2]
			set P1 $P2
		}
		when Up -> Start
	}
	
}

# positionner le focus sur un item texte.
# detruire l'item qui avait le focus auparavant s'il est vide.
# Cela evite de laisser trainer des items vides.
#
proc SetFocus {widget item} {
	set olditem [$widget focus]
	if {$olditem != "" && [$widget itemcget $olditem -text] == ""} {
		$widget delete $olditem
	}
	# poser le focus sur l'item
	$widget focus $item
}

# interpretation d'une trace.
# On regarde s'il s'agit d'un trait horizontal et s'il est sur un texte.
# Si c'est le cas on detruit le texte et le trait.
#
proc InterpreteTrace {widget trace} {
	# verifier que c'est bien un trait horizontal
	set line [$widget coords $trace]
	if {[MarkIsFlick $line] != "E"} {
		return
	}
	
	# recupere le widget texte qui est sous le trait
	set box [$widget bbox $trace]
	set text {}
	foreach item [eval $widget find overlapping $box] {
		if {[$widget type $item] == "text"} {
			set text $item
			break
		}
	}
	if {$text == {}} {
		return
	}
	
	# trouver la partie qui a ete rayee
	set x [lindex $line 0]
	set y [lindex $line 1]
	set first [$widget index $text @$x,$y]
	
	set l [llength $line]
	set x [lindex $line [expr $l -2]]
	set y [lindex $line [expr $l -1]]
	set last [$widget index $text @$x,$y]
	
	# detruire les caracteres ou l'item tout entier
	set end [$widget index $text end]
	if {$first == 0 && $last == $end} {
		$widget delete $text
	} else {
		$widget dchars $text $first $last
	}
	
	# detruire le trait
	$widget delete $trace
}

# creer un canvas, un bouton pour effacer et un bouton pour quitter
#
pack [canvas .c -borderwidth 2 -relief sunken] -expand on -fill both
pack [button .quit -text Quitter -command {destroy .}] -side right -padx 5 -pady 5
pack [button .clear -text Effacer -command {.c delete all}] -side right -padx 5 -pady 5

# activer la machine a etats pour le canvas
StateMachine::enable Edit .c
