#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
# Copyright (c) 1998-2008 Miguel Filgueiras mig@ncc.up.pt Universidade do Porto
#
#    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.
#
#  File: navigate.tcl
#  Last change:  27 July 2006
#

##### travel/navigation display

proc ToTravel {} {
    # change the map window for travelling and set up the travel frame if
    #  needed
    # the frame $WConf(travel,fr) will replace frame $WConf(travel,alt)
    #  assumed to be the only widget in their parent and managed by grid
    global WConf Travelling Travel MESS

    if { $Travelling } { return }
    grid remove $WConf(travel,alt)
    set fr $WConf(travel,fr)
    if { ! [winfo exists $fr.fctrl] } {	TravelInit $fr }
    grid $fr
    if { $Travel(posns) != "" && [GMConfirm $MESS(clrtrvlog)] } {
	set Travel(posns) ""
    }
    set Travelling 1
    return
}

proc TravelInit {fr} {
    # set up the travel frame $fr
    global Travel RcMenu TXT COLOUR MAPCOLOUR MAPDISTS MAPDISTVALS FontSize

    foreach e $Travel(els) { set Travel(info,$e) "" }
    set Travel(info,vel_z) ""
    set Travel(nav,datum,a) [lindex [EllipsdData $Travel(nav,datum)] 0]

    # widget names are used in other procedures
    frame $fr.fctrl
    set mn $fr.fctrl.ctrl.mn
    menubutton $fr.fctrl.ctrl -text $TXT(change) -menu $mn
    menu $mn
    $mn add command -label $TXT(GPSrec) \
	    -command {
	$RcMenu post [expr [winfo rootx $MpW]+30] [expr [winfo rooty $MpW]+30]
    }
    $mn add separator
    $mn add command -label $TXT(navMOB) -foreground $COLOUR(check) \
	    -command { TravelNav MOB }
    $mn add command -label "$TXT(create) $TXT(WP)" \
	    -command { TravelMarkPoint }
    $mn add cascade -label $TXT(startnav) -menu $mn.ms
    menu $mn.ms
    $mn.ms add command -label $TXT(navWP) -command { TravelNav WP }
    foreach wh "RT TR LN" {
	$mn.ms add command -label [format $TXT(follow) $TXT($wh)] \
		-command "TravelNav $wh"
    }
    $mn.ms add command -label $TXT(goback) -command { TravelNav GoBack }

    $mn add command -label $TXT(chggoal) \
	    -command { TravelNavCmd chggoal } -state disabled
    set Travel(travel,mnchggoal) [$mn index last]
    $mn add command -label $TXT(suspend) \
	    -command { TravelNavCmd suspend } -state disabled
    set Travel(travel,mnsuspend) [$mn index last]
    $mn add command -label $TXT(resume) \
	    -command { TravelNavCmd resume } -state disabled
    set Travel(travel,mnresume) [$mn index last]
    $mn add command -label $TXT(forgetgoal) \
	    -command { TravelNavCmd abort } -state disabled
    set Travel(travel,mnabort) [$mn index last]

    $mn add separator
    $mn add cascade -label $TXT(mindist) -menu $mn.mdist
    set ix [$mn index last]
    menu $mn.mdist
    set pmd $Travel(mindist) ; set nf 1
    foreach e $MAPDISTS d $MAPDISTVALS {
	$mn.mdist add command -label $e \
		-command "set Travel(mindist) $d ; \
		          $mn entryconfigure $ix -label {$TXT(mindist) = $e}"
	if { $d == $pmd } {
	    set nf 0
	    $mn.mdist invoke last
	}
    }
    if { $nf } {
	# bad value of minimum distance; set to last in menu
	set Travel(mindist) $d
	$mn entryconfigure $ix -label "$TXT(mindist)  $e"
    }

    # prevent bad value for boolean off-road
    if { $Travel(offroad) != 0 } { set Travel(offroad) 1 }
    # NOT YET IN USE!
    # $mn add checkbutton -label $TXT(offroad) -variable Travel(offroad) \
	    -selectcolor $COLOUR(check)

    $mn add command -label $TXT(chginggoal) -command TravelChgGoal
    $mn add cascade -label $TXT(warnings) -menu $mn.mwn
    menu $mn.mwn
    set warn $Travel(warn)
    $mn.mwn add checkbutton -label $TXT(dowarn) -onvalue 1 -offvalue 0 \
	    -variable Travel(warn) -selectcolor $COLOUR(check)
    $mn.mwn add command -label $TXT(warnconf) -command TravelWarnConfigure

    $mn add cascade -label $TXT(travdisplay) -menu $mn.dsp
    menu $mn.dsp
    foreach dsp $Travel(displays) {
	$mn.dsp add command -label $dsp -command "TravelSetUp $dsp"
    }

    $mn add separator
    $mn add command -label $TXT(notravel) -command NoTravel

    foreach dsp $Travel(displays) {
	radiobutton $fr.fctrl.b$dsp -value $dsp -anchor w \
		-selectcolor $COLOUR(check) -variable Travel(travel,cdsp,b) \
		-command "TravelChangeDisplay $dsp $fr.fri"
	BalloonBindings $fr.fctrl.b$dsp \
		[list "=[format $TXT(travchgdisplay) $dsp]"]
	grid $fr.fctrl.b$dsp -row 1 -column [expr $dsp-1]
    }
    set Travel(travel,cdsp,b) $Travel(travel,cdsp)
    frame $fr.fri
    set Travel(travel,dsppfr) $fr.fri
    foreach dsp $Travel(displays) {
	frame $fr.fri.f$dsp
	foreach i $Travel(els) s $Travel(elsizes) {
	    label $fr.fri.f$dsp.$i -width $s -font $Travel(font) \
		    -textvariable Travel(info,$i) -relief sunken
	    label $fr.fri.f$dsp.t_$i -font $Travel(font) -text $TXT(TRV$i)
	}
	# TRN arrows
	# set cbgw 121 ; set cbgh 55
	set cbgw 121 ; set cbgh 37
	set cbg $fr.fri.f$dsp.c_trn
	canvas $cbg -width $cbgw -height $cbgh -relief flat -bg $COLOUR(dialbg)
	set cw2 [expr $cbgw/2] ; set ch2 [expr $cbgh/2]
	set aw [expr int($cw2*0.7)] ; set aw2 [expr $aw/2]
	set ah4 [expr ($cbgh-2)/4]
	set ps "1 $ch2  $aw2 1  $aw2 [expr 1+$ah4]  $aw [expr 1+$ah4] \
		$aw [expr $cbgh-1-$ah4]  $aw2 [expr $cbgh-1-$ah4] \
		$aw2 [expr $cbgh-1]  1 $ch2"
	eval $cbg create polygon $ps -fill $COLOUR(dialbg) -width 1 \
		-outline black -tags left
	set psr ""
	foreach "x y" $ps { lappend psr [expr $cbgw-$x] $y }
	eval $cbg create polygon $psr -fill $COLOUR(dialbg) -width 1 \
		-outline black -tags right
	label $cbg.lab -textvariable Travel(info,trn) \
	    -font "fixed $FontSize bold"
	$cbg create window $cw2 $ch2 -anchor c -window $cbg.lab

	# up-down arrow
	set cvz $fr.fri.f$dsp.c_vel_z
	canvas $cvz -width $cbgw -height $cbgh -relief flat -bg $COLOUR(dialbg)
	set b [expr $cbgh-2]
	set psn "[expr $ch2+2] 2 \
		[expr $cbgh+2]  [expr 2+$aw2] \
		[expr $cbgh+2-$ah4] [expr 2+$aw2] \
		[expr $cbgh+2-$ah4] $b  [expr 2+$ah4] $b \
		[expr 2+$ah4] [expr 2+$aw2]  2 [expr 2+$aw2] \
		[expr $ch2+2] 2"
	eval $cvz create polygon $psn -fill $COLOUR(dialbg) -width 1 \
		-outline black -tags vel_z
	label $cvz.lab -textvariable Travel(info,vel_z) \
	    -font "fixed [expr $FontSize+2] bold"
	$cvz create window [expr 0.75*$cbgw] $ch2 -anchor c -window $cvz.lab
	set Travel(wdgts,vel_z) [expr 2+$b]
	set Travel(wdgts,vel_z,up) 1

	# TRK, CTS and secondary CTS arrows
	set ccp $fr.fri.f$dsp.c_trkcts
	set ccpw [set ccph [expr 2*$cbgh]]
	canvas $ccp -width $ccpw -height $ccph -relief flat -bg $COLOUR(dialbg)
	if { $ccpw >= $ccph } { set sl $ccph } else { set sl $ccpw }
	set sl [expr int($sl*0.8)]
	set x0 [expr ($ccpw-$sl)/2] ; set xe [expr $x0+$sl]
	set y0 [expr ($ccph-$sl)/2] ; set ye [expr $y0+$sl]
	$ccp create oval $x0 $y0 $xe $ye -fill $COLOUR(dialbg) -width 1 \
		-outline $MAPCOLOUR(trvtrk)
	set xm [expr $ccpw/2]
	set ah [expr int(($sl-4)*0.4)] ; set ah2 [expr $ah/2]
	set ah4 [expr $ah2/2]
	set yh [expr $y0+3+$ah]
	set cps [list $xm [expr $y0+3]  [expr $xm+$ah2] $yh  \
		[expr $xm+$ah4] $yh  [expr $xm+$ah4] [expr $ye-3]  \
		[expr $xm-$ah4] [expr $ye-3] \
		[expr $xm-$ah4] $yh  [expr $xm-$ah2] $yh  $xm [expr $y0+3]]
	foreach ts [list {cts ctss} {cts2 ctss} trk] {
	    set it [eval $ccp create polygon $cps -width 1]
	    # to avoid problems with spaces in names of colours
	    $ccp itemconfig $it -fill $COLOUR(dialbg) -tags $ts
	}
	$ccp raise trk
	set ym [expr $ccph/2]
	set cs ""
	foreach "x y" $cps {
	    lappend cs [expr $x-$xm] [expr $y-$ym]
	}
	set Travel(wdgts,trk) [set Travel(wdgts,cts) \
		[set Travel(wdgts,cts2) [list trkcts $cs $xm $ym]]]
	# draw N,E,S,W letters
	set xx0 [expr $xm-2] ; set xxe [expr $xm+2] ; set yye [expr $y0-6]
	set csn [list $xx0 $y0  $xx0 $yye  $xxe $y0  $xxe $yye]
	eval $ccp create line $csn -width 1 -fill red -tags north
	set xx1 [expr $xe+1] ; set xx2 [expr $xe+6]
	set yy1 [expr $ym-3] ; set yy2 [expr $ym+3]
	set cse [list $xx2 $yy1  $xx1 $yy1  $xx1 $ym  [expr $xx1+4] $ym \
		$xx1 $ym  $xx1 $yy2  $xx2 $yy2]
	eval $ccp create line $cse -width 1 -fill blue -tags east
	set yy3 [expr $ye+3] ; set yy4 [expr $ye+6]
	set css [list $xxe $ye  $xx0 $ye  $xx0 $yy3  $xxe $yy3 \
		$xxe $yy4  $xx0 $yy4]
	eval $ccp create line $css -width 1 -fill blue -tags south
	set xx3 [expr $x0-6] ; set xx4 [expr $x0-1]
	set csw [list $xx3 $yy1  $xx3 $yy2  [expr $x0-3] [expr $yy2-3] \
		$xx4 $yy2  $xx4 $yy1]
	eval $ccp create line $csw -width 1 -fill blue -tags west
	set ts [list trkcts $xm $ym]
	foreach e "north east south west" l "csn cse css csw" {
	    set cs ""
	    foreach "x y" [set $l] {
		lappend cs [expr $x-$xm] [expr $y-$ym]
	    }
	    lappend ts $e $cs
	}
	set Travel(wdgts,nesw) $ts

	TravelDraw $fr $dsp
    }
    grid $fr.fctrl.ctrl -row 0 -column 0 -columnspan 2
    grid $fr.fctrl -row 0 -column 0
    grid $fr.fri.f$Travel(travel,cdsp)
    grid $fr.fri -row 0 -column 1
    return
}

proc TravelChangeDisplay {dsp pfr} {
    # change to display $dsp
    #  $pfr is parent frame of displays
    global Travel WConf

    grid remove $pfr.f$Travel(travel,cdsp)
    grid $pfr.f$dsp
    set Travel(travel,cdsp) $dsp
    return
}

proc TravelDraw {fr dsp} {
    # draw widgets for displaying travel information
    #  $fr is the main travel frame
    #  $dsp gives the secondary frame
    # the list $Travel(conf,$dsp) gives the elements and canvases to be
    #  displayed; element names can be preceded by lab+ asking for a title
    #  label
    global Travel

    set r 0 ; set c 0
    foreach e $Travel(conf,$dsp) {
	if { $e == "c_trkcts" } {
	    # this needs 2 rows
	    if { $r != 0 } { set r 0 ; incr c 2 }
	    grid $fr.fri.f$dsp.$e -row 0 -rowspan 2 -column $c -sticky nesw
	    set r 0 ; incr c
	} elseif { [string first "c_" $e] == 0 } {
	    # other arrow graphics
	    grid $fr.fri.f$dsp.$e -row $r -column $c -columnspan 2 -sticky ew
	    set c [expr $c+2*$r] ; set r [expr 1-$r]
	} elseif { [regsub {^lab\+} $e "" ne] } {
	    # with title label
	    grid $fr.fri.f$dsp.t_$ne -row $r -column $c -sticky e
	    grid $fr.fri.f$dsp.$ne -row $r -column [expr $c+1] -sticky w
	    set c [expr $c+2*$r] ; set r [expr 1-$r]
	} else {
	    grid $fr.fri.f$dsp.$e -row $r -column $c -columnspan 2
	    set c [expr $c+2*$r] ; set r [expr 1-$r]		
	}
    }
    return
}

proc TravelChgGoal {} {
    # dialog for setting the parameter on when to change from
    #  current to next goal when following a RT or TR
    global Travel TXT DPOSX DPOSY

    set w .trvchggoal
    if { [winfo exists $w] } { Raise $w ; return }
    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "$TXT(chginggoal)/GPS Manager"
    wm geometry $w +$DPOSX+$DPOSY

    frame $w.ft
    label $w.ft.tit -text $TXT(chginggoal)
    BalloonButton $w.ft.bhlp chggoalhlp
    grid $w.ft.tit
    grid $w.ft.bhlp -row 0 -column 1 -sticky sw

    frame $w.fr -relief flat -borderwidth 5
    label $w.fr.t1 -text $TXT(soon)
    scale $w.fr.s -orient horizontal -from 0 -to $Travel(chggmaxix) \
	    -showvalue 0 -width 12 -length 100 -variable Travel(chggix)
    label $w.fr.t2 -text $TXT(late)
    grid $w.fr.t1 -row 0 -column 0
    grid $w.fr.s -row 0 -column 1
    grid $w.fr.t2 -row 0 -column 2

    button $w.ok -text Ok -command "destroy $w"

    pack $w.ft $w.fr $w.ok -pady 5
    update idletasks
    return
}

proc TravelChgGParam {n no op} {
    # called by trace when $Travel(chggix) has been changed
    global Travel

    set ix $Travel(chggix)
    set Travel(chggparam) [lindex $Travel(chggvals) $ix]
    set Travel(chggwparam) [lindex $Travel(chggwvals) $ix]
    return
}

proc NoTravel {} {
    # change the map window when stopping travelling
    # the frame $WConf(travel,alt) will replace frame $WConf(travel,fr)
    #  assumed to be the only widget in their parent and managed by grid
    global WConf Travelling

    set Travelling 0
    grid remove $WConf(travel,fr)
    grid $WConf(travel,alt)
    return
}

proc TravelRealTimeOff {} {
    # real-time logging stopped
    global Travel TXT

    TravelDoWarn $TXT(trvwnolog) important
    TravelDisplayChange notraveldata
    if { $Travel(nav) == "on" } {
	TravelNavCmd suspend
	set Travel(nav) nolog
	set Travel(nav,ok) 0
    }
    return
}

proc TravelRealTimeOn {} {
    # real-time logging (re-)started
    global Travel

    if { $Travel(nav) == "nolog" } {
	TravelNavCmd resume
    }
    return
}

##### configuring the travel displays

proc TravelSetUp {dsp} {
    # set up/change the contents of the travel display $dsp
    global Travel WConf TXT MESS DPOSX DPOSY COLOUR MAPFTSIZES

    # used explicitly elsewhere
    set w .travsetup
    if { [winfo exists $w] } { bell ; Raise $w ; return }

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "$TXT(travdsetup)/GPS Manager"
    wm geometry $w +$DPOSX+$DPOSY
    bind $w <Key-Return> "TravelSUAction ok $dsp"

    label $w.tit -text "$TXT(travdsetup) $dsp"
    frame $w.fr -relief flat -borderwidth 5

    # values and titles
    frame $w.fr.frl
    foreach l "hide show" col "0 3" {
	label $w.fr.frl.$l -text $TXT($l)
	BalloonBindings $w.fr.frl.$l trvhlpbox
	grid $w.fr.frl.$l -row 0 -column $col -sticky nesw
    }
    foreach b "a b" col "0 3" e "nuels cfels" {
	listbox $w.fr.frl.bx$b -height 12 -width 15 -relief groove \
		-yscrollcommand "$w.fr.frl.bscr$b set" -selectmode extended
	scrollbar $w.fr.frl.bscr$b -command "$w.fr.frl.bx$b yview"
	set Travel(move,bx$b) ""
	grid $w.fr.frl.bx$b -row 1 -column $col -sticky nesw
	grid $w.fr.frl.bscr$b -row 1 -column [expr $col+1] -sticky nesw
	foreach ev "<Button-3> <ButtonRelease-3>" p "start end" {
	    bind $w.fr.frl.bx$b $ev "TravelSUMove $p bx$b $e %x %y"
	}
	bind $w.fr.frl.bx$b <B3-Enter> \
		"TravelSUMove enter-down bx$b $e %X %Y; break"
	foreach ev "<B3-Motion> <Any-Enter> <B3-Leave>" p "go enter leave" {
	    bind $w.fr.frl.bx$b $ev "TravelSUMove $p bx$b $e %X %Y"
	}
    }
    set Travel(dsup,cfels) ""
    set Travel(dsup,nuels) [concat $Travel(cvss) $Travel(els)]
    foreach e $Travel(conf,$dsp) {
	lappend Travel(dsup,cfels) $e
	if { [regsub {^lab\+} $e "" e] } {
	    set t "$TXT(TRV$e), $TXT(label)"
	} else { set t $TXT(TRV$e) }
	$w.fr.frl.bxb insert end $t
	if { [set ix [lsearch -exact $Travel(dsup,nuels) $e]] != -1 } {
	    set Travel(dsup,nuels) [lreplace $Travel(dsup,nuels) $ix $ix]
	} else {
	    destroy $w
	    set Travel(conf,$dsp) ""
	    GMMessage $MESS(badtrvconf)
	    return [TravelSetUp $dsp]
	}
    }
    foreach e $Travel(dsup,nuels) {
	$w.fr.frl.bxa insert end $TXT(TRV$e)
    }
    frame $w.fr.frl.frb
    foreach b "a at r" ti "add addlabelled remove" al "- - <" ar "> > -" {
	button $w.fr.frl.frb.b$b -text "${al}--$TXT($ti)--$ar" \
		-command "TravelSUAction $ti $dsp"
	grid $w.fr.frl.frb.b$b
    }
    grid [BalloonButton $w.fr.frl.frb.bhlp trvhlpbxs]
    grid $w.fr.frl.frb -row 1 -column 2 -sticky n

    # options: font size
    frame $w.fr.fro -borderwidth 1 -bg $COLOUR(messbg)
    menubutton $w.fr.fro.fts -text $TXT(fntsize) -menu $w.fr.fro.fts.m
    menu $w.fr.fro.fts.m
    foreach s $MAPFTSIZES {
	$w.fr.fro.fts.m add command -label $s \
		-command "TravelSUAction font $dsp $s"
    }
    grid $w.fr.fro.fts

    # on, cancel buttons
    frame $w.fr.frbs
    button $w.fr.frbs.ok -text Ok \
	    -command "destroy $w ; TravelSUAction ok $dsp"
    button $w.fr.frbs.cnc -text $TXT(cancel) -command "destroy $w"
    grid $w.fr.frbs.ok
    grid $w.fr.frbs.cnc -row 0 -column 1

    grid $w.fr.frl
    grid $w.fr.fro -pady 5
    grid $w.fr.frbs -pady 7
    grid $w.tit
    grid $w.fr

    # change to display $dsp
    $WConf(travel,fr).fctrl.b$dsp invoke
    $WConf(travel,fr).fctrl.b$dsp select
    update idletasks
    return
}

proc TravelSUMove {how box lref x y} {
    # move element in listbox $box under travel display set up dialog
    #  $how in {start, go, end, enter-down, enter, leave}
    #  $lref is used to access the list $Travel(dsup,$lref) containing the
    #   internal representations of the listbox elements
    #  $x,$y are either the coordinates inside the listbox (for $how==start
    #   and $how==end) or the global coordinates
    global Travel COLOUR

    set pb .travsetup.fr.frl.$box
    set tb .travmv$box
    switch $how {
	start {
	    set ix [$pb index @$x,$y]
	    if { [set el [$pb get $ix]] == "" } { return }
	    if { $Travel(move,$box) != "" } {
		destroy $tb
	    } else { set Travel(move,$box) in }
	    toplevel $tb
	    wm resizable $tb 0 0
	    wm overrideredirect $tb 1
	    wm geometry $tb \
		   +[expr $x+[winfo rootx $pb]+9]+[expr $y+[winfo rooty $pb]+9]
	    label $tb.mess -text $el -relief groove -bg $COLOUR(ballbg) \
		    -fg $COLOUR(ballfg)
	    pack $tb.mess
	    set Travel(move,$box,el) $el
	    set Travel(move,$box,ix) $ix
	}
	enter-down {
	    if { $Travel(move,$box) == "out" } {
		if { [winfo exists $tb] } {
		    set Travel(move,$box) in
		    wm geometry $tb +[expr $x+9]+[expr $y+9]
		} else {
		    set Travel(move,$box) ""
		}
	    }
	}
	enter {
	    if { $Travel(move,$box) != "" } {
		destroy $tb ; set Travel(move,$box) ""
	    }
	}
	go {
	    if { $Travel(move,$box) == "in" && [winfo exists $tb] } {
		wm geometry $tb +[expr $x+9]+[expr $y+9]
	    }
	}
	end {
	    if { $Travel(move,$box) == "in" && [winfo exists $tb] } {
		destroy $tb ; set Travel(move,$box) ""
		set fix [$pb index @$x,$y]
		set ix $Travel(move,$box,ix)
		set e [lindex $Travel(dsup,$lref) $ix]
		if { $fix != $ix } {
		    $pb delete $ix
		    $pb insert $fix $Travel(move,$box,el)
		    set Travel(dsup,$lref) \
			    [lreplace $Travel(dsup,$lref) $ix $ix]
		    set Travel(dsup,$lref) \
			    [linsert $Travel(dsup,$lref) $fix $e]
		}
	    }
	}
	leave {
	    if { $Travel(move,$box) == "in" } { set Travel(move,$box) "out" }
	}
    }
    return
}

proc TravelSUAction {act dsp args} {
    # deal with user action when setting up the travel display number $dsp
    #  $act in {ok, add, addlabelled, remove, font}
    #  $args is font size (in $MAPFTSIZES) if $act==font
    global Travel WConf TXT

    set w .travsetup
    set bxfrom $w.fr.frl.bxa ; set bxto $w.fr.frl.bxb
    set fr $WConf(travel,fr)
    switch $act {
	add {
	    if { [set s [$bxfrom curselection]] == "" } {
		if { [$bxfrom size] != 1 } {
		    bell ; return
		} else { set s 0 }
	    }
	    set s [lsort -integer -decreasing $s]
	    set iix [$bxto size]
	    foreach ix $s {
		set ne [lindex $Travel(dsup,nuels) $ix]
		set Travel(dsup,nuels) [lreplace $Travel(dsup,nuels) $ix $ix]
		$bxfrom delete $ix $ix
		$bxto insert $iix $TXT(TRV$ne)
		set Travel(dsup,cfels) [linsert $Travel(dsup,cfels) $iix $ne]
	    }
	}
	addlabelled {
	    if { [set s [$bxfrom curselection]] == "" } {
		if { [$bxfrom size] != 1 } {
		    bell ; return
		} else { set s 0 }
	    }
	    if { [llength $s] == 1 } {
		set e [lindex $Travel(dsup,nuels) $s]
		if { [string first "c_" $e] == 0 } { bell ; return }
	    } else { set s [lsort -integer -decreasing $s] }
	    set iix [$bxto size]
	    foreach ix $s {
		set ne [lindex $Travel(dsup,nuels) $ix]
		set Travel(dsup,nuels) [lreplace $Travel(dsup,nuels) $ix $ix]
		$bxfrom delete $ix $ix
		if { [string first "c_" $ne] == 0 } {
		    set t $TXT(TRV$ne)
		} else {
		    set t "$TXT(TRV$ne), $TXT(label)"
		    set ne lab+$ne
		}
		$bxto insert $iix $t
		set Travel(dsup,cfels) [linsert $Travel(dsup,cfels) $iix $ne]
	    }
	}
	remove {
	    if { [set s [$bxto curselection]] == "" } {
		if { [$bxto size] != 1 } {
		    bell ; return
		} else { set s 0 }
	    }
	    set s [lsort -integer -decreasing $s]
	    set iix [$bxfrom size]
	    foreach ix $s {
		set ne [lindex $Travel(dsup,cfels) $ix]
		regsub {^lab\+} $ne "" ne
		set Travel(dsup,cfels) [lreplace $Travel(dsup,cfels) $ix $ix]
		$bxto delete $ix $ix
		$bxfrom insert $iix $TXT(TRV$ne)
		set Travel(dsup,nuels) [linsert $Travel(dsup,nuels) $iix $ne]
	    }
	}
	font {
	    # if $args is an integer it is the size in points, otherwise
	    #  the default of Tcl/Tk for the fixed font is used
	    set Travel(font) [GFixedFont $args]
	    foreach e $Travel(els) {
		$fr.fri.f$dsp.$e configure -font $Travel(font)
		$fr.fri.f$dsp.t_$e configure -font $Travel(font)
	    }
	}
	ok {
	    if { $Travel(dsup,cfels) != $Travel(conf,$dsp) } {
		set Travel(conf,$dsp) $Travel(dsup,cfels)
		foreach s [grid slaves $fr.fri.f$dsp] { grid forget $s }
		TravelDraw $fr $dsp
	    }
	}
    }
    return
}

##### warnings

proc TravelWarnConfigure {} {
    # configure warnings
    # warnings considered here as belonging to $Travel(warnings):
    #    prox, anchor, speed, trn, vspeed, xtk
    # all values in $Travel(warn,...) saved to Travel(wcfg,...)
    global Travel COLOUR DPOSX DPOSY TXT NAMEWIDTH DTUNIT SPUNIT

    set w .travwrnconf
    if { [winfo exists $w] } { bell ; Raise $w ; return }

    foreach p [array names Travel warn,*] {
	regsub {^warn,} $p "" s
	set Travel(wcfg,$s) $Travel($p)
    }

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "$TXT(warnconf)/GPS Manager"
    wm geometry $w +$DPOSX+$DPOSY
    bind $w <Key-Return> { TravelWCAction ok }

    label $w.tit -text $TXT(warnconf)
    frame $w.fr -relief flat -borderwidth 5
    frame $w.fr.fc -relief flat -borderwidth 1 -bg $COLOUR(messbg)
    set fr $w.fr.fc
    label $fr.prior -text $TXT(priority)
    foreach x "prox anchor speed trn vspeed xtk" {
	checkbutton $fr.chk$x -text $TXT(warn$x) -variable Travel(wcfg,$x) \
		-anchor w -selectcolor $COLOUR(check)
	frame $fr.frl$x
	set c -1
	foreach l "high medium low" {
	    radiobutton $fr.frl$x.l$l -text $TXT($l) -value $l -anchor w \
		    -variable Travel(wcfg,$x,level) -selectcolor $COLOUR(check)
	    grid $fr.frl$x.l$l -row 0 -column [incr c] -sticky w
	}
    }
    set nl $NAMEWIDTH
    foreach x "prox anchor" {
	button $fr.ch$x -text $TXT(select) -command "TravelWCAction selwp $x"
	label $fr.wp$x -textvariable Travel(wcfg,$x,wpn) -width $nl
	entry $fr.dt$x -textvariable Travel(wcfg,$x,dst) -width 4 \
		-justify right
	label $fr.du$x -text $DTUNIT
    }
    foreach x "speed trn xtk" \
	    u [list $SPUNIT $TXT(degrees) $DTUNIT] {
	entry $fr.max$x -textvariable Travel(wcfg,$x,max) -width 4 \
		-justify right
	label $fr.un$x -text $u
    }
    frame $fr.fs
    entry $fr.fs.minvspeed -textvariable Travel(wcfg,vspeed,min) -width 4 \
		-justify right
    label $fr.fs.sepvspeed -text "---"
    entry $fr.fs.maxvspeed -textvariable Travel(wcfg,vspeed,max) -width 4 \
		-justify right
    label $fr.fs.unvspeed -text m/s

    grid $fr.prior -row 0 -column 5 -sticky ew
    set r 0
    foreach x "prox anchor speed vspeed trn xtk" {
	grid $fr.chk$x -row [incr r] -column 0 -sticky ew
	grid $fr.frl$x -row $r -column 5 -sticky ew
	set row_$x $r
    }
    foreach x "prox anchor" {
	set r [set row_$x]
	grid $fr.dt$x -row $r -column 1 -sticky ew
	grid $fr.du$x -row $r -column 2 -sticky w
	grid $fr.ch$x -row $r -column 3 -padx 5
	grid $fr.wp$x -row $r -column 4
    }
    foreach x "speed trn xtk" {
	set r [set row_$x]
	grid $fr.max$x -row $r -column 1 -sticky ew
	grid $fr.un$x -row $r -column 2 -sticky w
    }
    grid $fr.fs.minvspeed -row 0 -column 0 -sticky ew
    grid $fr.fs.sepvspeed -row 0 -column 1 -sticky ew
    grid $fr.fs.maxvspeed -row 0 -column 2 -sticky w
    grid $fr.fs.unvspeed -row 0 -column 3 -sticky w
    grid $fr.fs -row $row_vspeed -column 1 -columnspan 3 -sticky w

    frame $w.fr.frbs
    button $w.fr.frbs.ok -text Ok \
	    -command "$w.fr.frbs.ok configure -state normal ; \
	              TravelWCAction ok $w"
    button $w.fr.frbs.cnc -text $TXT(cancel) -command "destroy $w"
    grid $w.fr.frbs.ok
    grid $w.fr.frbs.cnc -row 0 -column 1

    grid $w.fr.fc
    grid $w.fr.frbs -pady 7
    grid $w.tit ; grid $w.fr
    return
}

proc TravelWCAction {act args} {
    # react to action when configuring warnings
    #  $args:
    #    if $act==ok (end of configuration): window to destroy on success
    #    if $act=selwp: name of warning in {prox, anchor}
    # warnings considered here as belonging to $Travel(warnings):
    #    prox, anchor, speed, trn, vspeed, xtk
    global Travel WPName WPPosn WPDatum TXT MESS

    switch $act {
	selwp {
	    if { [set ix [ChooseItems WP single]] == "" } { return }
	    set Travel(wcfg,$args,wpn) $WPName($ix)
	    set Travel(wcfg-int,$args,wpix) $ix
	}
	ok {
	    # check values for all active warnings
	    set ws ""
	    foreach e $Travel(warnings) {
		if { $Travel(wcfg,$e) } {
		    lappend ws $e
		    switch $e {
			prox -  anchor {
			    if { $Travel(wcfg,$e,wpn) == "" } {
				GMMessage $MESS(badWP)
				return
			    }
			    if { [BadParam $TXT(warn$e) float>0 \
				    $Travel(wcfg,$e,dst)] } { return }
			}
			trn {
			    if { [BadParam $TXT(warn$e) long=0,180 \
				    $Travel(wcfg,$e,max)] } { return }
			    
			}
			vspeed {
			    if { [BadParam $TXT(warn$e) float \
				    $Travel(wcfg,$e,min)] || \
				    [BadParam $TXT(warn$e) \
				       float>$Travel(wcfg,$e,min) \
				       $Travel(wcfg,$e,max)] } { return }
			}
			speed -  xtk {
			    if { [BadParam $TXT(warn$e) float>0 \
				    $Travel(wcfg,$e,max)] } { return }
			}
		    }
		}
	    }
	    foreach e $ws {
		foreach p [array names Travel wcfg,$e*] {
		    regsub {^wcfg,} $p "" s
		    set Travel(warn,$s) $Travel($p)
		}
		if { $e == "prox" || $e == "anchor" } {
		    set ix $Travel(wcfg-int,$e,wpix)
		    set p $WPPosn($ix)
		    if { $WPDatum($ix) != $Travel(nav,datum) } {
			set p [ToDatum [lindex $p 0] [lindex $p 1] \
				$WPDatum($ix) $Travel(nav,datum)]
		    }
		    set Travel(warn,$e,pos) $p
		}
	    }
	    destroy $args
	}
    }
    return
}

proc TravelWarn {mess level} {
    # issue warning message if $Travel(warn)
    global Travel

    if { $Travel(warn) } { TravelDoWarn $mess $level }
    return
}

proc TravelDoWarn {mess level} {
    # display warning message on left-upper corner of map
    #  $level in {important, high, medium, normal, low, info} is the priority
    #    level used in cancelling existing warnings and in selecting the
    #    colour
    global Travel Map MAPCOLOUR

    set n [lsearch -exact {important high medium normal low info} $level]
    if { [winfo exists .travelwarn] } {
	if { $n > $Travel(warnprior) } { return }
	after cancel $Travel(warncancel)
	destroy .travelwarn
    }
    set Travel(warnprior) $n
    toplevel .travelwarn
    wm resizable .travelwarn 0 0
    wm overrideredirect .travelwarn 1
    wm geometry .travelwarn +[winfo rootx $Map]+[winfo rooty $Map]
    message .travelwarn.m -aspect 800 -text $mess \
	    -font $Travel(font) -fg $MAPCOLOUR(trvwrn$level)
    pack .travelwarn.m
    set Travel(warncancel) [after 4000 { destroy .travelwarn }]
    return
}

##### navigation

proc TravelNav {where} {
    # start navigation
    #  $where in {MOB, WP, RT, TR, LN, GoBack}
    global Travel WConf RealTimeLogOn TXT

    if { ! $RealTimeLogOn } {
	TravelDoWarn $TXT(trvwnolog) important
	return
    }
    if { $Travel(nav) != "" } {
	TravelDisplayChange clear
	set Travel(nav) ""
    }
    TravelDisplayChange restore
    if { $where == "MOB" } {
	if { $Travel(prevtime) == -1 } {
	    TravelDoWarn $TXT(trvwnopos) important
	    return
	}
	TravelNavToMOB
    } else {
	if { $where == "WP" } {
	    set ix [ChooseItems WP single]
	} elseif { $where == "GoBack" } {
	    if { $Travel(posns) == "" } {
		TravelDoWarn $TXT(trvwnopos) important
		set ix ""
	    } else { set ix ok }
	} else {
	    # RT, TR, LN
	    set ix [ChooseItems $where single \
		    "Travel(nav,pmode) Travel(nav,pnear) Travel(nav,prvrs)" \
		    [list "@$TXT(exactly)" "@$TXT(fromnrst)" "@$TXT(inrvrs)"]]
	}
	if { $ix == "" || ! [TravelNavTo$where $ix] } {
	    TravelDisplayChange clear
	    return
	}
    }
    set mn $WConf(travel,fr).fctrl.ctrl.mn
    foreach z "abort suspend" {
	$mn entryconfigure $Travel(travel,mn$z) -state normal
    }
    set Travel(nav) on ; set Travel(nav,dtype) $where
    set Travel(nav,ok) 1 ; set Travel(nav,towarn) $Travel(warn)
    return
}

proc TravelNavToWP {ix} {
    # start navigation to WP with given index
    # return 1 (cannot fail)
    global Travel WPPosn WPDatum WPName

    set p $WPPosn($ix)
    if { $WPDatum($ix) != $Travel(nav,datum) } {
	set p [ToDatum [lindex $p 0] [lindex $p 1] \
		   $WPDatum($ix) $Travel(nav,datum)]
    }
    set Travel(nav,nxtgoal) $p
    set Travel(nav,afternxt) ""
    set Travel(nav,save) nxtWP
    set Travel(nav,mode) exact
    set Travel(nav,state) starting
    set Travel(info,nxtWP) $WPName($ix)
    return 1
}

proc TravelNavToMOB {} {
    # start navigation to previous position (MOB)
    global Travel RealTimeLogAnim

    # create MOB WP
    set p [FormatLatLong [lindex $Travel(prevposn) 0] \
	       [lindex $Travel(prevposn) 1] DDD]
    set n [NewName WP MOB]
    set d [FormData WP "Name Commt Posn PFrmt Datum Symbol Obs" \
	    [list $n MOB $p DDD $Travel(nav,datum) MOB "MOB\n[NowTZ]"]]
    # display on map if animating
    StoreWP -1 $n $d $RealTimeLogAnim
    # go there
    TravelNavToWP [IndexNamed WP $n]
    return
}

proc TravelMarkPoint {} {
    # create waypoint at current log position
    global Travel RealTimeLogAnim TXT CREATIONDATE

    if { [set secs $Travel(prevtime)] == -1 } {
	TravelDoWarn $TXT(trvwnopos) info
	return
    }
    set p [FormatLatLong [lindex $Travel(prevposn) 0] \
	       [lindex $Travel(prevposn) 1] DDD]
    set opts [list create revert cancel]
    set dt [DateFromSecs $secs]
    if { $CREATIONDATE } {
	GMWPoint -1 $opts \
	    [FormData WP "PFrmt Posn Datum Date Displ" \
		 [list DDD $p $Travel(nav,datum) $dt $RealTimeLogAnim]]
    } else {
	GMWPoint -1 $opts \
	    [FormData WP "Commt PFrmt Posn Datum Displ" \
		 [list [DateCommt $dt] DDD $p $Travel(nav,datum) \
		     $RealTimeLogAnim]]
    }
    return
}

proc TravelNavToRT {ix} {
    # start navigation to RT with given index
    # return 0 on failure
    global Travel RTWPoints WPPosn WPDatum TXT

    set ps ""
    foreach nwp [set wpns $RTWPoints($ix)] {
	if { [set wpix [IndexNamed WP $nwp]] == -1 } {
	    TravelDoWarn $TXT(trvwuwps) normal
	    return 0
	}
	set p $WPPosn($wpix)
	if { $WPDatum($wpix) != $Travel(nav,datum) } {
	    set p [ToDatum [lindex $p 0] [lindex $p 1] \
		       $WPDatum($wpix) $Travel(nav,datum)]
	}
	lappend ps $p
	lappend wpixs $wpix
    }
    if { $Travel(nav,pmode) } {
	set Travel(nav,mode) exact
    } else {
	set Travel(nav,mode) approx
	set Travel(nav,nxtleg) ""
    }
    foreach "ps wpns" [TravelNavParams $ps $wpns] {}
    set Travel(nav,wpns) $wpns
    TravelNavLine $ps [lindex $wpns 0]
    return 1
}

proc TravelNavToTR {ix} {
    # start navigation to TR with given index
    # return 0 on failure
    global Travel TRTPoints TRDatum TXT

    if { $Travel(nav,pmode) } {
	set Travel(nav,mode) exact
    } else {
	set Travel(nav,mode) approx
	set Travel(nav,nxtleg) ""
    }
    set ps $TRTPoints($ix) ; set trdatum $TRDatum($ix)
    set datum $Travel(nav,datum)
    set Travel(nav,conv) ""
    if { $Travel(nav,mode) == "approx" && [llength $ps] > 150 } {
	# convert to list of at most 150 points and follow it
	set ps [TRCvTR $ps 150 $trdatum travel]
	set Travel(nav,conv) "*"
    }
    if { $TRDatum($ix) != $datum } {
	set ps [ChangeTPsDatum $ps $trdatum $datum]
    }
    set ps [lindex [TravelNavParams $ps ""] 0]
    TravelNavLine $ps "$TXT(TP) $Travel(nav,conv)0"
    return 1
}

proc TravelNavToLN {ix} {
    # start navigation to LN with given index
    # return 0 on failure
    global Travel LNLPoints LNDatum TXT

    if { $Travel(nav,pmode) } {
	set Travel(nav,mode) exact
    } else {
	set Travel(nav,mode) approx
	set Travel(nav,nxtleg) ""
    }
    set ps ""
    foreach lp $LNLPoints($ix) {
	lappend ps [lindex $lp 0]
    }
    set lndatum $LNDatum($ix) ; set datum $Travel(nav,datum)
    set Travel(nav,conv) ""
    if { $Travel(nav,mode) == "approx" && [llength $ps] > 150 } {
	# convert to list of at most 150 points and follow it
	set ps [TRCvTR $ps 150 $lndatum travel]
	set Travel(nav,conv) "*"
    }
    if { $lndatum != $datum } {
	set ps [ChangeLPsDatum $ps $lndatum $datum DDD]
    }
    set ps [lindex [TravelNavParams $ps ""] 0]
    TravelNavLine $ps "$TXT(LP) $Travel(nav,conv)0"
    return 1
}

proc TravelNavToGoBack {args} {
    # go back using current list of positions
    # follow it exactly, from last position to first ($Travel(posns) is
    #  in that order)
    global Travel TXT

    set Travel(nav,mode) exact
    set Travel(nav,conv) "*"
    TravelNavLine $Travel(posns) "$TXT(TP) $Travel(nav,conv)0"
    return 1
}

proc TravelNavLine {ps np} {
    # initialize variables to follow a line (RT, TR, LN or log)
    #  $ps is list of positions
    #  $np is name of next point
    global Travel WConf

    set Travel(nav,ps) $ps
    set Travel(nav,nxtgoal) [lindex $ps 0]
    set Travel(nav,afternxt) [lindex $ps 1]
    set Travel(nav,save) "prvWP nxtWP"
    set Travel(nav,state) starting
    set Travel(nav,ix) 0
    if { [set Travel(nav,maxix) [expr [llength $ps]-1]] > 0 } {
	set mn $WConf(travel,fr).fctrl.ctrl.mn
	$mn entryconfigure $Travel(travel,mnchggoal) -state normal
    }
    set Travel(info,prvWP) ""
    set Travel(info,nxtWP) $np
    # used for TR, LN and GoBack
    set Travel(ptname) [lindex $np 0]
    return
}

proc TravelNavParams {ps ns} {
    # using list of positions and corresponding names (may be void)
    #  return pair with new lists taking into account the selected
    #  parameters:
    #  $Travel(nav,pnear) if to start from nearest point
    #  $Travel(nav,prvrs) if to follow in reverse order
    global Travel TXT

    set rev $Travel(nav,prvrs)
    if { $Travel(nav,pnear) } {
	if { $Travel(prevtime) != -1 } {
	    set datum $Travel(nav,datum)
	    set currpos $Travel(prevposn)
	    set dmin 1e77 ; set ix 0
	    foreach p $ps {
		if { [set nd [ComputeDist $currpos $p $datum]] < $dmin } {
		    set ixm $ix ; set dmin $nd
		}
		incr ix
	    }
	    if { $rev } {
		set rps [lrange $ps 0 $ixm] ; set rns [lrange $ns 0 $ixm]
		set ps "" ; set ns ""
		foreach p $rps n $rns {
		    set ps [linsert $ps 0 $p] ; set ns [linsert $ns 0 $n]
		}
	    } else {
		set ps [lrange $ps $ixm end] ; set ns [lrange $ns $ixm end]
	    }
	    set Travel(nav,conv) "*"
	    return [list $ps $ns]
	}
	TravelDoWarn $TXT(trvwnopos) important
    }
    if { $rev } {
	set rps $ps ; set rns $ns
	set ps "" ; set ns ""
	foreach p $rps n $rns {
	    set ps [linsert $ps 0 $p] ; set ns [linsert $ns 0 $n]
	}
	set Travel(nav,conv) "*"
    }
    return [list $ps $ns]
}

proc TravelNavCmd {cmd} {
    # process user/internal command when navigating
    #  $cmd in {chggoal, abort, suspend, resume}
    global Travel WConf

    set mn $WConf(travel,fr).fctrl.ctrl.mn
    switch $cmd {
	abort {
	    set Travel(nav) ""
	    foreach z "chggoal abort suspend resume" {
		$mn entryconfigure $Travel(travel,mn$z) -state disabled
	    }
	    TravelDisplayChange clear
	}
	suspend {
	    set Travel(nav) susp ; set vs ""
	    foreach e $Travel(nav,save) { lappend vs $Travel(info,$e) }
	    set Travel(nav,saved) $vs
	    set Travel(nav,chggstate) \
		    [$mn entrycget $Travel(travel,mnchggoal) -state]
	    $mn entryconfigure $Travel(travel,mnchggoal) -state disabled
	    $mn entryconfigure $Travel(travel,mnsuspend) -state disabled
	    $mn entryconfigure $Travel(travel,mnresume) -state normal
	    TravelDisplayChange suspend
	}
	resume {
	    $mn entryconfigure $Travel(travel,mnchggoal) \
		    -state $Travel(nav,chggstate)
	    $mn entryconfigure $Travel(travel,mnsuspend) -state normal
	    $mn entryconfigure $Travel(travel,mnresume) -state disabled
	    TravelDisplayChange restore
	    foreach e $Travel(nav,save) v $Travel(nav,saved) {
		set Travel(info,$e) $v
	    }
	    set Travel(nav) on	    
	}
	chggoal {
	    if { [TravelChangeGoal] || $Travel(nav,afternxt) == "" } {
		$mn entryconfigure $Travel(travel,mnchggoal) -state disabled
	    }
	}
    }
    return
}

##### displaying information

proc TravelDisplayChange {act} {
    # change information on current travel/navigation in displays
    #  $act in {notraveldata, clear, suspend, restore}
    # assume $Travel(_,els) is list of relevant $e used in accessing
    #  Travel(info,$e), and $Travel(_,cvels) is list of pairs with
    #  canvas and tag $t of relevant item in canvas (coloured
    #  $MAPCOLOUR(trv$t) when in normal state)
    global Travel WConf COLOUR MAPCOLOUR

    set nels $Travel(nav,els) ; set cvels $Travel(nav,cvels)
    set fri $WConf(travel,fr).fri
    switch $act {
	notraveldata {
	    foreach e $Travel(trav,els) { set Travel(info,$e) "" }
	    set Travel(info,vel_z) ""
	    foreach p $Travel(trav,cvels) {
		foreach "cv tag" $p {}
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$cv itemconfigure $tag -fill $COLOUR(dialbg)
		    $fri.f$dsp.$cv lower $tag
		}
	    }
	}
	clear {
	    foreach e $nels { set Travel(info,$e) "" }
	    foreach p $cvels {
		foreach "cv tag" $p {}
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$cv itemconfigure $tag -fill $COLOUR(dialbg)
		    $fri.f$dsp.$cv lower $tag
		}
	    }
	}
	suspend {
	    foreach e $nels {
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$e configure -bg red
		}
	    }
	    foreach p $cvels {
		foreach "cv tag" $p {}
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$cv itemconfigure $tag -fill red
		}
	    }
	}
	restore {
	    foreach e $nels {
		set Travel(info,$e) ""
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$e configure -bg $COLOUR(bg)
		}
	    }
	    foreach p $cvels {
		foreach "cv tag" $p {}
		foreach dsp $Travel(displays) {
		    $fri.f$dsp.$cv itemconfigure $tag -fill $MAPCOLOUR(trv$tag)
		}
	    }
	}
    }
    return
}

proc TravelBadDate {} {
    # a message with invalid date was received
    global WConf Travel

    foreach dsp $Travel(displays) {
	$WConf(travel,fr).fri.f$dsp.hour configure -bg orange
    }
    return
}

proc TravelData {hms posn lat long fix alt velx vely vel_z trk speed} {
    # compute and display new data
    #  $hms is list with hour, minutes and seconds
    #  $posn is current position
    #  $lat, $long in formatted DMS corresponding to $posn
    #  $fix in {error, _, 2D, 3D, 2D-diff, 3D-diff, GPS, DGPS, Auto,
    #                 simul}
    #  the following arguments may be "_" for undefined, and stand for
    #   altitude (m), velocity components (m/s), true course (degrees),
    #   and speed (km/h)
    global WConf Travel RealTimeLogLast COLOUR MAPCOLOUR TXT DSCALE

    set fr $WConf(travel,fr)
    # fix
    if { $fix == "error" } {
	if { $Travel(nav) == "on" && $Travel(nav,ok) } {
	    TravelNavCmd suspend
	    set Travel(nav,ok) 0
	}
	foreach dsp $Travel(displays) { $fr.fri.f$dsp.fix configure -bg red }
	return
    }
    if { $fix == "_" || $fix == "simul" } {
	set c orange
    } else { set c lightgreen }
    foreach dsp $Travel(displays) { $fr.fri.f$dsp.fix configure -bg $c }
    # time
    set hour [eval format %d:%02d:%02d $hms]
    foreach dsp $Travel(displays) {
	$fr.fri.f$dsp.hour configure -bg $COLOUR(bg)
    }
    # formatted position
    set pos "$lat $long"
    # altitude
    if { [string first "2D" $fix] == 0 } {
	set alt "" ; set vel_z "_"
    } elseif { $alt != "_" } {
	set alt [expr round($alt)]
    } else { set alt "" }
    # vertical speed
    if { $vel_z == "_" } {
	set vel_z ""
	foreach dsp $Travel(displays) {
	    $fr.fri.f$dsp.c_vel_z itemconfigure vel_z -fill $COLOUR(dialbg)
	}
    } else {
	if { $vel_z < 0 } {
	    if { $Travel(wdgts,vel_z,up) } {
		TravelObjectUpDown vel_z
		set Travel(wdgts,vel_z,up) 0
	    }
	} elseif { $vel_z > 0 && ! $Travel(wdgts,vel_z,up) } {
	    TravelObjectUpDown vel_z
	    set Travel(wdgts,vel_z,up) 1
	}
	# used below as number!
	set vel_z [format "%4d" [expr round($vel_z)]]
    }
    # speed, true course and horizontal velocity vector
    set dtime [expr $RealTimeLogLast-$Travel(prevtime)]
    if { $velx == "_" || $vely == "_" } {
	if { $speed != "_" && $trk != "_" } {
	    set a [expr $trk*0.01745329251994329576]
	    # m/s
	    set velx [expr $speed*sin($a)/3.6]
	    set vely [expr $speed*cos($a)/3.6]
	} elseif { $dtime < $Travel(nav,maxdtime) } {
	    # assume $Travel(prevtime) != -1 because $RealTimeLogLast
	    #  is much larger than $Travel(nav,maxdtime)
	    # m/s
	    set latp [lindex $Travel(prevposn) 0]
	    set longp [lindex $Travel(prevposn) 1]
	    set latc [lindex $posn 0] ; set longc [lindex $posn 1]
	    set cosmlat [expr cos(($latp+$latc)*0.00872664625997164788)]
	    set velx [expr 111120.0*($longc-$longp)*$cosmlat/$dtime]
	    set vely [expr 111120.0*($latc-$latp)/$dtime]
	} else { set velx "_" ; set vely "_" }
    }
    if { $velx != "_" } {
	set novel 0 ; set notrk 0
	if { $trk == "_" && [set trk [VectorBearing $velx $vely]] == "_" } {
	    set notrk 1
	}
	# speed in km/h
	if { $speed == "_" } {
	    set speed [expr sqrt($velx*$velx+$vely*$vely)*3.6]
	}
    } else {
	set novel 1
	if { $trk == "_" } { set notrk 1 } else { set notrk 0 }
    }
    if { $notrk } {
	set trk ""
	foreach c "trkcts trkcts trn trn" e "trk ctss left right" {
	    foreach dsp $Travel(displays) {
		$fr.fri.f$dsp.c_$c itemconfigure $e -fill $COLOUR(dialbg)
	    }
	}
    } else {
	TravelTurnObjects nesw $trk
	foreach dsp $Travel(displays) {
	    $fr.fri.f$dsp.c_trkcts itemconfigure trk -fill $MAPCOLOUR(trvtrk)
	}
	set trk [expr round($trk)]
    }
    # external representations of values
    set fix $TXT(posfix$fix)
    if { $speed != "_" } {
	set ispeed $speed
	# speed in user units /h
	set speed [expr round($speed*$DSCALE)]
    } else { set speed "" }
    foreach e "fix hour speed vel_z pos alt trk" {
	set Travel(info,$e) [set $e]
    }
    # needed for warnings and not set if navigation is off
    set trn "" ; set xtk ""
    set datum $Travel(nav,datum)
    # navigation
    if { $Travel(nav) == "on" } {
	if { ! $Travel(nav,ok) } {
	    TravelNavCmd resume
	    set Travel(nav,ok) 1
	}
	# compute: ete eta vmg xtk cts trn dist
	set es "ete eta xtk dist vmg cts trn"
	set rho $Travel(nav,datum,a)
	while 1 {
	    # loop through subsequent points (if any) when arrived
	    set showcts2 0
	    set nxtgoal $Travel(nav,nxtgoal)
	    set db [ComputeDistBear $posn $nxtgoal $datum]
	    set dtkp [lindex $db 1]
	    if { $Travel(nav,state) == "starting" } {
		set Travel(nav,state) on
		set Travel(nav,dtk) $dtkp
		set Travel(nav,posn0) $posn
		if { [set afternxt $Travel(nav,afternxt)] != "" } {
		    set Travel(nav,dtkfn) \
			    [ComputeBear $nxtgoal $afternxt $datum]
		}
	    }
	    # distance in km
	    set idist [lindex $db 0]
	    #  and in user units
	    set dist [expr $idist*$DSCALE]
	    foreach "d b" [ComputeDistBear $Travel(nav,posn0) $posn $datum] {}
	    # in user units
	    set xtk [expr 1e-3*$rho*$DSCALE*asin(sin(1000.0*$d/$rho)* \
		    sin(($b-$Travel(nav,dtk))*0.01745329251994329576))]
	    set dtype $Travel(nav,dtype) ; set done 0
	    if { $dist <= $Travel(mindist) } {
		# issue arrival warning
		if { $Travel(nav,towarn) } {
		    TravelWarn \
			    [format $TXT(trvwarrv) $Travel(info,nxtWP)] normal
		}
		if { $dtype == "WP" || $dtype == "MOB" } {
		    set done 1
		    break
		}
		if { [set done [TravelChangeGoal]] } { break }
		continue
	    } elseif { $Travel(nav,mode) == "approx" && \
		    [set afternxt $Travel(nav,afternxt)] != ""} {
		if { $Travel(nav,nxtleg) == "" } {
		    set Travel(nav,nxtleg) \
			    [ComputeDist $nxtgoal $afternxt $datum]
		}
		set d2 [ComputeDist $posn $afternxt $datum]
		set sd [expr $idist+$Travel(nav,nxtleg)]
		if { $d2 < $Travel(chggparam)*$sd } {
		    # change goal
		    if { [set done [TravelChangeGoal]] } { break }
		    continue
		}
		if { $d2 < $Travel(chggwparam)*$sd } {
		    # show arrow pointing to goal after next
		    set showcts2 1
		}
	    }
	    break
	}
	if { $done } {
	    foreach e "ete eta xtk dist" { set $e 0 }
	    foreach e "vmg cts trn" { set $e "" }
	    foreach dsp $Travel(displays) {
		$fr.fri.f$dsp.c_trkcts itemconfigure ctss -fill $COLOUR(dialbg)
		$fr.fri.f$dsp.c_trn itemconfigure left -fill $COLOUR(dialbg)
		$fr.fri.f$dsp.c_trn itemconfigure right -fill $COLOUR(dialbg)
	    }
	    set showcts2 0
	} elseif { $notrk } {
	    foreach e "ete eta vmg cts trn" { set $e "" }
	    set showcts2 0
	} else {
	    # in km/h
	    set vmg [expr $ispeed*cos(abs($dtkp-$trk)*0.01745329251994329576)]
	    if { $vmg < 0.001 } {
		set ete [set eta "######"]
		set cts $dtkp
	    } else {
		# seconds
		set ete [expr round(3600.0*$idist/$vmg)]
		# time from receiver; cannot use computer time
		#  and Tcl "clock" procedure
		foreach "h m s" $hms {}
		incr s $ete ; incr m [expr $s/60]
		set s [expr $s%60] ; set h [expr ($h+$m/60)%24]
		set m [expr $m%60]
		set eta [format %d:%02d:%02d $h $m $s]
		set ete [clock format $ete -format %T]
		# no computation of drift
		set cts $dtkp
	    }
	    set trn [TravelLeftRight trn [expr $cts-$trk]]
	    TravelTurnArrow cts $trn
	    set vmg [expr round($vmg*$DSCALE)]
	}
	foreach e "dist xtk" {
	    set $e [expr round([set $e])]
	}
	if { $showcts2 } {
	    TravelTurnArrow cts2 \
		    [expr [ComputeBear $posn $afternxt $datum]-$trk]
	} else {
	    foreach dsp $Travel(displays) {
		$fr.fri.f$dsp.c_trkcts itemconfigure cts2 -fill $COLOUR(dialbg)
		$fr.fri.f$dsp.c_trkcts lower cts2 trk
	    }
	}
	foreach e $es { set Travel(info,$e) [set $e] }
    }
    # warnings (see initialization of Travel(warn:...) for list of
    #  variables assumed to be defined here
    foreach e $Travel(warnings) {
	if { $Travel(warn,$e) && [expr $Travel(warn:$e,cond)] } {
	    eval TravelWarn $Travel(warn:$e,cont) $Travel(warn,$e,level)
	}
    }    
    # saving current position and time-stamp
    set Travel(prevposn) $posn ; set Travel(prevtime) $RealTimeLogLast
    set Travel(posns) [linsert $Travel(posns) 0 $posn]

    update idletasks
    return
}

proc TravelChangeGoal {} {
    # change leg when following RT, TR or LN
    # return 1 if there are no more points
    global Travel TXT

    if { [set tix $Travel(nav,ix)] >= $Travel(nav,maxix) } { return 1 }
    if { $Travel(nav,dtype) == "RT" } {
	set Travel(info,prvWP) [lindex $Travel(nav,wpns) $tix]
	set Travel(info,nxtWP) [lindex $Travel(nav,wpns) [incr tix]]
    } else {
	# TR, LN or GoBack
	set Travel(info,prvWP) "$Travel(ptname) $Travel(nav,conv)$tix"
	set Travel(info,nxtWP) "$Travel(ptname) $Travel(nav,conv)[incr tix]"
    }
    # issue change warning
    if { $Travel(nav,towarn) } {
	TravelWarn [format $TXT(trvwchg) $Travel(info,nxtWP)] info
    }
    set Travel(nav,nxtgoal) $Travel(nav,afternxt)
    set Travel(nav,afternxt) [lindex $Travel(nav,ps) [expr $tix+1]]
    set Travel(nav,nxtleg) ""
    set Travel(nav,state) starting
    set Travel(nav,ix) $tix
    return 0
}

##### graphical

proc TravelTurnArrow {el trk} {
    # turn arrow to point to $trk degrees and colour it
    #  $el is used in indexing $Travel(wdgts,$el) and $MAPCOLOUR(trv$el);
    #   the former is a list with canvas id, list of coordinates from origin
    #   (x_m, y_m) of arrow pointing to 0, and x_m and y_m (coordinates
    #   of rotation centre)
    #   of x and y)
    global Travel MAPCOLOUR

    set rad [expr (360-$trk)*0.01745329251994329576]
    set cos [expr cos($rad)] ; set sin [expr sin($rad)]
    foreach "cid cs0 xm ym" $Travel(wdgts,$el) {}
    set cs ""
    foreach "x y" $cs0 {
	lappend cs [expr round($xm+$x*$cos+$y*$sin)] \
		[expr round($ym-$x*$sin+$y*$cos)]
    }
    foreach dsp $Travel(displays) {
	set cv $Travel(travel,dsppfr).f$dsp.c_$cid
	eval $cv coords $el $cs
	$cv itemconfigure $el -fill $MAPCOLOUR(trv$el)
	$cv raise $el
    }
    update idletasks
    return
}

proc TravelTurnObjects {el trk} {
    # turn objects $trk degrees from vertical north
    #  $el is used in indexing $Travel(wdgts,$el) which is a list with canvas
    #   id, x_m and y_m (coordinates of rotation centre), followed by
    #   object tag and list of coordinates from origin (x_m, y_m) of
    #   object at initial position for each object
    global Travel

    set dsppfr $Travel(travel,dsppfr)
    set rad [expr $trk*0.01745329251994329576]
    set cos [expr cos($rad)] ; set sin [expr sin($rad)]
    foreach "cid xm ym" $Travel(wdgts,$el) { break }
    foreach "tag cs0" [lrange $Travel(wdgts,$el) 3 end] {
	set cs ""
	foreach "x y" $cs0 {
	    lappend cs [expr round($xm+$x*$cos+$y*$sin)] \
		    [expr round($ym-$x*$sin+$y*$cos)]
	}
	foreach dsp $Travel(displays) {
	    eval $dsppfr.f$dsp.c_$cid coords $tag $cs
	}
    }
    update idletasks
    return
}

proc TravelLeftRight {el diff} {
    # change colours of pair of left-right arrows
    #  $el is used the canvas id and in indexing $MAPCOLOUR(trv$el);
    #   the canvas has two items with tags "left" and "right"
    #  $diff is desired bearing minus current bearing (both in 0..359)
    # assume $COLOUR(dialbg) to be the "off" colour
    # return turn in -180..180
    global Travel MAPCOLOUR COLOUR

    set cu $MAPCOLOUR(trv$el) ; set cd $COLOUR(dialbg)
    set dir right ; set odir left
    if { $diff == 180 } {
	set cd $cu
    } elseif { $diff == 0 } {
	set cu $cd
    } else {
	if { $diff > 180 } {
	    incr diff -360
	} elseif { $diff < -180 } { incr diff 360 }
	if { $diff < 0 } {
	    set dir left ; set odir right
	}
    }
    foreach dsp $Travel(displays) {
	set cv $Travel(travel,dsppfr).f$dsp.c_$el
	$cv itemconfigure $dir -fill $cu
	$cv itemconfigure $odir -fill $cd
    }
    return $diff
}

proc TravelObjectUpDown {el} {
    # turn canvas object upside down and colour it
    #  $el is used as the canvas id and in indexing $Travel(wdgts,$el) and
    #   $MAPCOLOUR(trv$el)
    #  $Travel(wdgts,$el) is the sum of minimum and maximum y
    # the object is turned by changing the signs of the y-coordinates and
    #  adding this sum
    global Travel MAPCOLOUR

    set cs ""
    set sum $Travel(wdgts,$el)
    set x 1
    set cv $Travel(travel,dsppfr).f1.c_$el
    foreach xory [$cv coords $el] {
	if { $x } { lappend cs $xory } else {
	    lappend cs [expr $sum-$xory]
	}
	set x [expr 1-$x]
    }
    foreach dsp $Travel(displays) {
	set cv $Travel(travel,dsppfr).f$dsp.c_$el
	$cv coords $el $cs
	$cv itemconfigure $el -fill $MAPCOLOUR(trv$el)
    }
    update idletasks
    return
}

