#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2002 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: wrtdials.tcl
#  Last change:  4 February 2002
#
# Includes contributions by
#  - Brian Baulch (baulchb@onthenet.com.au) marked "BSB contribution"
#  - Alessandro Palmas (alpalmas@tin.it) marked "AP contribution"
#

# BSB contribution: WPNum field accomodated
proc GMWPoint {index options data} {
    # create dialog window for editing/showing data of WP with given index
    #  $index is -1 if this is a new WP
    #  $options is a list of buttons to display;
    #	an empty list means no editing; supported options are:
    #     cancel, create, change, revert, forget
    #     change and forget assume $index != -1
    #     see proc GMButton for further details
    #  if $options is empty, $index cannot be -1 as this is not a new WP
    #   the only button is OK, and only binding: return to destroy
    # order of elements in $data list reflects order in $Storage(WP)
    #  which is used below
    # return window path
    global GMWPIndex GMWPNum GMWPDatum GMWPDispl GMWPSymbol GMWPDispOpt \
	    GMWPAlt GMWPData GMWPMapChg GMWPShow GMWPHidden MapLoading \
	    COLOUR DPOSX DPOSY NAMEWIDTH COMMENTWIDTH CREATIONDATE \
	    OBSWIDTH OBSHEIGHT TXT ICONWIDTH ICONHEIGHT MAXMENUITEMS \
	    SYMBOLIMAGE DISPOPTS ChangedPosn

    foreach "name num commt obs pformt posn datum date symbol dispopt \
	    alt hidden displ" $data {}
    set ed 0 ; set st disabled
    if { "$options" != "" } {
	if { [winfo exists .gmWP] } { Raise .gmWP ; bell ; return .gmWP }
	set ed 1 ; set st normal
	set w .gmWP
	set GMWPIndex $index ; set GMWPNum $num ; set GMWPDatum $datum
	set GMWPDispl $displ
	set GMWPSymbol $symbol ; set GMWPDispOpt $dispopt
	set GMWPAlt $alt ; set GMWPHidden $hidden
	# this will be set to 1 if the user edits the position entries
	#  and will contain current position (possibly "") otherwise;
	#  use of this variable assumes a single .gmWP window at a time!
	set ChangedPosn $posn
	# this depends on Storage(WP)
	set GMWPData $data
	set GMWPMapChg 0
	set x $DPOSX ; set y $DPOSY
    } else {
	set w .gmWPsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMWPShow
	set x [expr $DPOSX+45*(1+$GMWPShow % 5)]
	set y [expr $DPOSY+45*(1+$GMWPShow % 5)]
    }

    toplevel $w
    wm title $w "GPS Manager: $TXT(waypoint)"
    wm geometry $w +$x+$y
    if { ! $ed } {
	wm protocol $w WM_DELETE_WINDOW "destroy $w"
	bind $w <Key-Return> "destroy $w"
    } else {
	wm protocol $w WM_DELETE_WINDOW { GMButton WP cancel }
    }

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ntitle -text "$TXT(name):"
    entry $w.fr.fr1.id -width $NAMEWIDTH -exportselection 1
    ShowTEdit $w.fr.fr1.id $name $ed
    label $w.fr.fr1.dtitle -text "$TXT(created):"
    entry $w.fr.fr1.date -width 18 -exportselection 1
    ShowTEdit $w.fr.fr1.date $date $ed
    if { $ed } {
	ShowPosnDatum $w.fr $pformt $posn GMWPChangeDatum GMWPDatum $st $ed \
		ChangedPosn
    } else {
	ShowPosnDatum $w.fr $pformt $posn "" $datum $st $ed nil
    }

    frame $w.fr.fr11 -relief flat -borderwidth 0
    label $w.fr.fr11.atit -text "$TXT(alt) (m):"
    entry $w.fr.fr11.alt -width 7 -exportselection 1
    ShowTEdit $w.fr.fr11.alt $alt $ed

    frame $w.fr.fr2 -relief flat -borderwidth 0
    label $w.fr.fr2.ctitle -text "$TXT(cmmt):"
    entry $w.fr.fr2.commt -width $COMMENTWIDTH -exportselection 1
    ShowTEdit $w.fr.fr2.commt $commt $ed
    menubutton $w.fr.fr2.route -text $TXT(nameRT) -relief raised \
	    -direction below -menu $w.fr.fr2.route.m
    menu $w.fr.fr2.route.m -tearoff 0
    bind $w.fr.fr2.route <Button-1> "FillWPRTMenu $w.fr.fr2.route.m $index"
    menubutton $w.fr.fr2.comp -text $TXT(comp) -relief raised \
	    -direction below -menu $w.fr.fr2.comp.m
    bind $w.fr.fr2.comp <Button-1> \
	    "FillWPOthersMenu $w.fr.fr2.comp.m.mothers $w $ed"
    menu $w.fr.fr2.comp.m -tearoff 0
    menu $w.fr.fr2.comp.m.mothers -tearoff 0
    $w.fr.fr2.comp.m add cascade -label $TXT(distazim) \
	    -menu $w.fr.fr2.comp.m.mothers
    $w.fr.fr2.comp.m add command -label $TXT(nearestWPs) \
	    -command "GMWPNearest $w $ed"

    frame $w.fr.fr3 -relief flat -borderwidth 0
    label $w.fr.fr3.obstit -text "$TXT(rmrk):"
    text $w.fr.fr3.obs -wrap word -width $OBSWIDTH -height $OBSHEIGHT \
	    -exportselection true
    $w.fr.fr3.obs insert 0.0 $obs
    $w.fr.fr3.obs configure -state $st
    TextBindings $w.fr.fr3.obs

    frame $w.fr.fr4 -relief flat -borderwidth 0
    menubutton $w.fr.fr4.symb -text $TXT(symbol) -relief raised \
	    -direction below -menu $w.fr.fr4.symb.m -state $st
    set mw $w.fr.fr4.symb.m
    menu $mw -tearoff 0
    FillSymbolsMenu $mw ChangeWPSymbol
    canvas $w.fr.fr4.symbim -width $ICONWIDTH -height $ICONHEIGHT
    $w.fr.fr4.symbim create image 1 1 -anchor nw -image $SYMBOLIMAGE($symbol)
    label $w.fr.fr4.symbname -text $TXT(SY$symbol)
    menubutton $w.fr.fr4.dispopt -text $TXT(dispopt): -relief raised \
	    -direction below -menu $w.fr.fr4.dispopt.m -state $st
    set mw $w.fr.fr4.dispopt.m
    menu $mw -tearoff 0
    foreach opt $DISPOPTS {
	$mw add command -label $TXT(DISP$opt) -command "ChangeWPDispOpt $opt"
    }
    label $w.fr.fr4.dispo -text $TXT(DISP$dispopt) -width 15

    frame $w.fr.frsel -relief flat -borderwidth 0
    if { $ed } {
	checkbutton $w.fr.displayed -text $TXT(displ) \
		-variable GMWPDispl -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	if { $MapLoading != 0 } { $w.fr.displayed configure -state disabled }
	set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		    -command "$b$e configure -state normal ; GMButton WP $e"
	    pack $b$e -side left
	}
    } else {
	checkbutton $w.fr.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check) 
	if { $displ } { $w.fr.displayed select }
	button $w.fr.frsel.b -text Ok -command "destroy $w"
	pack $w.fr.frsel.b
    }

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle $w.fr.fr1.id -side left -padx 3
    if { $CREATIONDATE } {
	pack $w.fr.fr1.dtitle $w.fr.fr1.date -side left -padx 3
    }
    pack $w.fr.fr11.atit $w.fr.fr11.alt -side left -padx 3
    pack $w.fr.fr2.ctitle $w.fr.fr2.commt -side left -padx 3
    pack $w.fr.fr2.route $w.fr.fr2.comp -side left -padx 10
    pack $w.fr.fr3.obstit $w.fr.fr3.obs -side left -padx 3
    pack $w.fr.fr4.symb $w.fr.fr4.symbim $w.fr.fr4.symbname -side left -padx 3
    pack $w.fr.fr4.dispopt $w.fr.fr4.dispo -side left -padx 5
    pack $w.fr.fr1 $w.fr.frp $w.fr.frd $w.fr.fr11 $w.fr.fr2 $w.fr.fr3 \
	    $w.fr.fr4 $w.fr.displayed $w.fr.frsel -side top -pady 5
    update idletasks
    return $w
}

# BSB contribution: indices in GMWPData affected by new WPNum field
proc RevertWP {} {
    # reset data in WP edit window to initial values
    # this depends on Storage(WP)
    global GMWPDispl GMWPData GMWPDatum INVTXT ChangedPosn

    .gmWP.fr.fr1.id delete 0 end
    .gmWP.fr.fr1.id insert 0 [lindex $GMWPData 0]
    .gmWP.fr.fr2.commt delete 0 end
    .gmWP.fr.fr2.commt insert 0 [lindex $GMWPData 2]
    .gmWP.fr.fr3.obs delete 1.0 end
    .gmWP.fr.fr3.obs insert 1.0 [lindex $GMWPData 3]
    set pft [PosType $INVTXT([.gmWP.fr.frp.pfmt cget -text])]
    set opf [lindex $GMWPData 4] ; set t [PosType $opf]
    set p [lindex $GMWPData 5]
    if { "$pft" == "$t" } {
	RevertPos .gmWP.fr.frp $opf $t $p
    } else {
	RedrawPos .gmWP.fr.frp $pft $opf $p ChangedPosn
    }
    set ChangedPosn $p
    set GMWPDatum [lindex $GMWPData 6]
    .gmWP.fr.fr1.date delete 0 end
    .gmWP.fr.fr1.date insert 0 [lindex $GMWPData 7]
    ChangeWPSymbol [lindex $GMWPData 8]
    ChangeWPDispOpt [lindex $GMWPData 9]
    .gmWP.fr.fr11.alt delete 0 end
    .gmWP.fr.fr11.alt insert 0 [lindex $GMWPData 10]
    # hidden: lindex $GMWPData 11
    set GMWPDispl [lindex $GMWPData end]
    if { $GMWPDispl } {
	.gmWP.fr.displayed select
    } else {
	.gmWP.fr.displayed deselect
    }
    return
}

proc GMWPCheck {} {
    # check validity of data in WP edit window
    # this depends on Storage(WP)
    global GMWPDatum GMWPDispl GMWPSymbol GMWPDispOpt GMWPHidden GMWPMapChg \
	    INVTXT KEEPHIDDEN MESS
    # BSB contribution
    global GMWPNum

    set r [CheckEntries GMMessage "{.gmWP.fr.fr1.id CheckName} \
	    {.gmWP.fr.fr2.commt CheckComment} {.gmWP.fr.fr1.date CheckDate}"]
    if { "$r" == "nil" } { return nil }
    set p [PosnGetCheck .gmWP.fr.frp $GMWPDatum GMMessage ChangedPosn]
    if { "$p" == "nil" } { return nil }
    set alt [string trim [.gmWP.fr.fr11.alt get]]
    if { [BadAltitude $alt] } {
	GMMessage $MESS(badalt)
	return nil
    }
    if { "$GMWPHidden" != "" } {
	switch $KEEPHIDDEN {
	    never { set GMWPHidden "" }
	    always { }
	    ask {
		if { [GMConfirm $MESS(nohidden)] } { set GMWPHidden "" }
	    }
	}
    }
    lappend r $GMWPSymbol $GMWPDispOpt $alt $GMWPHidden $GMWPDispl
    set nb [CheckNB [.gmWP.fr.fr3.obs get 0.0 end]]
    set GMWPMapChg 1
    set r [linsert $r 2 $nb $INVTXT([.gmWP.fr.frp.pfmt cget -text]) \
	                 "$p" "$GMWPDatum"]
    # BSB contribution
    return [linsert $r 1 $GMWPNum]
}

proc FillWPRTMenu {menu index} {
    # fill menu with RTs having WP of given index
    global WPRoute

    $menu delete 0 end
    if { $index > -1 } {
	foreach rt "$WPRoute($index)" {
	    $menu add command -label $rt \
		    -command "OpenItem RT [IndexNamed RT $rt]"
	}
    }
    return
}

proc FillWPOthersMenu {menu w editing} {
    # fill menu with names of other WPs for WP edit or show window
    global WPName LsW MAXMENUITEMS TXT

    if { [winfo exists $menu] } {
	destroy $menu
    }
    set mw $menu ; menu $mw -tearoff 0
    set wpn [$w.fr.fr1.id get]
    set n 0 ; set m 0
    foreach it [$LsW.frlWP.frl.box get 0 end] {
	if { "$wpn" != "$it" } {
	    if { $n > $MAXMENUITEMS } {
		$mw add cascade -label "$TXT(more) ..." -menu $mw.m$m
		set mw $mw.m$m ; menu $mw -tearoff 0
		set n 0 ; incr m
	    }
	    $mw add command -label $it \
		    -command [list GMCompDistBearWP $w $it $editing]
	    incr n
	}
    }
    return
}

proc ChangeWPSymbol {symbol args} {
    # change symbol of WP being edited
    #  $args not used, but called back like this
    global GMWPSymbol SYMBOLIMAGE TXT

    set GMWPSymbol $symbol
    set w .gmWP
    $w.fr.fr4.symbim delete all
    $w.fr.fr4.symbim create image 0 0 -anchor nw -image $SYMBOLIMAGE($symbol)
    $w.fr.fr4.symbname configure -text $TXT(SY$symbol)
    return
}

proc ChangeWPDispOpt {opt} {
    # change display option of WP being edited
    global GMWPDispOpt TXT

    set GMWPDispOpt $opt
    .gmWP.fr.fr4.dispo configure -text $TXT(DISP$opt)
    return
}

proc GMCompDistBearWP {window wp2 editing} {
    # create dialog to show distance and bearing from WP of a edit/show
    #  window to another WP with name $wp2
    global GMWPDatum WPPosn WPDatum DPOSX DPOSY COLOUR MESS TXT DSCALE DTUNIT

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

    set wp1 [$window.fr.fr1.id get]
    if { $editing } {
	set p1 [PosnGetCheck $window.fr.frp $GMWPDatum GMMessage \
		ChangedPosn]
	if { "$p1" == "nil" } { return }
	set d1 $GMWPDatum
	if { "$wp1" == "" } { set wp1 "(???)" }
    } else {
	set ix1 [IndexNamed WP $wp1]
	set p1 $WPPosn($ix1) ; set d1 $WPDatum($ix1)
    }
    set ix2 [IndexNamed WP $wp2]
    set p2 $WPPosn($ix2) ; set d2 $WPDatum($ix2)
    set db [CompDistBearDatums $p1 $d1 $p2 $d2]
    set dist [format "%8.2f" [expr [lindex $db 0]*$DSCALE]]
    set bear [format "%5d" [lindex $db 1]]

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "GPS Manager: $TXT(distazim)"
    set x [expr $DPOSX+100] ; set y [expr $DPOSY+100]
    wm geometry $w +$x+$y
  
    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.fromto -text [format $TXT(fromto) $wp1 $wp2]
    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.dist -text "$dist $DTUNIT" -width 15 -font fixed -anchor w
    label $w.fr.fr1.bear -text "$bear $TXT(degrees)" -width 15 -font fixed \
	    -anchor w
    frame $w.fr.frsel -relief flat -borderwidth 0
    button $w.fr.frsel.save -text "$TXT(save) ..." \
	    -command "SaveFile comp WPDistBear $w ; \
	              $w.fr.frsel.save configure -state normal"
    button $w.fr.frsel.ok -text Ok -command "destroy $w"

    pack $w.fr -side top
    pack $w.fr.fr1.dist $w.fr.fr1.bear -side top -pady 2
    pack $w.fr.frsel.save $w.fr.frsel.ok -side left -padx 5
    pack $w.fr.fromto $w.fr.fr1 $w.fr.frsel -side top
    return
}

proc GMWPNearest {window editing} {
    # create dialog to show nearest WPs to WP of given edit/show window
    # actually compute distances and bearings to all other WPs and sort
    #  by increasing distance
    global GMWPDatum WPName WPPosn WPDatum DPOSX DPOSY COLOUR MESS TXT LsW \
	    DTUNIT

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

    set wp1 [$window.fr.fr1.id get]
    if { $editing } {
	set p1 [PosnGetCheck $window.fr.frp $GMWPDatum GMMessage \
		ChangedPosn]
	if { "$p1" == "nil" } { return }
	set d1 $GMWPDatum ; set ix1 -1
	if { "$wp1" == "" } { set wp1 "(???)" }
    } else {
	set ix1 [IndexNamed WP $wp1]
	set p1 $WPPosn($ix1) ; set d1 $WPDatum($ix1)
    }
    SetCursor . watch

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "GPS Manager: $TXT(distazim)"
    set x [expr $DPOSX+100] ; set y [expr $DPOSY+100]
    wm geometry $w +$x+$y
  
    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.from -text [format $TXT(fromto) $wp1 ""]
    frame $w.fr.fr1 -relief flat -borderwidth 0
    frame $w.fr.fr1.frtits -relief flat -borderwidth 0
    label $w.fr.fr1.frtits.fill -width 2 -font fixed
    frame $w.fr.fr1.frbx -relief flat -borderwidth 0
    set h [$LsW.frlWP.frl.box size]
    if { $h > 15 } { set h 15 }
    foreach b "xn xd xb" m "8 8 4" t $TXT(WPnearflds) {
	label $w.fr.fr1.frtits.tit$b -width $m -text $t -font fixed
	listbox $w.fr.fr1.frbx.b$b -height $h -width $m -relief flat \
	    -yscrollcommand "$w.fr.fr1.frbx.bscr set" \
 	    -selectmode single -exportselection false -font fixed
	bind $w.fr.fr1.frbx.b$b <Double-1> {
	    set n [[winfo parent %W].bxn get [%W nearest %y]]
	    if { "$n" != "" } {
		OpenItem WP [IndexNamed WP $n]
	    }
	}
	bind $w.fr.fr1.frbx.b$b <Button-3> {
	    set n [[winfo parent %W].bxn get [%W nearest %y]]
	    if { "$n" != "" } {
		ToggleDisplayNamed WP $n
	    }
	}
	bind $w.fr.fr1.frbx.b$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] {bxn bxd bxb}
	}
    }
    # BSB contribution: wheelmouse scrolling
    set boxes [list $w.fr.fr1.frbx.bxn $w.fr.fr1.frbx.bxd $w.fr.fr1.frbx.bxb]
    scrollbar $w.fr.fr1.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes 

    SetDatumData $d1
    set i 0
    foreach ix2 [array names WPName] {
	if { "$wp1" != "$WPName($ix2)" } {
	    set p2 $WPPosn($ix2) ; set d2 $WPDatum($ix2)
	    if  { "$d1" != "$d2" } {
		set p2 [ConvertDatum [lindex $p2 0] [lindex $p2 1] $d2 $d1 DDD]
	    }
	    set db [ComputeDistBearFD $p1 $p2]
	    set d [lindex $db 0]
	    set dist [format "%8.2f" $d]
	    set bear [format "%4d" [lindex $db 1]]
	    if { $i } {
		set i0 0 ; set in $i
		while { 1 } {
		    set z [expr int(($in-$i0)/2)+$i0]
		    if { [set m [$w.fr.fr1.frbx.bxd get $z]] > $d } {
			if { $z==0 || \
		            [$w.fr.fr1.frbx.bxd get [expr $z-1]] <= $d } {
			    break
			}
			set in $z
		    } elseif { $m < $d } {
			if { $z==[expr $in-1] } {
			    set z end
			    break
			}
			set i0 $z
		    } else { break }
		}
	    } else {
		set z 0
	    }
	    $w.fr.fr1.frbx.bxn insert $z $WPName($ix2)
	    $w.fr.fr1.frbx.bxd insert $z $dist
	    $w.fr.fr1.frbx.bxb insert $z $bear
	    incr i
	}
    }

    frame $w.fr.frsel -relief flat -borderwidth 0
    button $w.fr.frsel.save -text "$TXT(save) ..." \
	    -command "SaveFile comp WPNearest $w ; \
	              $w.fr.frsel.save configure -state normal"
    button $w.fr.frsel.ok -text Ok -command "destroy $w"

    pack $w.fr -side top
    pack $w.fr.fr1.frtits.titxn $w.fr.fr1.frtits.titxd \
	    $w.fr.fr1.frtits.titxb $w.fr.fr1.frtits.fill -side left -fill y
    pack $w.fr.fr1.frbx.bxn $w.fr.fr1.frbx.bxd $w.fr.fr1.frbx.bxb \
	    $w.fr.fr1.frbx.bscr -side left -fill y
    pack $w.fr.frsel.save $w.fr.frsel.ok -side left -padx 5
    pack $w.fr.fr1.frtits $w.fr.fr1.frbx -side top -fill y -pady 1
    pack $w.fr.from $w.fr.fr1 $w.fr.frsel -side top -pady 3
	    
    ResetCursor .
    return
}

proc GMRoute {index options data} {
    # create dialog window for editing/showing data of RT with given index
    # including computed distances and bearings
    #  $index is -1 if this is a new RT
    #  $options is a list of buttons to display;
    #	an empty list means no editing; supported options are:
    #     cancel, create, change, revert, forget
    #     change and forget assume $index != -1
    #     see proc GMButton for further details
    #  if $options is empty, $index cannot be -1 as this is not a new RT
    #   the only button is OK, and only binding: return to destroy
    # an editing window created when $MapMakingRT is true will have some
    #  of its buttons disabled
    # order of elements in $data list reflects order in $Storage(RT)
    #  which is used below
    # return window path
    global GMRTIndex GMRTData GMRTDispl GMRTMapChg GMRTShow \
	    DPOSX DPOSY DPOSRTMAP COLOUR LISTHEIGHT NAMEWIDTH COMMENTWIDTH \
	    OBSWIDTH OBSHEIGHT TXT DTUNIT DSCALE MapMakingRT MapLoading \
	    Map MAPWIDTH

    foreach "number commt obs wps stages displ" $data {}
    set ed 0 ; set st disabled ; set stmm normal
    if { "$options" != "" } {
	if { [winfo exists .gmRT] } { Raise .gmRT ; bell ; return .gmRT }
	set ed 1 ; set st normal ; set stmm normal
	if { $MapMakingRT } {
	    set stmm disabled
	    set x [expr [winfo rootx $Map]+$MAPWIDTH+$DPOSRTMAP]
	} else {
	    set x $DPOSX
	}
	set y $DPOSY
	set w .gmRT
	set GMRTIndex $index ; set GMRTDispl $displ ; set GMRTMapChg 0
	# this depends on Storage(RT)
	set GMRTData $data
    } else {
	set stmm disabled
	set w .gmRTsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMRTShow
	set x [expr $DPOSX+50*(1+$GMRTShow % 5)]
	set y [expr $DPOSY+50*(1+$GMRTShow % 5)]
    }

    toplevel $w
    wm title $w "GPS Manager: $TXT(route)"
    wm geometry $w +$x+$y
    if { ! $ed } {
	wm protocol $w WM_DELETE_WINDOW "destroy $w"
	bind $w <Key-Return> "destroy $w"
    } else {
	wm protocol $w WM_DELETE_WINDOW { GMButton RT cancel }
    }

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ntitle -text "$TXT(number):"
    entry $w.fr.fr1.id -width 3 -exportselection 1
    ShowTEdit $w.fr.fr1.id $number $ed

    frame $w.fr.fr2 -relief flat -borderwidth 0
    label $w.fr.fr2.ctitle -text "$TXT(cmmt):"
    entry $w.fr.fr2.commt -width $COMMENTWIDTH -exportselection 1
    ShowTEdit $w.fr.fr2.commt $commt $ed

    frame $w.fr.fr21 -relief flat -borderwidth 0
    label $w.fr.fr21.obstit -text "$TXT(rmrk):"
    text $w.fr.fr21.obs -wrap word -width $OBSWIDTH -height $OBSHEIGHT \
	    -exportselection true
    $w.fr.fr21.obs insert 0.0 $obs
    $w.fr.fr21.obs configure -state $st
    TextBindings $w.fr.fr21.obs

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.fr31 -relief flat -borderwidth 0
    set frb $w.fr.fr3.fr31
    frame $frb.frtits -relief flat -borderwidth 0
    label $frb.frtits.fill -width 2 -font fixed
    frame $frb.frbx -relief flat -borderwidth 0
    set boxes ""
    foreach b "xn ox xd xb xda xsc xsl" \
	    m "3 $NAMEWIDTH 8 4 9 $COMMENTWIDTH $NAMEWIDTH" \
	    t $TXT(RTcompflds) {
	lappend boxes $frb.frbx.b$b
	label $frb.frtits.tit$b -width $m -text $t -font fixed
	listbox $frb.frbx.b$b -height 15 -width $m -relief flat \
	    -yscrollcommand "$frb.frbx.bscr set" \
 	    -selectmode single -exportselection false -font fixed
	bind $frb.frbx.b$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] \
		    {bxn box bxd bxb bxda bxsc bxsl}
	}
	bind $frb.frbx.b$b <Button-3> {
	    set n [[winfo parent %W].box get [%W nearest %y]]
	    if { "$n" != "" } {
		ToggleDisplayNamed WP $n
	    }
	}
    }
    foreach b "xn ox xd xb xda" {
	bind $frb.frbx.b$b <Double-1> {
	    set n [[winfo parent %W].box get [%W nearest %y]]
	    if { "$n" != "" } {
		OpenItem WP [IndexNamed WP $n]
	    }
	}
    }
    foreach b "xsc xsl" {
	bind $frb.frbx.b$b <Double-1> {
	    set p [winfo parent %W]
	    if { [set i [%W nearest %y]] < [$p.box size]-1 && \
		    "[$p.box get $i]" != "" } {
		GMRTStage $p $i
	    }
	}
    }
    # BSB contribution: wheelmouse scrolling
    # $boxes defined in the foreach loop that creates them
    scrollbar $frb.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes

    set i 1 ; set n [llength $wps]
    set td 0 ; set tddef 1
    foreach wp $wps nxt [lrange $wps 1 end] stg $stages {
	$frb.frbx.bxn insert end [format "%2d." $i]
	incr i
	$frb.frbx.box insert end "$wp"
	set d [GMRTDistBearDAltWP $frb.frbx end 0 $wp $nxt]
	if { "$d" != "---" } {
	    set td [expr $td+$d]
	} else { set tddef 0 }
	# $stg may be ""
	$frb.frbx.bxsc insert end [lindex $stg 0]
	$frb.frbx.bxsl insert end [lindex $stg 1]
    }

    frame $frb.frt -relief flat -borderwidth 0
    if { $tddef } {
	set td [format "%8.2f" $td]
    } else { set td "---" }
    label $frb.frt.tit -text [format $TXT(totdst) $DTUNIT]
    label $frb.frt.tt -text $td

    frame $w.fr.fr3.frbt -relief flat -borderwidth 0
    label $w.fr.fr3.frbt.title -text $TXT(nameWP)
    menubutton $w.fr.fr3.frbt.insb -text $TXT(insb) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.insb.m -state $stmm
    menu $w.fr.fr3.frbt.insb.m -tearoff 0
    menubutton $w.fr.fr3.frbt.insa -text $TXT(insa) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.insa.m -state $stmm
    menu $w.fr.fr3.frbt.insa.m -tearoff 0
    button $w.fr.fr3.frbt.del -text $TXT(del) -state $stmm \
	    -command { GMRTChange del }
    menubutton $w.fr.fr3.frbt.repl -text $TXT(repl) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.repl.m -state $stmm
    menu $w.fr.fr3.frbt.repl.m -tearoff 0
    frame $w.fr.fr3.frbt.sep -height 6 -bg $COLOUR(dialbg) \
	    -relief flat -borderwidth 0
    menubutton $w.fr.fr3.frbt.chg -text $TXT(change) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.chg.m -state $stmm
    menu $w.fr.fr3.frbt.chg.m -tearoff 0
    $w.fr.fr3.frbt.chg.m add command -label $TXT(invert) \
	    -command { GMRTChange inv }
    $w.fr.fr3.frbt.chg.m add command -label $TXT(chophd) \
	    -command { GMRTChange chh }
    $w.fr.fr3.frbt.chg.m add command -label $TXT(choptl) \
	    -command { GMRTChange cht }
    menu $w.fr.fr3.frbt.chg.m.mb -tearoff 0 \
	    -postcommand "FillItemsMenu $w.fr.fr3.frbt.chg.m.mb incb RT RT"
    $w.fr.fr3.frbt.chg.m add cascade -label $TXT(incb) \
	    -menu $w.fr.fr3.frbt.chg.m.mb
    menu $w.fr.fr3.frbt.chg.m.ma -tearoff 0 \
	    -postcommand "FillItemsMenu $w.fr.fr3.frbt.chg.m.ma inca RT RT"
    $w.fr.fr3.frbt.chg.m add cascade -label $TXT(inca) \
	    -menu $w.fr.fr3.frbt.chg.m.ma
    $w.fr.fr3.frbt.chg.m add command -label $TXT(clear) \
	    -command { GMRTChange clear }
    button $w.fr.fr3.frbt.edmap -text $TXT(edmap) -state $stmm \
	    -command "$w.fr.fr3.frbt.edmap configure -state normal ; \
	              MapEditRT"
    button $w.fr.fr3.frbt.totr -text $TXT(mkTR) \
	    -command "RTToTR $w ; $w.fr.fr3.frbt.totr configure -state normal"
    menubutton $w.fr.fr3.frbt.comp -text $TXT(computations) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.comp.m
    menu $w.fr.fr3.frbt.comp.m -tearoff 0
    $w.fr.fr3.frbt.comp.m add command -label "$TXT(savecomp) ..." \
	    -command "SaveFile comp RTComp $w"
    $w.fr.fr3.frbt.comp.m add command -label "$TXT(comparea) ..." \
	    -command "ComputeArea $w"

    frame $w.fr.frsel -relief flat -borderwidth 0
    if { $ed } {
	foreach b "insb insa repl" {
	    bind $w.fr.fr3.frbt.$b <Button-1> \
		    "FillItemsMenu $w.fr.fr3.frbt.$b.m $b WP RT"
	}
	checkbutton $w.fr.displayed -text $TXT(displ) -variable GMRTDispl \
		-onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
	if { $MapLoading != 0 } { $w.fr.displayed configure -state disabled }
	set i 0 ; set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		    -command "$b$e configure -state normal ; GMButton RT $e"
	    pack $b$e -side left
	    incr i
	}
    } else {
	checkbutton $w.fr.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.displayed select }
	button $w.fr.frsel.b -text Ok -command "destroy $w"
	pack $w.fr.frsel.b
    }
    # AP contribution ; command changed by MF
    button $w.fr.fr3.frbt.hgraph -text $TXT(elevation) \
	    -command "$w.fr.fr3.frbt.hgraph configure -state normal ; \
	              GMRTHgraph $w"

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle $w.fr.fr1.id -side left -padx 3
    pack $w.fr.fr2.ctitle $w.fr.fr2.commt -side left -padx 3
    pack $w.fr.fr21.obstit $w.fr.fr21.obs -side left -padx 3
    pack $frb.frtits.titxn $frb.frtits.titox $frb.frtits.titxd \
	    $frb.frtits.titxb $frb.frtits.titxda $frb.frtits.titxsc \
	    $frb.frtits.titxsl $frb.frtits.fill -side left -fill y
    pack $frb.frbx.bxn $frb.frbx.box $frb.frbx.bxd $frb.frbx.bxb \
	    $frb.frbx.bxda $frb.frbx.bxsc $frb.frbx.bxsl $frb.frbx.bscr \
	    -side left -fill y
    pack $frb.frt.tit $frb.frt.tt -side left
    pack $frb.frtits $frb.frbx -side top -fill y -pady 1
    pack $frb.frt -side top -fill y -pady 5
    # AP contribution: hgraph button
    pack $w.fr.fr3.frbt.title $w.fr.fr3.frbt.insb \
	    $w.fr.fr3.frbt.insa $w.fr.fr3.frbt.del \
	    $w.fr.fr3.frbt.repl $w.fr.fr3.frbt.sep \
	    $w.fr.fr3.frbt.chg $w.fr.fr3.frbt.edmap \
	    $w.fr.fr3.frbt.totr $w.fr.fr3.frbt.comp \
	    $w.fr.fr3.frbt.hgraph -side top -pady 3 -fill x
    pack $frb $w.fr.fr3.frbt -side left -padx 5
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.fr21 $w.fr.fr3 \
	    $w.fr.displayed $w.fr.frsel -side top -pady 5
    update idletasks
    return $w
}

proc GMRTHgraph {w} {
    # AP contribution
    # compute data in RT edit/show window and call Hgraph
    global WPAlt

    set frb $w.fr.fr3.fr31
    set dbTot 0
    set alTot 0
    set lll ""
    set tmp [expr [$frb.frbx.bxn size]-1]
    for { set i 0 } { $i < $tmp } { incr i } {
	set tmp2 [$frb.frbx.bxd get $i]
	#get the elevation
	set tmpname [$frb.frbx.box get $i];# get the wp name of ith WP
	set tmpwpindex [IndexNamed WP $tmpname];# get its index (-1 if undef)
	if { $tmpwpindex >= 0 } { set alTot $WPAlt($tmpwpindex) }
	lappend lll [list $dbTot $alTot]
	# since each leg gives the distance to the next wp, we must save the
	# current dbtot to be associated to next wp
	set dbTot [expr $dbTot + $tmp2]
    }
    # and now the last one!
    set tmpname [$frb.frbx.box get $tmp];# get the wp name of ith WP
    set tmpwpindex [IndexNamed WP $tmpname];# get its index (-1 if undef)
    if { $tmpwpindex >= 0 } { set alTot $WPAlt($tmpwpindex) }
    lappend lll [list $dbTot $alTot]
    Hgraph $lll
    return
}

proc GMRouteMapEdit {} {
    # change RT edit window when the RT is to be edited on the map
    global Map MAPWIDTH DPOSRTMAP DPOSY

    set x [expr [winfo rootx $Map]+$MAPWIDTH+$DPOSRTMAP]
    wm geometry .gmRT +$x+$DPOSY
    foreach b "insb insa repl chg edmap" {
	.gmRT.fr.fr3.frbt.$b configure -state disabled
    }
    .gmRT.fr.displayed configure -state disabled
    return
}

proc GMRouteMapEditEnd {} {
    # change RT edit window when the RT stops being edited on the map
    global GMRTDispl GMRTIndex

    foreach b "insb insa repl chg edmap" {
	.gmRT.fr.fr3.frbt.$b configure -state normal
    }
    .gmRT.fr.displayed configure -state normal
    .gmRT.fr.displayed deselect
    return
}

proc GMRouteSelect {i} {
    # select $i-th WP in RT edit window
    #  $i may be an integer from 0, or "end"

    set frbx .gmRT.fr.fr3.fr31.frbx
    foreach b "bxn box bxd bxb" {
	$frbx.$b selection clear 0 end
	$frbx.$b selection set $i
    }
    foreach b "bxda bxsc bxsl" { $frbx.$b selection clear 0 end }
    return
}

proc RevertRT {} {
    # reset data in RT edit window to initial values
    # this depends on Storage(RT)
    global GMRTData GMRTDispl GMRTMapChg MapMakingRT GMRTIndex

    if { $MapMakingRT } { MapCancelRT dontask dontclose }
     set GMRTMapChg 0
    .gmRT.fr.fr1.id delete 0 end
    .gmRT.fr.fr1.id insert 0 [lindex $GMRTData 0]
    .gmRT.fr.fr2.commt delete 0 end
    .gmRT.fr.fr2.commt insert 0 [lindex $GMRTData 1]
    .gmRT.fr.fr21.obs delete 1.0 end
    .gmRT.fr.fr21.obs insert 1.0 [lindex $GMRTData 2]
    set frb .gmRT.fr.fr3.fr31
    foreach b "xn ox xd xb xda xsc xsl" {
	$frb.frbx.b$b delete 0 end
    }
    set wps [lindex $GMRTData 3] ; set stages [lindex $GMRTData 4]
    set i 1 ; set n [llength $wps]
    set td 0 ; set tddef 1
    foreach wp $wps nxt [lrange $wps 1 end] st $stages {
	$frb.frbx.bxn insert end [format "%2d." $i]
	incr i
	$frb.frbx.box insert end "$wp"
	set d [GMRTDistBearDAltWP $frb.frbx end 0 $wp $nxt]
	if { "$d" != "---" } {
	    set td [expr $td+$d]
	} else { set tddef 0 }
	# $st may be ""
	$frb.frbx.bxsc insert end [lindex $st 0]
	$frb.frbx.bxsl insert end [lindex $st 1]
    }
    if { $tddef } {
	set td [format "%8.2f" $td]
    } else { set td "---" }
    $frb.frt.tt configure -text $td

    if { [set d [lindex $GMRTData end]] && ! $GMRTDispl } {
	PutMapRT $GMRTIndex
    }
    if { [set GMRTDispl $d] } {
	.gmRT.fr.displayed select
    } else {
	.gmRT.fr.displayed deselect
    }
    return
}

proc GMRTCheck {} {
    # check validity of data in RT edit window
    # this depends on Storage(RT)
    global GMRTDispl GMRTMapChg GMRTData MAXWPINROUTE MESS TXT DataIndex

    set r [CheckEntries GMMessage "{.gmRT.fr.fr1.id CheckNumber} \
	     {.gmRT.fr.fr2.commt CheckComment}"]
    set n [.gmRT.fr.fr3.fr31.frbx.box size]
    if { $n == 0 } {
	GMMessage $MESS(voidRT)
	set r nil
    } elseif { $n > $MAXWPINROUTE && \
	    ![GMConfirm [format $MESS(toomany) $TXT(nameWP) $MAXWPINROUTE]] } {
	set r nil
    }
    if { [.gmRT.fr.fr1.id get]==0 && \
	    ![GMConfirm $MESS(activeRT)] } {
	return nil
    }
    if { "$r" != "nil" } {
	set nb [CheckNB [.gmRT.fr.fr21.obs get 0.0 end]]
	set stages [GMRTStages .gmRT.fr.fr3.fr31.frbx]
	set oldstages [lindex $GMRTData 4]
	set GMRTMapChg [expr $GMRTMapChg||[string compare $stages $oldstages]]
	lappend r "[.gmRT.fr.fr3.fr31.frbx.box get 0 end]" $stages $GMRTDispl
	set r [linsert $r 2 $nb]
    }
    return $r
}

proc GMRTStage {sfr i} {
    # edit stage from frame $sfr at position $i of listbox in RT edit window
    global TXT DPOSX DPOSY COLOUR COMMENTWIDTH NAMEWIDTH

    if { $i == [$sfr.bxn size] } { return }
    set w .gmRS
    if { [winfo exists $w] } { Raise $w ; bell ; return }
    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "GPS Manager: $TXT(stage)"
    wm transient $w .gmRT
    wm geometry $w +$DPOSX+$DPOSY

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.title -text "[$sfr.box get $i]-[$sfr.box get [expr $i+1]]"

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ctitle -text "$TXT(cmmt):"
    entry $w.fr.fr1.commt -width $COMMENTWIDTH -exportselection 1
    ShowTEdit $w.fr.fr1.commt [$sfr.bxsc get $i] 1
    label $w.fr.fr1.ltitle -text "$TXT(label):"
    entry $w.fr.fr1.label -width $NAMEWIDTH -exportselection 1
    ShowTEdit $w.fr.fr1.label [$sfr.bxsl get $i] 1

    frame $w.fr.bs -relief flat -borderwidth 0
    button $w.fr.bs.ok -text Ok -command "GMRTStageFinish $sfr $i"
    button $w.fr.bs.cancel -text $TXT(cancel) -command "destroy $w"

    grid config $w.fr.fr1.ctitle -column 0 -row 0 -sticky w
    grid config $w.fr.fr1.commt -column 1 -row 0 -sticky w
    grid config $w.fr.fr1.ltitle -column 0 -row 1 -sticky w
    grid config $w.fr.fr1.label -column 1 -row 1 -sticky w
    pack $w.fr.bs.ok $w.fr.bs.cancel -side left -pady 5
    pack $w.fr.title $w.fr.fr1 $w.fr.bs -side top -pady 5
    pack $w.fr -side top

    update idletasks
    grab $w
    RaiseWindow $w
    return
}

proc GMRTStageFinish {sfr i} {
    # change RT stage as in corresponding edit window
    # (see proc GMRTStage for the details)

    set efr .gmRS.fr.fr1
    $sfr.bxsc insert $i [string trim [$efr.commt get]]
    $sfr.bxsl insert $i [string trim [$efr.label get]]
    foreach b "bxsc bxsl" {
	$sfr.$b delete [expr $i+1] ; $sfr.$b selection set $i
    }
    destroy .gmRS
    return
}

proc GMRTStages {w} {
    # get information on stages from frame $w in RT edit window
    # return "" if no information found

    set sts "" ; set noinfo 1 ; set lstbut1 [expr [$w.box size]-2]
    foreach sc [$w.bxsc get 0 $lstbut1] sl [$w.bxsl get 0 $lstbut1] {
	set sc [string trim $sc] ; set sl [string trim $sl]
	if { "$sc" != "" || "$sl" != "" } {
	    set noinfo 0
	    set st [list $sc $sl]
	} else { set st "" }
	lappend sts $st
    }
    if { $noinfo } { return "" }
    return $sts
}

proc GMRTChange {how args} {
    # perform edit operations on RT
    #  $how is one of
    #      insb  insert WP before selected WP in list or at the beginning
    #      insa  insert WP after selected WP in list or at the end
    #      repl  replace a WP by another one
    #      del   delete a WP
    #      inv   invert RT
    #      chh   chop head: delete all WPs from first to selected one inclusive
    #              or just first one if there is no selection
    #      cht   chop tail: delete all WPs from selected to end or last one
    #      incb  include RT before selected or at beginning
    #      inca  include RT after selected or at end
    #      clear clear all WPs
    #  $args
    #      for $how in {insb, insa, repl}, is the name of the other WP
    #      for $how==del, if making RT on map and in answer to event on map
    #       is either 0 or "sel" for previous; otherwise (Delete button
    #       in RT window), is ""
    #      for $how in {incb, inca}, is the name of the other RT
    #      in other cases, is ""
    # if making RT on map there must be more than one WP
    global GMRTMapChg MapMakingRT MapRTLast RTWPoints RTStages

    set GMRTMapChg 1
    set frbx .gmRT.fr.fr3.fr31.frbx
    set sel [$frbx.box curselection]
    switch $how {
	insb {
	    set owp [lindex $args 0]
	    if { "$sel" != "" } {
		set nxt [expr $sel+1]
	    } else { set nxt [set sel 0] }
	    set tddef 1
	    if { $sel != 0 } {
		set p [expr $sel -1]
		set wpp [$frbx.box get $p]
		set d [GMRTDistBearDAltWP $frbx $p 1 $wpp $owp]
		if { "$d" == "---" } {
		    set tddef 0
		}
	    }
	    set d [GMRTDistBearDAltWP $frbx $sel 0 $owp [$frbx.box get $sel]]
	    if { "$d" == "---" } {
		set tddef 0
	    }
	    $frbx.box insert $sel $owp
	    $frbx.bxn insert end [format %2d. [$frbx.box size]]
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    $frbx.bxsc insert $sel "" ; $frbx.bxsl insert $sel ""
	    if { $nxt } {
		foreach b "xn ox xd xb xda xsc xsl" {
		    $frbx.b$b selection clear 0 end
		    $frbx.b$b selection set $nxt $nxt
		}
	    }
	}
	insa {
	    set owp [lindex $args 0]
	    set tddef 1
	    if { "$sel" == "" || \
		    [set last [expr [$frbx.bxn size]-1]] == $sel } {
		set sel [set nxt end]
		set wpnxt ""
	    } else {
		set nxt [expr $sel+1]
		set wpnxt [$frbx.box get $nxt]
	    }
	    if { "[set cwp [$frbx.box get $sel]]" == "" } {
		set d "---" ; set tddef 0
	    } else {
		set d [GMRTDistBearDAltWP $frbx $sel 1 $cwp $owp]
		if { "$d" == "---" } {
		    set tddef 0
		}
	    }
	    set d [GMRTDistBearDAltWP $frbx $nxt 0 $owp $wpnxt]
	    if { "$d" == "---" } {
		set tddef 0
	    }
	    $frbx.box insert $nxt $owp
	    $frbx.bxn insert end [format %2d. [$frbx.box size]]
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    $frbx.bxsc insert $nxt "" ; $frbx.bxsl insert $nxt ""
	    GMRouteSelect $sel
	}
	repl {
	    set owp [lindex $args 0]
	    if { "$sel" == "" } { return }
	    ReplaceWPInRTWindow $sel $frbx .gmRT.fr.fr3.fr31.frt $owp
	}
	del  {
	    set last [expr [$frbx.bxn size]-1]
	    if { $MapMakingRT } {
		# if called in answer to event on the map
		# $args may be "0" (delete 1st WP) for "sel" (delete selected)
		# otherwise $args==""
		if { $MapRTLast == 0 } { bell ; return }
		if { $MapRTLast == 1 } {
		    .gmRT.fr.fr3.frbt.del configure -state disabled
		}
		# as there were at least 2 WPs, $prev will be set below
		#  unless the 1st WP is being deleted
		if { [lindex $args 0] == 0 } {
		    set sel 0
		} elseif { "$sel" == "" } { BUG "no selected WP to delete" }
		set delwp [$frbx.box get $sel]
	    } elseif { "$sel" == "" } { return }
	    set tddef 1
	    if { $sel > 0 } {
		set p [expr $sel-1]
		set prev [$frbx.box get $p]
		if { $sel == $last } {
		    $frbx.bxd delete $p ; $frbx.bxd insert $p "========"
		    $frbx.bxb delete $p ; $frbx.bxb insert $p "==="
		    $frbx.bxsc delete $p ; $frbx.bxsl delete $p
		} else {
		    set d [GMRTDistBearDAltWP $frbx $p 1 $prev \
			                  [$frbx.box get [expr $sel+1]]]
		    if { "$d" == "---" } { set tddef 0 }
		}
	    }
	    foreach b "ox xd xb xda xsc xsl" {
		$frbx.b$b delete $sel
		$frbx.b$b selection clear 0 end
	    }
	    $frbx.bxn delete end
	    $frbx.bxn selection clear 0 end
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    if { $MapMakingRT } {
		if { $sel == 0 } {
		    MapDelRT1st $delwp
		} else { MapDelRTPrevious $prev $delwp }
	    }
	}
	inv  {
	    if { [set lstbut1 [expr [$frbx.bxn size]-2]] < 0 } { return }
	    set wps [$frbx.box get 0 end]
	    set stcs [$frbx.bxsc get 0 $lstbut1]
	    set stls [$frbx.bxsl get 0 $lstbut1]
	    set ds [$frbx.bxd get 0 $lstbut1]
	    set bs [$frbx.bxb get 0 $lstbut1]
	    set das [$frbx.bxda get 0 $lstbut1]
	    foreach b "ox xd xb xda xsc xsl" {
		$frbx.b$b delete 0 end
	    }
	    foreach w $wps {
		$frbx.box insert 0 $w
	    }
	    $frbx.bxd insert 0 "========" ; $frbx.bxb insert 0 "==="
	    foreach d $ds b $bs da $das {
		if { $b > 179 } {
		    set b [expr $b-180]
		} else { set b [expr 180+$b] }
		$frbx.bxd insert 0 $d ; $frbx.bxb insert 0 [format %4d $b]
		if { "$da" != "" } { set da [format %7.1f [expr -$da]] }
		$frbx.bxda insert 0 $da
	    }
	    foreach stc $stcs stl $stls {
		$frbx.bxsc insert 0 $stc ; $frbx.bxsl insert 0 $stl
	    }
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b selection clear 0 end
	    }
	}
	clear {
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b delete 0 end
	    }
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt 1
	}
	chh {
	    # chop head: delete WPs from first to selected, or first one
	    if { "$sel" == "" } { set sel 0 }
	    foreach b "ox xd xb xda xsc xsl" {
		$frbx.b$b delete 0 $sel
		$frbx.b$b selection clear 0 end
	    }
	    $frbx.bxn delete [$frbx.box size] end
	    $frbx.bxn selection clear 0 end
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt 1
	}
	cht {
	    # chop tail: delete all WPs from selected to end, or last one
	    if { "$sel" == "" } { set sel end }
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b delete $sel end
		$frbx.b$b selection clear 0 end
		$frbx.b$b yview end
	    }
	    foreach b "xd xb xda xsc xsl" v "======== ==== {} {} {}" {
		$frbx.b$b delete end ; $frbx.b$b insert end $v
		$frbx.b$b yview end
	    }
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt 1
	}
	incb {
	    # include RT before selected or first
	    if { [set ixrt [IndexNamed RT [lindex $args 0]]] == -1 } {
		return
	    }
	    if { "$sel" == "" } { set sel 0 }
	    set wps $RTWPoints($ixrt)
	    set tddef 1
	    if { $sel != 0 } {
		set pr [expr $sel-1]
		set wp [$frbx.box get $pr]
		set d [GMRTDistBearDAltWP $frbx $pr 1 $wp [lindex $wps 0]]
		if { "$d" == "---" } { set tddef 0 }
	    }
	    set nwps [lrange $wps 1 end] ; lappend nwps [$frbx.box get $sel]
	    set i [$frbx.bxn size]
	    foreach wp $wps nxt $nwps stg $RTStages($ixrt) {
		incr i
		$frbx.bxn insert end [format "%2d." $i]
		$frbx.box insert $sel "$wp"
		set d [GMRTDistBearDAltWP $frbx $sel 0 $wp $nxt]
		if { "$d"== "---" } { set tddef 0 }
		# $stg may be ""
		$frbx.bxsc insert end [lindex $stg 0]
		$frbx.bxsl insert end [lindex $stg 1]
		incr sel
	    }
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b selection clear 0 end
	    }
	}
	inca {
	    # include RT after selected or at end
	    if { [set ixrt [IndexNamed RT [lindex $args 0]]] == -1 } {
		return
	    }
	    set i [$frbx.bxn size]
	    if { "$sel" == "" } { set sel [expr $i-1] }
	    set wps $RTWPoints($ixrt)
	    set wp [$frbx.box get $sel]
	    set d [GMRTDistBearDAltWP $frbx $sel 1 $wp [lindex $wps 0]]
	    if { "$d" == "---" } {
		set tddef 0
	    } else { set tddef 1 }
	    set nwps [lrange $wps 1 end]
	    if { $sel < $i-1 } {
		lappend nwps [$frbx.box get [expr $sel+1]]
	    }
	    foreach wp $wps nxt $nwps stg $RTStages($ixrt) {
		incr i ; incr sel
		$frbx.bxn insert end [format "%2d." $i]
		$frbx.box insert $sel "$wp"
		set d [GMRTDistBearDAltWP $frbx $sel 0 $wp $nxt]
		if { "$d"== "---" } { set tddef 0 }
		# $stg may be ""
		$frbx.bxsc insert end [lindex $stg 0]
		$frbx.bxsl insert end [lindex $stg 1]
	    }
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b selection clear 0 end
	    }
	}
    }
    return
}

proc ReplaceWPInRTWindow {i frbx frt wpn} {
    # replace WP at position $i in boxes framed by $frbx by WP named $wpn
    #  $frt is the frame where the total distance is shown
    global MapMakingRT

    $frbx.box insert $i $wpn
    $frbx.box delete [expr $i+1]
    set tddef 1
    if { $i > 0 } {
	set p [expr $i-1]
	set d [GMRTDistBearDAltWP $frbx $p 1 [$frbx.box get $p] $wpn]
	if { "$d" == "---" } {
	    set tddef 0
	}
    }
    if { $i < [expr [$frbx.bxn size]-1] } {
	set p [expr $i+1]
	set d [GMRTDistBearDAltWP $frbx $i 1 $wpn [$frbx.box get $p]]
	if { "$d" == "---" } { set tddef 0 }
    }
    GMRTConfigTDist $frbx $frt $tddef
    if { ! $MapMakingRT } {
	foreach b "xn ox xd xb xda xsc xsl" {
	    $frbx.b$b selection clear 0 end
	    $frbx.b$b selection set $i
	}
    }
    return
}

proc GMRTDistBearDAltWP {fr i del wp1 wp2} {
    # compute distance, bearing and difference in altitude between two WPs
    #  and insert them in listboxes under $fr at index $i, deleting previous
    #  value if $del is set; $wp2 can be ""
    # (see GMRoute for structure of $fr and listboxes)
    # return distance or "---" if could not compute it, or 0 if $wp2 is ""
    global DSCALE WPAlt

    set da ""
    if { "$wp2" != "" } {
	set db [CompWPDistBear $wp1 $wp2]
	set d [lindex $db 0]
	if { "$d" != "---" } {
	    set d [expr $d*$DSCALE]
	    set pd [format %8.2f $d]
	    set db [format %4d [lindex $db 1]]
	} else {
	    set db "---" ; set pd "--------"
	}
	if { [set ix1 [IndexNamed WP $wp1]] != -1 && \
		[set ix2 [IndexNamed WP $wp2]] != -1 && \
		"[set a1 $WPAlt($ix1)]" != "" && \
		"[set a2 $WPAlt($ix2)]" != "" } {
	    set da [format "%7.1f" [expr $a2-$a1]]
	}
    } else { set d 0 ; set pd "========" ; set db "====" }
    if { $del } {
	$fr.bxd delete $i ; $fr.bxb delete $i ; $fr.bxda delete $i
    }
    $fr.bxd insert $i $pd ; $fr.bxb insert $i $db
    $fr.bxda insert $i $da
    return $d
}

proc GMRTConfigTDist {frbx frt def} {
    # compute and configure total distance if expected to be defined
    #  $frbx is frame for listboxes with distances
    #  $frt is the frame for the total distance label
    #  $def is 1 if distance is expected to be defined

    if { $def } {
	set td 0
	foreach d [$frbx.bxd get 0 end] {
	    if { "$d" == "---" } {
		$frt.tt configure -text "---"
		break
	    }
	    if { "$d" != "========" } {
		set td [expr $td+$d]
	    }
	}
	$frt.tt configure -text [format %8.2f $td]
    } else {
	$frt.tt configure -text "---"
    }
    return
}

proc ChangeWPInRTWindows {oldname newname} {
    # replace name of WP in RT windows and/or remake computations
    # change data on RT being edited if WP belongs to it and was renamed
    # assume edited WP has already been stored in the data-base
    global GMRTData

    if { [winfo exists .gmRT] } {
	set wps "" ; set found 0
	foreach wp [lindex $GMRTData 3] {
	    if { "$wp" == "$oldname" } { set wp $newname ; set found 1 }
	    lappend wps $wp
	}
	if { $found } { set GMRTData [lreplace $GMRTData 3 3 $wps] }
	set frbx .gmRT.fr.fr3.fr31.frbx ; set i 0
	foreach n [$frbx.box get 0 end] {
	    if { "$n" == "$oldname" } {
		ReplaceWPInRTWindow $i $frbx .gmRT.fr.fr3.fr31.frt $newname
	    }
	    incr i
	}
    }
    set ws [winfo children .]
    while { 1 } {
	if { [set i [lsearch -glob $ws ".gmRTsh*"]] == -1 } { return }
	set w [lindex $ws $i]
	set ws [lrange $ws [expr $i+1] end]
	set frbx $w.fr.fr3.fr31.frbx ; set i 0
	foreach n [$frbx.box get 0 end] {
	    if { "$n" == "$oldname" } {
		ReplaceWPInRTWindow $i $frbx $w.fr.fr3.fr31.frt $newname
	    }
	    incr i
	}
    }
    return
}

proc RTToTR {w} {
    # make a TR from the RT in window $w
    global WPPosn WPAlt WPDatum Datum

    set tpfs "latd longd latDMS longDMS"
    set tpfsa "alt latd longd latDMS longDMS"
    set tps ""
    foreach wpn [.gmRT.fr.fr3.fr31.frbx.box get 0 end] {
	if { [set wpix [IndexNamed WP $wpn]] != -1 } {
	    set p $WPPosn($wpix)
	    if { "$WPDatum($wpix)" != "$Datum" } {
		set p [ConvertDatum [lindex $p 0] [lindex $p 1] \
			$WPDatum($wpix) $Datum DMS]
	    } else {
		set p [CreatePos [lindex $p 0] [lindex $p 1] \
			DMS latlong $Datum]
	    }
	    set p [lrange $p 0 3]
	    if { "[set a $WPAlt($wpix)]" != "" } {
		lappend tps [FormData TP $tpfsa [linsert $p 0 $a]]
	    } else { lappend tps [FormData TP $tpfs $p] }
	}
    }
    if { "$tps" == "" } { bell ; return }
    set opts "create revert cancel"
    GMTrack -1 $opts [FormData TR TPoints [list $tps]]
    return
}

proc ComputeArea {w} {
    # compute area of polygon whose boundary is the RT in window $w
    # the RT cannot intersect itself; in particular its WPs cannot occur
    #  twice except for the 1st one that can appear also as last one
    # only this particular case is checked
    # computation is either based on a spherical approximation, or, if that
    #  has precision problems (too small areas), done by projecting on the
    #  plane and calculating the area of projected polygon; the projection
    #  used for this is the Transverse Mercator, or the Universal Polar
    #  Stereographic for absolute latitudes above ca. 80 degrees
    global MESS ASCALE ARUNIT

    set wps [$w.fr.fr3.fr31.frbx.box get 0 end]
    if { "[lindex $wps 0]" == "[lindex $wps end]" } {
	set wps [lreplace $wps end end]
    }
    if { [set n [llength $wps]] < 3 } {
	GMMessage $MESS(missingdata)
	return
    }
    set wpixs [Apply $wps IndexNamed WP]
    set os [lreplace $wpixs 0 0]
    foreach ix $wpixs {
	foreach ixn $os {
	    if { $ix == $ixn } {
		GMMessage $MESS(selfintsct)
		return
	    }
	}
	set os [lreplace $os 0 0]
    }
    if { [set area [SphericalArea $wpixs]] < 0 } {
	GMMessage $MESS(projarea)
	set area [ProjectedArea $wpixs]
	if { $area < 0.001 } {
	    GMMessage [format $MESS(areatoosmall) 0.001]
	    return
	}
    }
    GMMessage [format $MESS(areais) [expr $area*$ASCALE] $ARUNIT]
    return
}

proc GMTrack {index options data} {
    # create dialog window for editing/showing data of TR with given index
    #  $options is a list of buttons to display;
    #  $index is -1 if this is a new TR
    #	an empty list means no editing; supported options are:
    #     cancel, create, change, revert, forget
    #     change and forget assume $index != -1
    #     see proc GMButton for further details
    #  if $options is empty, $index cannot be -1 as this is not a new TR
    #   the only button is OK, and only binding: return to destroy
    # order of elements in $data list reflects order in $Storage(TR)
    #  which is used below
    # return window path
    global GMTRIndex GMTRData GMTRDatum GMTRTPs GMTRDispl GMTRMapChg GMTRShow \
	    GMTRHidden MapLoading DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	    DATEWIDTH OBSWIDTH OBSHEIGHT TXT

    foreach "name obs datum tps hidden displ" $data {}
    set ed 0 ; set st disabled
    if { "$options" != "" } {
	if { [winfo exists .gmTR] } { Raise .gmTR ; bell ; return .gmTR }
	set ed 1 ; set st normal
	set w .gmTR
	set GMTRIndex $index ; set GMTRDispl $displ ; set GMTRDatum $datum
	set GMTRTPs $tps ; set GMTRHidden $hidden ; set GMTRMapChg 0
	# this depends on Storage(TR)
	set GMTRData $data
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmTRsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMTRShow
	set x [expr $DPOSX+50*((1+$GMTRShow) % 5)]
	set y [expr $DPOSY+50*((1+$GMTRShow) % 5)]
    }

    toplevel $w
    wm title $w "GPS Manager: $TXT(track)"
    wm geometry $w +$x+$y
    if { ! $ed } {
	wm protocol $w WM_DELETE_WINDOW "destroy $w"
	bind $w <Key-Return> "destroy $w"
    } else {
	wm protocol $w WM_DELETE_WINDOW { GMButton TR cancel }
    }

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ntitle -text "$TXT(name):"
    entry $w.fr.fr1.id -width $DATEWIDTH -exportselection 1
    ShowTEdit $w.fr.fr1.id $name $ed

    frame $w.fr.fr2 -relief flat -borderwidth 0
    label $w.fr.fr2.obstit -text "$TXT(rmrk):"
    text $w.fr.fr2.obs -wrap word -width $OBSWIDTH -height $OBSHEIGHT \
	    -exportselection true
    $w.fr.fr2.obs insert 0.0 $obs
    $w.fr.fr2.obs configure -state $st
    TextBindings $w.fr.fr2.obs

    frame $w.fr.frd -relief flat -borderwidth 0
    menubutton $w.fr.frd.dttitle -text Datum -relief raised \
	    -direction below -menu $w.fr.frd.dttitle.m -state $st
    menu $w.fr.frd.dttitle.m -tearoff 0
    if { $ed } {
	FillDatumMenu $w.fr.frd.dttitle.m GMTRChangeDatum
	label $w.fr.frd.datum -text $GMTRDatum -textvariable GMTRDatum \
		-width [Measure "Astro B4 Sorol Atoll"]
    } else {
	label $w.fr.frd.datum -text $datum \
		-width [Measure "Astro B4 Sorol Atoll"]
    }

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    foreach b "n d lat long alt dep" m "5 $DATEWIDTH 12 12 7 7" {
	listbox $w.fr.fr3.frbx.bx$b -height 15 -width $m -relief flat \
	    -yscrollcommand "$w.fr.fr3.frbx.bscr set" \
 	    -selectmode single -exportselection false
	bind $w.fr.fr3.frbx.bx$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] \
		    {bxn bxd bxlat bxlong bxalt bxdep}
	}
	bind $w.fr.fr3.frbx.bx$b <Double-1> {
	    set i [%W nearest %y] ; set n [%W get $i]
	    set ww [winfo toplevel %W]
	    MultSelect [winfo parent %W] $i {bxn bxd bxlat bxlong bxalt bxdep}
	    if { "$n" != "" } {
		MarkPoint TR $ww $i
	    }
	}
    }
    # BSB contribution: wheelmouse scrolling
    set boxes [list $w.fr.fr3.frbx.bxd $w.fr.fr3.frbx.bxn \
	          $w.fr.fr3.frbx.bxlat $w.fr.fr3.frbx.bxlong \
	          $w.fr.fr3.frbx.bxalt $w.fr.fr3.frbx.bxdep]
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes
    FillTPs $w $tps

    frame $w.fr.fr3.frbt -relief flat -borderwidth 0
    button $w.fr.fr3.frbt.chh -text $TXT(chophd) -state $st \
	    -command { GMTRChange chh }
    button $w.fr.fr3.frbt.cht -text $TXT(choptl) -state $st \
	    -command { GMTRChange cht }
    menubutton $w.fr.fr3.frbt.inc -text $TXT(incb) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.inc.m -state $st
    menu $w.fr.fr3.frbt.inc.m -tearoff 0
    menubutton $w.fr.fr3.frbt.app -text $TXT(app) -relief raised \
	    -direction right -menu .gmTR.fr.fr3.frbt.app.m -state $st
    menu $w.fr.fr3.frbt.app.m -tearoff 0
    button $w.fr.fr3.frbt.clear -text $TXT(clear) -state $st \
	    -command { GMTRChange clear }
    frame $w.fr.fr3.frbt.sep -height 6 -bg $COLOUR(dialbg) \
	    -relief flat -borderwidth 0
    button $w.fr.fr3.frbt.anim -text $TXT(animation) -command "GMTRAnimate $w"
    button $w.fr.fr3.frbt.comp -text $TXT(comp) -command "GMTRCompute $w"
    button $w.fr.fr3.frbt.avg -text $TXT(mkavgWP) -command "GMTRtoWP $w"
    button $w.fr.fr3.frbt.route -text $TXT(mkRT) -command "GMTRtoRT $w"

    frame $w.fr.frsel -relief flat -borderwidth 0
    if { $ed } {
	foreach b "inc app" {
	    bind $w.fr.fr3.frbt.$b <Button-1> \
		    "FillItemsMenu $w.fr.fr3.frbt.$b.m $b TR TR"
	}
	checkbutton $w.fr.displayed -text $TXT(displ) -variable GMTRDispl \
		-onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
	if { $MapLoading != 0 } { $w.fr.displayed configure -state disabled }
	set i 0 ; set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		    -command "$b$e configure -state normal ; GMButton TR $e"
	    pack $b$e -side left
	    incr i
	}
    } else {
	checkbutton $w.fr.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.displayed select }
	button $w.fr.frsel.b -text Ok -command "destroy $w"
	pack $w.fr.frsel.b
    }

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle $w.fr.fr1.id -side left -padx 3
    pack $w.fr.fr2.obstit $w.fr.fr2.obs -side left -padx 3
    pack $w.fr.frd.dttitle $w.fr.frd.datum -side left -padx 3
    pack $w.fr.fr3.frbx.bxn $w.fr.fr3.frbx.bxd $w.fr.fr3.frbx.bxlat \
	    $w.fr.fr3.frbx.bxlong $w.fr.fr3.frbx.bxalt \
	    $w.fr.fr3.frbx.bxdep $w.fr.fr3.frbx.bscr -side left -fill y
    pack $w.fr.fr3.frbt.chh $w.fr.fr3.frbt.cht $w.fr.fr3.frbt.inc \
	    $w.fr.fr3.frbt.app $w.fr.fr3.frbt.clear \
	    $w.fr.fr3.frbt.sep $w.fr.fr3.frbt.anim $w.fr.fr3.frbt.comp \
	    $w.fr.fr3.frbt.avg $w.fr.fr3.frbt.route -side top -pady 2 -fill x
    pack $w.fr.fr3.frbx $w.fr.fr3.frbt -side left -padx 5
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.frd $w.fr.fr3 \
	    $w.fr.displayed $w.fr.frsel -side top -pady 5
    update idletasks
    return $w
}

proc RevertTR {} {
    # reset data in TR edit window to initial values
    # this depends on Storage(TR)
    global GMTRData GMTRDatum GMTRTPs GMTRDispl GMTRMapChg

    set GMTRMapChg 0
    .gmTR.fr.fr1.id delete 0 end
    .gmTR.fr.fr1.id insert 0 [lindex $GMTRData 0]
    .gmTR.fr.fr2.obs delete 1.0 end
    .gmTR.fr.fr2.obs insert 1.0 [lindex $GMTRData 1]
    .gmTR.fr.fr3.frbx.bxn delete 0 end
    .gmTR.fr.fr3.frbx.bxd delete 0 end
    .gmTR.fr.fr3.frbx.bxlat delete 0 end
    .gmTR.fr.fr3.frbx.bxlong delete 0 end
    .gmTR.fr.fr3.frbx.bxalt delete 0 end
    .gmTR.fr.fr3.frbx.bxdep delete 0 end
    set GMTRDatum [lindex $GMTRData 2]
    set GMTRTPs [lindex $GMTRData 3]
    FillTPs .gmTR $GMTRTPs
    # hidden attributes: [lindex $GMTRData 4]
    set GMTRDispl [lindex $GMTRData end]
    if { $GMTRDispl } {
	.gmTR.fr.displayed select
    } else {
	.gmTR.fr.displayed deselect
    }
    return
}

proc GMTRCheck {} {
    # check validity of data in TR edit window
    # this depends on Storage(TR)
    global GMTRDatum GMTRTPs GMTRDispl GMTRHidden MESS KEEPHIDDEN

    set id [.gmTR.fr.fr1.id get]
    if { ! [CheckString GMMessage $id] } {
	focus .gmTR.fr.fr1.id
	return nil
    }
    if { [llength $GMTRTPs] == 0 } { GMMessage $MESS(voidTR) ; return nil }
    if { "$GMTRHidden" != "" } {
	switch $KEEPHIDDEN {
	    never { set GMTRHidden "" }
	    always { }
	    ask {
		if { [GMConfirm $MESS(nohidden)] } { set GMTRHidden "" }
	    }
	}
    }
    return [list $id [CheckNB [.gmTR.fr.fr2.obs get 0.0 end]] \
		 $GMTRDatum $GMTRTPs $GMTRHidden $GMTRDispl]
}

proc ChopTPs {ix1 ixn} {
    # delete all TPs in TR in range $ix1 to $ixn
    global GMTRTPs

    if { "$ix1" == "$ixn" } {
	set fd end
    } else {
	set n [.gmTR.fr.fr3.frbx.bxn size]
	if { "$ixn" == "end" } {
	    set ixn [expr $n-1]
	}
	set fd [expr $n-$ixn+$ix1-1]
    }
    .gmTR.fr.fr3.frbx.bxn delete $fd end
    .gmTR.fr.fr3.frbx.bxn selection clear 0 end
    foreach b "bxd bxlat bxlong bxalt bxdep" {
	.gmTR.fr.fr3.frbx.$b delete $ix1 $ixn
    }
    set GMTRTPs [lreplace $GMTRTPs $ix1 $ixn]
    return
}

proc GMTRNewDate {tpsa tpsb} {
    # create dialog window to get new date for the first point of a TR ($tpsa)
    #  when appending it to a another one ($tpsb)
    global DATEWIDTH EPOSX EPOSY COLOUR TempTR GMTRDatum TXT

    set l [set na [llength $tpsa]]
    if { $na > 4 } { set na 4 }
    set tpsa [lrange $tpsa [expr $l-$na] end]
    set l [set nb [llength $tpsb]]
    if { $nb > 4 } { set nb 4 }
    set tpsb [lrange $tpsb 0 [expr $nb-1]]

    toplevel .gmTRnd
    wm protocol .gmTRnd WM_DELETE_WINDOW { set TempTR cnc ; set waitnd 0 }
    wm title .gmTRnd "GPS Manager: $TXT(date)"
    wm transient .gmTRnd .gmTR
    wm geometry .gmTRnd +$EPOSX+$EPOSY
    bind .gmTRnd <Key-Return> { set TempTR ok ; set waitnd 0 }

    frame .gmTRnd.fr -relief flat -borderwidth 5 -bg $COLOUR(selbg)
    label .gmTRnd.fr.tit -text $TXT(newdate) -relief sunken

    set ll [Measure "$TXT(endprTR):"]
    frame .gmTRnd.fr.frbxp -relief flat -borderwidth 0
    label .gmTRnd.fr.frbxp.tit -text "$TXT(endprTR):" -width $ll
    listbox .gmTRnd.fr.frbxp.bxt -width $DATEWIDTH -height 4 \
	    -exportselection 0 -font fixed
    bind .gmTRnd.fr.frbxp.bxt <Button-1> {
	.gmTRnd.fr.frbxp.bxt selection clear 0 end
    }
    listbox .gmTRnd.fr.frbxp.bxl -width 8 -height 4 -exportselection 0 \
	    -font fixed
    bind .gmTRnd.fr.frbxp.bxl <Button-1> {
	.gmTRnd.fr.frbxp.bxl selection clear 0 end
    }
    set dst 0
    foreach tp $tpsa nxt [lrange $tpsa 1 end] {
	.gmTRnd.fr.frbxp.bxt insert end [lindex $tp 4]
	if { "$nxt" != "" } {
	    set d [ComputeDist $tp $nxt $GMTRDatum]
	    set dst [expr $dst+$d]
	    .gmTRnd.fr.frbxp.bxl insert end [format "%8.2f" $d]
	}
    }
    set d [ComputeDist [lindex $tpsa end] [lindex $tpsb 0] $GMTRDatum]
    .gmTRnd.fr.frbxp.bxl insert end [format "%8.2f" $d]
    set tl [lindex [lindex $tpsa end] 5]
    if { $dst != 0 } {
	set t [expr $tl-[lindex [lindex $tpsa 0] 5]]
	set ns [expr round($t*$d/$dst)+$tl]
    } else { set ns $tl }
    
    frame .gmTRnd.fr.frbxn -relief flat -borderwidth 0
    label .gmTRnd.fr.frbxn.tit -text "$TXT(begnxt):" -width $ll
    listbox .gmTRnd.fr.frbxn.bxt -width $DATEWIDTH -height 4 \
	    -exportselection 0 -font fixed
    bind .gmTRnd.fr.frbxn.bxt <Button-1> {
	.gmTRnd.fr.frbxn.bxt selection clear 0 end
    }
    listbox .gmTRnd.fr.frbxn.bxl -width 8 -height 4 -exportselection 0 \
	    -font fixed
    bind .gmTRnd.fr.frbxn.bxl <Button-1> {
	.gmTRnd.fr.frbxn.bxl selection clear 0 end
    }
    foreach tp $tpsb nxt [lrange $tpsb 1 end] {
	.gmTRnd.fr.frbxn.bxt insert end [lindex $tp 4]
	if { "$nxt" != "" } {
	    set d [ComputeDist $tp $nxt $GMTRDatum]
	    .gmTRnd.fr.frbxn.bxl insert end [format "%8.2f" $d]
	}
    }

    frame .gmTRnd.fr.fe -relief flat -borderwidth 0
    label .gmTRnd.fr.fe.tit -text "$TXT(date1st):"
    entry .gmTRnd.fr.fe.en -width $DATEWIDTH -exportselection 1
    .gmTRnd.fr.fe.en insert 0 [DateFromSecs $ns]
    TextBindings .gmTRnd.fr.fe.en

    frame .gmTRnd.fr.bs -relief flat -borderwidth 0
    button .gmTRnd.fr.bs.ok -text Ok -command { set TempTR ok ; set waitnd 0 }
    button .gmTRnd.fr.bs.cnc -text $TXT(cancel) \
	    -command { set TempTR cnc ; set waitnd 0 }

    pack .gmTRnd.fr.frbxp.tit .gmTRnd.fr.frbxp.bxt .gmTRnd.fr.frbxp.bxl \
	    -side left -fill y
    pack .gmTRnd.fr.frbxn.tit .gmTRnd.fr.frbxn.bxt .gmTRnd.fr.frbxn.bxl \
	    -side left -fill y
    pack .gmTRnd.fr.fe.tit .gmTRnd.fr.fe.en -side top -pady 3
    pack .gmTRnd.fr.bs.ok .gmTRnd.fr.bs.cnc -side left -pady 5
    pack .gmTRnd.fr.tit .gmTRnd.fr.frbxp .gmTRnd.fr.frbxn .gmTRnd.fr.fe \
	    .gmTRnd.fr.bs -side top -pady 5
    pack .gmTRnd.fr -side top

    update idletasks
    grab .gmTRnd
    RaiseWindow .gmTRnd
    while 1 {
	tkwait variable waitnd

	switch "$TempTR" {
	    ""  { }
	    cnc { 
		  destroy .gmTRnd
	          update idletasks
	          return -1
	        }
	    ok  {
		  set fn [string trim [.gmTRnd.fr.fe.en get] " "]
		  set d [CheckConvDate $fn]
		  if { "$d" != "" } {
		      destroy .gmTRnd
		      return $d
		  }
	        }
	}
    }
}

proc GMTRChange {how args} {
    # perform edit operations on TR
    #  $how is one of
    #      chh    chop head: delete all TPs from the beginning to selected
    #              or only the first one
    #      cht    chop tail: delete all TPs from the selected to end
    #              or only the last one
    #      inc    include a TR at the beginning
    #      app    append a TR (to end, of course)
    #      clear  delete all TPs
    #  $args is the name of the other TR when $how is in {inc, app}
    global TRTPoints TRDatum GMTRDatum GMTRTPs GMTRMapChg

    set GMTRMapChg 1
    set sel [.gmTR.fr.fr3.frbx.bxn curselection]
    switch $how {
	chh {
	    if { [.gmTR.fr.fr3.frbx.bxn size] == 0 } { return }
	    if { "$sel" == "" } { set sel 0 }
	    ChopTPs 0 $sel
	}
	cht {
	    if { [.gmTR.fr.fr3.frbx.bxn size] == 0 } { return }
	    if { "$sel" == "" } { set sel end }
	    ChopTPs $sel end
	}
	inc {
	    if { [set ix [IndexNamed TR [lindex $args 0]]] != -1 } {
		set tps $TRTPoints($ix)
		if { "$GMTRDatum" != "$TRDatum($ix)" } {
		    set tps [ChangeTPsDatum $tps $TRDatum($ix) $GMTRDatum]
		}
		if { [.gmTR.fr.fr3.frbx.bxn size] == 0 } {
		    FillTPs .gmTR $tps
		    set GMTRTPs "$tps"
		    return
		}
		if { [set d [GMTRNewDate $tps $GMTRTPs]] != "-1" } {
		    foreach b "bxn bxd bxlat bxlong bxalt bxdep" {
			.gmTR.fr.fr3.frbx.$b selection clear 0 end
		    }
		    set d [expr [lindex $d 1]-[lindex [lindex $GMTRTPs 0] 5]]
		    set i [.gmTR.fr.fr3.frbx.bxn size] ; set j 0
		    foreach tp $tps {
			incr i
			.gmTR.fr.fr3.frbx.bxn insert end "$i."
			.gmTR.fr.fr3.frbx.bxd insert $j [lindex $tp 4]
			.gmTR.fr.fr3.frbx.bxlat insert $j [lindex $tp 2]
			.gmTR.fr.fr3.frbx.bxlong insert $j [lindex $tp 3]
			.gmTR.fr.fr3.frbx.bxalt insert $j [lindex $tp 6]
			.gmTR.fr.fr3.frbx.bxdep insert $j [lindex $tp 7]
			incr j
		    }
		    set l $tps
		    foreach tp $GMTRTPs {
			set ns [expr [lindex $tp 5]+$d]
			set nd [DateFromSecs $ns]
			lappend l [lreplace $tp 4 5 $nd $ns]
			.gmTR.fr.fr3.frbx.bxd delete $j
			.gmTR.fr.fr3.frbx.bxd insert $j $nd
			incr j
		    }
		    set GMTRTPs $l
		}
	    }
	}
	app {
	    if { [set ix [IndexNamed TR [lindex $args 0]]] != -1 } {
		set tps $TRTPoints($ix)
		if { "$GMTRDatum" != "$TRDatum($ix)" } {
		    set tps [ChangeTPsDatum $tps $TRDatum($ix) $GMTRDatum]
		}
		if { [.gmTR.fr.fr3.frbx.bxn size] == 0 } {
		    FillTPs .gmTR $tps
		    set GMTRTPs "$tps"
		    return
		}
		if { [set d [GMTRNewDate $GMTRTPs $tps]] != -1 } {
		    foreach b "bxn bxd bxlat bxlong bxalt bxdep" {
			.gmTR.fr.fr3.frbx.$b selection clear 0 end
		    }
		    set d [expr [lindex $d 1]-[lindex [lindex $tps 0] 5]]
		    set i [.gmTR.fr.fr3.frbx.bxn size]
		    foreach tp $tps {
			incr i
			.gmTR.fr.fr3.frbx.bxn insert end "$i."
			set ns [expr [lindex $tp 5]+$d]
			set nd [DateFromSecs $ns]
			lappend GMTRTPs [lreplace $tp 4 5 $nd $ns]
			.gmTR.fr.fr3.frbx.bxd insert end $nd
			.gmTR.fr.fr3.frbx.bxlat insert end [lindex $tp 2]
			.gmTR.fr.fr3.frbx.bxlong insert end [lindex $tp 3]
			.gmTR.fr.fr3.frbx.bxalt insert end [lindex $tp 6]
			.gmTR.fr.fr3.frbx.bxdep insert end [lindex $tp 7]
		    }
		}
	    }
	}
	clear {
	    if { [.gmTR.fr.fr3.frbx.bxn size] == 0 } { return }
	    ChopTPs 0 end
	}
    }
    return
}

proc GMTRAnimate {window} {
    # launch animation for TR in edit/show window
    global GMTRTPs GMTRDatum TRTPoints TRDatum TXT

    set name [$window.fr.fr1.id get]
    if { "$window" == ".gmTR" } {
	set tps $GMTRTPs
	if { "$tps" == "" } {
	    GMMessage $MESS(voidTR)
	    return
	}
	set datum $GMTRDatum
    } else {
	set ixt [IndexNamed TR $name]
	set tps $TRTPoints($ixt) ; set datum $TRDatum($ixt)
    }
    InitAnimation TR "$TXT(nameTR): $name" $tps $datum
    return
}

# AP contribution
#=============================================================================

proc Hnormalize {x xmin scale} {
	return [expr ($x-$xmin)*$scale]
}

proc Hgraph {k} {
    	global MESS TXT BITMAP
	# in list k {x y} couples from a track
	# check to have enough points for a graph
	if {[llength $k] <3} {
    		GMMessage $MESS(missingdata)
		return true
	}
	# the last couple must be wrong to stop (force last print)
	lappend k {99999 *}

	# MX and MY are the width and heigth of the canvas
	set MX 400
	set MY 200
	# initializations
	set xmin 99999
	set ymin 99999
	set xmax -99999
	set ymax -99999
	# get max amd min of x and y
	set valids 0
	foreach a $k {
	    set x [lindex $a 0]
	    set y [lindex $a 1]
		
	    if { ! [CheckSignedFloat Ignore $x] } {continue}
	    if { ! [CheckSignedFloat Ignore $y] } {continue}
	    if {$x < $xmin} {set xmin $x}
	    if {$y < $ymin} {set ymin $y}
	    if {$x > $xmax} {set xmax $x}
	    if {$y > $ymax} {set ymax $y}
	    incr valids
	}
	if { $valids < 3 } {
	    GMMessage $MESS(missingdata)
	    return true
	}
	# now find the right quota lines to plot
	set yexcursion [expr $ymax-$ymin]
	if {$yexcursion > 10000.0} {
		set deltay [expr int($yexcursion/4.0)]
	} else {
		foreach mx "5000 2000 1000 500 250 100  0" \
		     deltay "1000  500  250 100  50  25 10" {
		     	if {$yexcursion > $mx } { break }
		}     	
	}
	# put the max at nearest delta, but save true max
	set trueymax $ymax
	set ymax [expr ( (int($ymax) / $deltay) + 1) * $deltay]

	# calculate the scale constants to fit the canvas
	set scalx  [expr 1.0*$MX/($xmax-$xmin)]
	set scaly  [expr 1.0*$MY/($ymax-$ymin)]

	#go graphic
	if { [winfo exists .graph] } {
		destroy .graph
	}
	toplevel .graph
	# a frame with border to avoid wm corniche to cover inner canvas
	frame .graph.gf -borderwidth 3
	pack .graph.gf

	set c [canvas .graph.gf.c -height [expr $MY+20] -width $MX]
	pack $c

	set bf [frame .graph.bf]
	pack $bf -side bottom

	set b [button .graph.bf.b -text Ok -command "destroy .graph"]
	pack $b
	set p [button .graph.bf.p -text "$TXT(saveto) PS" \
		-command {SaveCanvas .graph.gf.c {} } ]
	pack $b $p -side left
	
	# normalize list k
	# and put in z a list of point to plot
	set bad "false"
	set z ""
	set count 0;#debug 
	foreach a $k {
	    set count [incr count]; #debug
	    # extraction of x, with check
	    set tmp [lindex $a 0]
	    if { ! [CheckSignedFloat Ignore $tmp] } { continue }
	    set x $tmp
	    # extraction of y, with check
	    # only the first bad elevation wp is used (start bad zone),
	    #  the others are ignored
	    set tmp [lindex $a 1]
	    if { ! [CheckSignedFloat Ignore $tmp] } {
		if { "$bad" == "false" } {
		    set bad "true"
		    if { "$z" == "" } { continue };#nothing to plot
		    if { [llength $z] < 4 } { continue };#not enough data to
		                                         #plot
		    eval $c create line $z
		    set tmpl [llength $z]
		    set primo [expr $tmpl - 2]
		    set tmp [lrange $z [expr $tmpl - 2] [expr $tmpl - 1]]
		    set z [lrange $tmp 0 1]
		}
	    } else {
		set y $tmp
		set x [Hnormalize ($x $xmin $scalx)]
		set y [Hnormalize ($y $ymin $scaly)]
		set y [ expr $MY-$y]
		if { $bad == "true"} {
		    set bad "false"
		    lappend z [expr int($x)] [expr int($y)]
		    if { "$z" == "" } { continue }
		    if { [llength $z] < 4 } { continue }
		    eval $c create line $z -fill red \
			    -stipple $BITMAP(brokenline)
		    set z ""
		}
		lappend z [expr int($x)] [expr int($y)]
	    }
	};#foreach

	# plot quota lines
	for {set y  -1000} {$y < 50000} {incr y [expr $deltay]} {
		if {($y<$ymax)&&($y>$ymin)} {
			set tmp [Hnormalize ($y $ymin $scaly)]
			set tmp [expr $MY-$tmp]
			set tmp [expr int($tmp)]
			$c create line 0 $tmp $MX $tmp -fill "blue" -width 2
			$c create text 50 $tmp -fill "red" -text "$y"
		}
	}
	# for plot data is beautified
	set trueymax [expr round($trueymax * 10)/10.0]
	set ymin [expr round($ymin * 10)/10.0]

	set xexcursion [expr round(($xmax-$xmin)*10)/10.0]
	if {$xexcursion > 10000.0} {
		set deltax [expr int($xexcursion/4.0)]
	} else {
		foreach mx "5000 2000 1000 500 250 100 50 10 0" \
		     deltax "2000 1000  250 100  50  25 10  5 1" {
		     	if {$xexcursion > $mx } { break }
		}     	
	}

	for {set x 0} {$x < $xmax} {incr x [expr $deltax]} {
		if {($x<$xmax)&&($x>$xmin)} {
			set tmp [Hnormalize ($x $xmin $scalx)]
			set tmp [expr int($tmp)]
			$c create line $tmp $MY $tmp [expr $MY - 10] \
				-fill "blue" 
		}
	}
	$c create text [expr $MX - 60] [expr $MY + 15] -fill "black" \
		-text "delta $deltax"
	$c create text [expr $MX - 50] [expr $MY + 5] -fill "black" \
		-text "tot. $xexcursion"
	$c create text 50 [expr $MY + 5] -fill "black" -justify "left" \
		-text "h. max: $trueymax"
	$c create text 50 [expr $MY + 15] -fill "black" -justify "left" \
		-text "h. min: $ymin"

	#to be polite, let's return explicitly
	return
}
#=============================================================================

proc GMTRCompute {window} {
    # create dialog to show results of computation for TR of edit/show window
    global DPOSX DPOSY COLOUR GMTRTPs GMTRDatum TRTPoints TRDatum TXT MESS \
	    DTUNIT SPUNIT DSCALE

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

    if { "$window" == ".gmTR" } {
	set tps $GMTRTPs
	if { "$tps" == "" } {
	    GMMessage $MESS(voidTR)
	    return
	}
	set datum $GMTRDatum
    } else {
	set ixt [IndexNamed TR [$window.fr.fr1.id get]]
	set tps $TRTPoints($ixt) ; set datum $TRDatum($ixt)
    }

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "destroy $w"
    wm title $w "GPS Manager: $TXT(TRcomp)"
    set x [expr $DPOSX+100] ; set y [expr $DPOSY+100]
    wm geometry $w +$x+$y

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ntitle -text "$TXT(name): [$window.fr.fr1.id get]"

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.frtits -relief flat -borderwidth 0
    label $w.fr.fr3.frtits.fill -width 2 -font fixed
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    set h [$window.fr.fr3.frbx.bxn size]
    if { $h > 15 } { set h 15 }
    foreach b "xn xl tl al dt sp bg" m "4 8 8 7 8 6 4" t $TXT(TRcompflds) {
	label $w.fr.fr3.frtits.tit$b -width $m -text $t -font fixed
	listbox $w.fr.fr3.frbx.b$b -height $h -width $m -relief flat \
	    -yscrollcommand "$w.fr.fr3.frbx.bscr set" \
 	    -selectmode single -exportselection false -font fixed
	bind $w.fr.fr3.frbx.b$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] \
		    {bxn bxl btl bal bdt bsp bbg}
	}
    }
    # BSB contribution: wheelmouse scrolling
    set boxes [list $w.fr.fr3.frbx.bxn $w.fr.fr3.frbx.bxl \
	            $w.fr.fr3.frbx.btl $w.fr.fr3.frbx.bal $w.fr.fr3.frbx.bdt \
	            $w.fr.fr3.frbx.bsp $w.fr.fr3.frbx.bbg]
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes

    set i 1
    set td 0 ; set tt 0 ; set mxsp 0 ; set mnsp 999999
    set imx 0 ; set imn 0
    # AP contribution
    #======
    set dbTot 0
    set lll ""
    #======
    SetDatumData $datum
    set tp0 [lindex $tps 0]
    set mxd0 0 ; set ismxd0 ""
    foreach tp $tps nxt [lrange $tps 1 end] {
	$w.fr.fr3.frbx.bxn insert end [format "%2d." $i]
	if { "$nxt" != "" } {
	    set db [ComputeDistBearFD $tp $nxt]
	    set d [expr [lindex $db 0]*$DSCALE]
	    set b [format "%4d" [lindex $db 1]]
	    set dt [expr [lindex $nxt 5]-[lindex $tp 5]]
	    set al [lindex $tp 6]
	    set d0 [expr [ComputeDistFD $tp0 $nxt]*$DSCALE]
	    # AP contribution
	    #=======
	    set dbTot [expr $dbTot + $d]
	    lappend lll [list $dbTot $al]
	    #=======
	    if { $d0 > $mxd0 } {
		set mxd0 $d0 ; set ismxd0 [expr $i+1]
	    } elseif { $d0 == $mxd0 } { lappend ismxd0 [expr $i+1] }
	    if { $dt == 0 } {
		set sp "------"
	    } else {
		set sp [expr 3600*$d/$dt]
		if { $sp > $mxsp } { set mxsp $sp ; set imx $i }
		if { $sp < $mnsp } { set mnsp $sp ; set imn $i }
		set sp [format "%6.2f" $sp]
	    }
	    set td [expr $td+$d] ; incr tt $dt
	    set sd [format "%8.2f" $td]
	    set d [format "%8.2f" $d]
	    set dt [FormatTime $dt]
	} else {
	    set d "========" ; set dt "========"
	    set sp "======" ; set b "===="
	    set sd "========" ; set al "====="
	}
	incr i
	$w.fr.fr3.frbx.bxl insert end $d
	$w.fr.fr3.frbx.bdt insert end $dt
	$w.fr.fr3.frbx.btl insert end $sd
	$w.fr.fr3.frbx.bal insert end $al
	$w.fr.fr3.frbx.bsp insert end $sp
	$w.fr.fr3.frbx.bbg insert end $b
    }
    # AP contribution
    #=======
    #From this poin on you can call:
    #Hgraph $lll
    #=======
     set td [format "%8.2f" $td]
    if { $tt == 0 } {
	set avsp "======"
	set mxp "======" ; set mnsp "======"
    } else {
	set avsp [format "%6.2f" [expr 3600*$td/$tt]]
	set mxsp [format "%6.2f" $mxsp] ; set mnsp [format "%6.2f" $mnsp]
    }
    set tt [FormatTime $tt]
    set d0 [format "%8.2f" $d0] ; set mxd0 [format "%8.2f" $mxd0]

    frame $w.fr.fr3.frt -relief flat -borderwidth 0
    label $w.fr.fr3.frt.td -text "$TXT(totdst): $td $DTUNIT"
    label $w.fr.fr3.frt.tt -text "$TXT(tottime): $tt"

    label $w.fr.fr3.avg -text "$TXT(avgsp): $avsp $SPUNIT"
    frame $w.fr.fr3.frsp -relief flat -borderwidth 0
    label $w.fr.fr3.frsp.max -text "$TXT(maxsp): $mxsp (@$imx)"
    label $w.fr.fr3.frsp.min -text "$TXT(minsp): $mnsp (@$imn)"

    frame $w.fr.fr3.frd0 -relief flat -borderwidth 0
    label $w.fr.fr3.frd0.toend -text [format $TXT(starttoend) $d0]
    label $w.fr.fr3.frd0.max -text "[format $TXT(startmax) $mxd0] (@$ismxd0)"

    frame $w.fr.frsel -relief flat -borderwidth 0
    button $w.fr.frsel.save -text "$TXT(save) ..." \
	    -command "SaveFile comp TRComp $w ; \
	              $w.fr.frsel.save configure -state normal"
    button $w.fr.frsel.ok -text Ok -command "destroy $w"
    # AP contribution
    #-----
    button $w.fr.frsel.hgraph -text $TXT(elevation) \
	    -command "$w.fr.frsel.hgraph configure -state normal ; \
	              Hgraph {$lll}"

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle -side left
    pack $w.fr.fr3.frtits.titxn $w.fr.fr3.frtits.titxl $w.fr.fr3.frtits.tittl \
	    $w.fr.fr3.frtits.tital $w.fr.fr3.frtits.titdt \
	    $w.fr.fr3.frtits.titsp $w.fr.fr3.frtits.titbg \
	    $w.fr.fr3.frtits.fill -side left
    pack $w.fr.fr3.frbx.bxn $w.fr.fr3.frbx.bxl $w.fr.fr3.frbx.btl \
	    $w.fr.fr3.frbx.bal $w.fr.fr3.frbx.bdt \
	    $w.fr.fr3.frbx.bsp $w.fr.fr3.frbx.bbg $w.fr.fr3.frbx.bscr \
	    -side left -fill y
    pack $w.fr.fr3.frt.td $w.fr.fr3.frt.tt -side left -padx 5
    pack $w.fr.fr3.frsp.max $w.fr.fr3.frsp.min -side left -padx 3
    pack $w.fr.fr3.frd0.toend $w.fr.fr3.frd0.max -side left -padx 2
    pack $w.fr.fr3.frtits $w.fr.fr3.frbx -side top -fill y -pady 1
    pack $w.fr.fr3.frt $w.fr.fr3.avg $w.fr.fr3.frsp $w.fr.fr3.frd0 \
	    -side top -fill y -pady 5
    # AP contribution: hgraph button
    pack $w.fr.frsel.hgraph $w.fr.frsel.save $w.fr.frsel.ok -side left -padx 5
    pack $w.fr.fr1 $w.fr.fr3 $w.fr.frsel -side top    
    return
}

proc GMGroup {index options data} {
    # create dialog window for editing/showing data of GR with given index
    #  $index is -1 if this is a new GR
    #  $options is a list of buttons to display;
    #	an empty list means no editing; supported options are:
    #     cancel, create, change, revert, forget
    #     change and forget assume $index != -1
    #     see proc GMButton for further details
    #  if $options is empty, $index cannot be -1 as this is not a new GR
    #   the only button is OK, and only binding: return to destroy
    # order of elements in $data list reflects order in $Storage(GR)
    #  which is used below
    # return window path
    global GMGRIndex GMGRData GMGRDispl GMGRMapChg GMGRShow MapLoading \
	    DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	    OBSWIDTH OBSHEIGHT TXT TYPES

    foreach "name obs conts displ" $data {}
    set ed 0 ; set st disabled
    if { "$options" != "" } {
	if { [winfo exists .gmGR] } { Raise .gmGR ; bell ; return .gmGR }
	set ed 1 ; set st normal
	set w .gmGR
	set GMGRIndex $index ; set GMGRDispl $displ ; set GMGRMapChg 0
	# this depends on Storage(GR)
	set GMGRData $data
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmGR$index
	if { [winfo exists $w] } { destroy $w }
	incr GMGRShow
	set x [expr $DPOSX+50*(1+$GMGRShow % 5)]
	set y [expr $DPOSY+50*(1+$GMGRShow % 5)]
    }

    toplevel $w
    wm title $w "GPS Manager: $TXT(group)"
    wm geometry $w +$x+$y
    if { ! $ed } {
	wm protocol $w WM_DELETE_WINDOW "destroy $w"
	bind $w <Key-Return> "destroy $w"
    } else {
	wm protocol $w WM_DELETE_WINDOW { GMButton GR cancel }
    }

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.ntitle -text "$TXT(name):"
    entry $w.fr.fr1.id -width $COMMENTWIDTH -exportselection 1
    ShowTEdit $w.fr.fr1.id $name $ed

    frame $w.fr.fr2 -relief flat -borderwidth 0
    label $w.fr.fr2.obstit -text "$TXT(rmrk):"
    text $w.fr.fr2.obs -wrap word -width $OBSWIDTH -height $OBSHEIGHT \
	    -exportselection true
    $w.fr.fr2.obs insert 0.0 $obs
    $w.fr.fr2.obs configure -state $st
    TextBindings .gmGR.fr.fr2.obs

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    foreach b "bxn bxw box" m "3 3 $COMMENTWIDTH" {
	listbox $w.fr.fr3.frbx.$b -height 15 -width $m -relief flat \
		-yscrollcommand "$w.fr.fr3.frbx.bscr set" \
		-selectmode single -exportselection false
	bind $w.fr.fr3.frbx.$b <Double-1> {
	    global INVTXT
	    set p [winfo parent %W]
	    set n [$p.box get [%W nearest %y]]
	    set wh $INVTXT([$p.bxw get [%W nearest %y]])
	    if { "$n" != "" } {
		OpenItem $wh [IndexNamed $wh $n]
	    }
	}
	bind $w.fr.fr3.frbx.$b <Button-3> {
	    global INVTXT
	    set p [winfo parent %W]
	    set n [$p.box get [%W nearest %y]]
	    set wh $INVTXT([$p.bxw get [%W nearest %y]])
	    if { "$n" != "" } {
		ToggleDisplayNamed $wh $n
	    }
	}
	bind $w.fr.fr3.frbx.$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] {bxn bxw box}
	}
    }
    # BSB contribution: wheelmouse scrolling
    set boxes [list $w.fr.fr3.frbx.box $w.fr.fr3.frbx.bxw $w.fr.fr3.frbx.bxn]
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes

    set i 1
    foreach p $conts {
	set wh [lindex $p 0]
	foreach e [lindex $p 1] {
	    $w.fr.fr3.frbx.box insert end "$e"
	    $w.fr.fr3.frbx.bxw insert end "$TXT($wh)"
	    $w.fr.fr3.frbx.bxn insert end "$i."
	    incr i
	}
    }

    frame $w.fr.fr3.frbt -relief flat -borderwidth 0
    label $w.fr.fr3.frbt.title -text $TXT(element)
    foreach wh $TYPES {
	button $w.fr.fr3.frbt.ins$wh -relief raised \
		-text "$TXT(insert) $TXT(name$wh)" -state $st \
		-command "GMGRChange ins$wh ; \
		          $w.fr.fr3.frbt.ins$wh configure -state normal"
    }
    button $w.fr.fr3.frbt.del -text $TXT(del) -state $st \
	    -command { GMGRChange del }
    menubutton $w.fr.fr3.frbt.repl -text $TXT(repl) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.repl.m -state $st
    menu $w.fr.fr3.frbt.repl.m -tearoff 0
    frame $w.fr.fr3.frbt.sep -height 6 -bg $COLOUR(dialbg) \
	    -relief flat -borderwidth 0
    menubutton $w.fr.fr3.frbt.join -text $TXT(joinGR) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.join.m -state $st
    menu $w.fr.fr3.frbt.join.m -tearoff 0
    button $w.fr.fr3.frbt.clear -text $TXT(clear) -state $st \
	    -command { GMGRChange clr }
    menubutton $w.fr.fr3.frbt.uwps -text $TXT(usewps) -relief raised \
	    -direction right -menu $w.fr.fr3.frbt.uwps.m
    menu $w.fr.fr3.frbt.uwps.m -tearoff 0
    $w.fr.fr3.frbt.uwps.m add command -label $TXT(mkavgWP) \
	    -command "GMGRtoWP $w"
    $w.fr.fr3.frbt.uwps.m add cascade -label $TXT(chgpfrmt) \
	    -menu $w.fr.fr3.frbt.uwps.m.mpf
    menu $w.fr.fr3.frbt.uwps.m.mpf -tearoff 0
    FillPFormtMenu $w.fr.fr3.frbt.uwps.m.mpf GMGRChangeWPPFormt $w
    $w.fr.fr3.frbt.uwps.m add cascade -label $TXT(chgdatum) \
	    -menu $w.fr.fr3.frbt.uwps.m.mdat
    menu $w.fr.fr3.frbt.uwps.m.mdat -tearoff 0
    FillDatumMenu $w.fr.fr3.frbt.uwps.m.mdat GMGRChangeWPDatum
    $w.fr.fr3.frbt.uwps.m add command -label $TXT(mkclusters) \
	    -command "MakeClusters $w"

    frame $w.fr.frsel -relief flat -borderwidth 0
    if { $ed } {
	bind $w.fr.fr3.frbt.join <Button-1> \
		"FillItemsMenu $w.fr.fr3.frbt.join.m join GR GR"
	bind $w.fr.fr3.frbt.repl <Button-1> {
	    global INVTXT
	    set pp [winfo parent [winfo parent %W]]
	    set sel [$pp.frbx.box curselection]
	    if { "$sel" != "" } {
		FillItemsMenu $pp.frbt.repl.m repl \
			$INVTXT([$pp.frbx.bxw get $sel]) GR
	    }
	}
	checkbutton $w.fr.displayed -text $TXT(displ) -variable GMGRDispl \
		-onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
	if { $MapLoading != 0 } { $w.fr.displayed configure -state disabled }
	set i 0 ; set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		    -command "$b$e configure -state normal ; GMButton GR $e"
	    pack $b$e -side left
	    incr i
	}
    } else {
	checkbutton $w.fr.displayed  -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.displayed select }
	button $w.fr.frsel.bok -text Ok -command "destroy $w"
	pack $w.fr.frsel.bok -side left
    }

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle $w.fr.fr1.id -side left -padx 3
    pack $w.fr.fr2.obstit $w.fr.fr2.obs -side left -padx 3
    set f $w.fr.fr3.frbx
    pack $f.bxn $f.bxw $f.box $f.bscr -side left -fill y
    set f $w.fr.fr3.frbt
    pack $f.title $f.insWP $f.insRT $f.insTR $f.insGR $f.del $f.repl \
	    $f.sep $f.join $f.clear $w.fr.fr3.frbt.uwps -side top -pady 2 \
	    -fill x
    pack $w.fr.fr3.frbx $w.fr.fr3.frbt -side left -padx 5
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.fr3 \
	    $w.fr.displayed $w.fr.frsel -side top -pady 5
    update idletasks
    return $w
}

proc RevertGR {} {
    # reset data in GR edit window to initial values
    # this depends on Storage(GR)
    global GMGRIndex GMGRData GMGRDispl TXT

    .gmGR.fr.fr1.id delete 0 end
    .gmGR.fr.fr1.id insert 0 [lindex $GMGRData 0]
    .gmGR.fr.fr2.obs delete 0.0 end
    .gmGR.fr.fr2.obs insert 0.0 [lindex $GMGRData 1]
    .gmGR.fr.fr3.frbx.box delete 0 end
    .gmGR.fr.fr3.frbx.bxw delete 0 end
    .gmGR.fr.fr3.frbx.bxn delete 0 end
    set i 1
    foreach p [lindex $GMGRData 2] {
	set wh [lindex $p 0]
	foreach e [lindex $p 1] {
	    .gmGR.fr.fr3.frbx.box insert end "$e"
	    .gmGR.fr.fr3.frbx.bxw insert end "$TXT($wh)"
	    .gmGR.fr.fr3.frbx.bxn insert end "$i."
	    incr i
	}
    }
    if { [set GMRTDispl [lindex $GMGRData 3]] } {
	.gmGR.fr.displayed select
    } else {
	.gmGR.fr.displayed deselect
    }
    return
}

proc NotWellFounded {gr1 gr2} {
    global GRConts

    if { "$gr1" == "$gr2" } { return 1 }
    foreach p $GRConts([IndexNamed GR $gr2]) {
	if { "[lindex $p 0]" == "GR" } {
	    foreach g [lindex $p 1] {
		if { [NotWellFounded $gr1 $g] } { return 1 }
	    }
	}
    }
    return 0
}

proc GMGRCheck {} {
    # check validity of data in GR edit window
    # this depends on Storage(GR)
    global GMGRIndex GMGRData GMGRDispl MESS INVTXT TYPES

    set id [.gmGR.fr.fr1.id get]
    if {! [CheckString GMMessage $id] } {
	focus .gmGR.fr.fr1.id
	return nil
    }
    if { [.gmGR.fr.fr3.frbx.box size] == 0 } {
	GMMessage $MESS(voidGR)
	return nil
    }
    foreach wh $TYPES { set es($wh) "" }
    foreach twh [.gmGR.fr.fr3.frbx.bxw get 0 end] \
	    e [.gmGR.fr.fr3.frbx.box get 0 end] {
	lappend es($INVTXT($twh)) $e
    }
    if { $GMGRIndex!=-1 && "$id" == "[lindex $GMGRData 0]" } {
	foreach g $es(GR) {
	    if { [NotWellFounded $id $g] } {
		GMMessage "$MESS(initselfGR) $g"
		return nil
	    }
	}
    }
    set cs ""
    foreach wh $TYPES {
	if { "$es($wh)" != "" } {
	    lappend cs [list $wh $es($wh)]
	}
    }
    return [list $id [CheckNB [.gmGR.fr.fr2.obs get 0.0 end]] $cs $GMGRDispl]
}

proc GRInsert {wh list} {
    # insert elements in group being edited
    #  $wh is type of elements whose names are in $list
    # GR well-foundedness will be checked when creating/changing
    global TXT INVTXT

    set n [.gmGR.fr.fr3.frbx.box size]
    for { set i 0 } { $i < $n } { incr i } {
	if { "$INVTXT([.gmGR.fr.fr3.frbx.bxw get $i])" == "$wh" } {
	    break
	}
    }
    set f $i
    while { $i<$n && "$INVTXT([.gmGR.fr.fr3.frbx.bxw get $i])" == "$wh" } {
	incr i
    }
    set l $i
    foreach name $list {
	set ok 1
	for { set k $f } { $k < $l } { incr k } {
	    if { "[.gmGR.fr.fr3.frbx.box get $k]" == "$name" } {
		bell ; set ok 0
	    }
	}
	if { $ok } {
	    foreach b "bxn box bxw" k "end $i $i" \
		    m [list "[expr $n+1]." $name $TXT($wh)] {
		.gmGR.fr.fr3.frbx.$b insert $k $m
		.gmGR.fr.fr3.frbx.$b selection clear 0 end
	    }
	    incr i ; incr n
	}
    }
}

proc GMGRChange {how args} {
    # perform edit operations on GR
    #  $how is one of
    #      ins$wh  insert item of type $wh (in {WP, RT, TR, GR})
    #      del   delete selected item
    #      repl  replace selected item by another one of same type
    #      join  join (set union) other GR
    #      clr   set to void
    #  $args is the name of the new item for $how in {repl, join}
    # Note that GMGRMapChg is not affected: changes in a mapped GR will not
    #  affect mapping of its old or new elements
    global GRConts Number INVTXT

    set sel [.gmGR.fr.fr3.frbx.box curselection]
    switch -glob $how {
	ins* {
	    regsub ins $how "" wh
	    if { $Number($wh) > 0 } {
		GRInsert $wh [Apply [ChooseItems $wh] NameOf $wh]
	    }
	}
	repl {
	    # GR well-foundedness will be checked when creating/changing
	    set new [lindex $args 0]
	    if { "$sel" == "" } { return }
	    set wh $INVTXT([.gmGR.fr.fr3.frbx.bxw get $sel])
	    foreach e [.gmGR.fr.fr3.frbx.box get 0 end] \
		    ewh [.gmGR.fr.fr3.frbx.bxw get 0 end] {
		if { "$wh" == "$INVTXT($ewh)" && "$new" == "$e" } {
		    bell
		    return
		}
	    }
	    .gmGR.fr.fr3.frbx.box insert $sel $new
	    .gmGR.fr.fr3.frbx.box delete [expr $sel+1]
	    .gmGR.fr.fr3.frbx.box selection set $sel
	}
	del  {
	    if { "$sel" == "" } { return }
	    .gmGR.fr.fr3.frbx.box delete $sel
	    .gmGR.fr.fr3.frbx.bxw delete $sel
	    .gmGR.fr.fr3.frbx.bxn delete end
	    .gmGR.fr.fr3.frbx.bxw selection clear 0 end
	    .gmGR.fr.fr3.frbx.bxn selection clear 0 end
	}
	join {
	    foreach p $GRConts([IndexNamed GR [lindex $args 0]]) {
		GRInsert [lindex $p 0] [lindex $p 1]
	    }
	}
	clr {
	    .gmGR.fr.fr3.frbx.box delete 0 end
	    .gmGR.fr.fr3.frbx.bxw delete 0 end
	    .gmGR.fr.fr3.frbx.bxn delete 0 end
	}
    }
    return
}

proc GMGRCollectWPs {window} {
    # collect all WPs in GR shown in $window, recursively
    # return "void", "error" (a message is issued), or list of indices of WPs
    global TYPES INVTXT GMGRIndex GMGRData GMember MESS

    set id [$window.fr.fr1.id get]
    if { "$window" == ".gmGR" } {
	if { [.gmGR.fr.fr3.frbx.box size] == 0 } { return void }
	foreach wh $TYPES { set es($wh) "" }
	foreach twh [.gmGR.fr.fr3.frbx.bxw get 0 end] \
		e [.gmGR.fr.fr3.frbx.box get 0 end] {
	    lappend es($INVTXT($twh)) $e
	}
	if { $GMGRIndex != -1 && "$id" == "[lindex $GMGRData 0]" } {
	    foreach g $es(GR) {
		if { [NotWellFounded $id $g] } {
		    GMMessage "$MESS(initselfGR) $g"
		    return "error"
		}
	    }
	}
	catch "unset GMember"
	foreach wp $es(WP) {
	    set GMember([IndexNamed WP $wp]) 1
	}
	set grixs ""
	foreach gr $es(GR) {
	    lappend grixs [IndexNamed GR $gr]
	}
	if { "$grixs" != "" } { GRsElsCollect $grixs 1 WP }
	set ixs [array names GMember]
	catch "unset GMember"
    } else {
	set grix [IndexNamed GR $id]
	set ixs [GRsElements $grix 1 WP]
    }
    return $ixs
}

proc GMGRChangeWPPFormt {pformt window} {
    # change the position format of all WPs in GR shown in $window
    global MESS EdWindow WPPosn WPDatum WPPFrmt

    switch "[set ixs [GMGRCollectWPs $window]]" {
	void {
	    GMMessage $MESS(voidGR) ; return
	}
	error {
	    return
	}
    }
    if { "$ixs" == "" } { bell ; return }
    set t [PosType $pformt]
    foreach ix $ixs {
	if { "$WPPFrmt($ix)" != "$pformt" } {
	    set p $WPPosn($ix) ; set la [lindex $p 0] ; set lo [lindex $p 1]
	    set p [CreatePos $la $lo $pformt $t $WPDatum($ix)]
	    if { "[lindex $p 0]" != "--" } {
		set WPPosn($ix) $p ; set WPPFrmt($ix) $pformt
		UpdateItemWindows WP $ix
	    }
	}
    }
    return
}

proc GMGRChangeWPDatum {datum menu} {
    # change the datum of all WPs in GR shown in $window
    # fails silently for each WP whose position format is a grid requiring
    #  a different datum
    global MESS EdWindow WPPosn WPDatum WPPFrmt

    regexp {^(\.[^.]+)\.} $menu match window
    switch "[set ixs [GMGRCollectWPs $window]]" {
	void {
	    GMMessage $MESS(voidGR) ; return
	}
	error {
	    return
	}
    }
    if { "$ixs" == "" } { bell ; return }
    foreach ix $ixs {
	if { "[set od $WPDatum($ix)]" != "$datum" } {
	    set pfmt $WPPFrmt($ix) ; set  pt [PosType $pfmt]
	    if { ("$pt" == "grid" || "$pt" == "nzgrid") && \
		    "[set gdatum [GridDatum $pfmt]]" != "" && \
		    "$datum" != "$gdatum" } {
		continue
	    }
	    set p $WPPosn($ix)
	    set p [ConvertDatum [lindex $p 0] [lindex $p 1] $od $datum $pfmt]
	    set WPPosn($ix) $p ; set WPDatum($ix) $datum
	    UpdateItemWindows WP $ix
	}
    }
    return
}

proc GMButton {wh button} {
    # callback to button when editing item of type $wh (in $TYPES)
    #  $button in {cancel, create, change, revert, forget}
    global Storage EdWindow WPRoute RTIdNumber RTWPoints \
	    GRConts GM${wh}Index GM${wh}Displ GM${wh}Data GM${wh}MapChg \
	    MESS TXT MapMakingRT

    set w $EdWindow($wh)
    set ids [lindex $Storage($wh) 0]
    global $ids
    switch $button {
	cancel {
	    destroy $w
	    if { $MapMakingRT && "$wh" == "RT" } {
		MapCancelRT dontask dontclose
	    }
	}
	create {
	    set data [GM${wh}Check]
	    if { "$data" != "nil" } {
		if { ! [CheckArrayElement $ids [lindex $data 0]] } {
		    # new name
		    set ix [CreateItem $wh $data]
		    if { "$wh" == "RT" } {
			SetWPRoute [lindex $data 0] [lindex $data 3]
			if { $MapMakingRT } { MapCancelRT dontask dontclose }
		    }
		    if { [set GM${wh}Displ] } { PutMap $wh $ix }
		    destroy $w
		} else {
		    GMMessage $MESS(idinuse)
		    focus $w.fr.fr1.id
		    return
		}
	    }
	}
	change {
	                           # $GM??Index assumed > -1
	    set data [GM${wh}Check]
	    if { "$data" != "nil" } {
		set ix [set GM${wh}Index]
		set oldname [set [set ids]($ix)]
		set newname [lindex $data 0]
		set diffname [string compare $oldname $newname]
		set mapped [lindex [set GM${wh}Data] end]
		set tomap [set GM${wh}Displ]
		if { $diffname } {
		    if { [CheckArrayElement $ids $newname] } {
			GMMessage $MESS(idinuse)
			focus $w.fr.fr1.id
			return
		    }
		}
		if { $mapped } {
		    if { $tomap } {
			SetItem $wh $ix $data
			if { [set GM${wh}MapChg] } {
			    MoveOnMap $wh $ix $oldname $diffname $newname
			}
		    } else {
			if { [UnMap $wh $ix] } {
			    SetItem $wh $ix $data
			} else {
			    GMMessage [format $MESS(cantunmap) $TXT(name$wh)]
			    return
			}
		    }
		} else {
		    SetItem $wh $ix $data
		    if { $tomap } { PutMap $wh $ix }
		}
		if { $diffname } {
		    ListDelete $wh $ix ; ListAdd $wh $ix
		    if { "$wh" == "WP" } {
			foreach rt "$WPRoute($ix)" {
			    set ixrt [IndexNamed RT $rt]
			    set i [lsearch -exact $RTWPoints($ixrt) $oldname]
			    set RTWPoints($ixrt) \
				    [lreplace $RTWPoints($ixrt) $i $i $newname]
			}
		    }
		    foreach grix [array names GRConts] {
			set i 0
			foreach p $GRConts($grix) {
			    if { "[lindex $p 0]" == "$wh" } {
				set es [lindex $p 1]
				set j [lsearch -exact $es $oldname]
				if { $j != -1 } {
				    set GRConts($grix) [lreplace $p $i $i \
					    [list $wh [lreplace $es $j $j \
					               $newname]]]
				}
				break
			    }
			}
			incr i
		    }
		}
		if { "$wh" == "WP" } {
		    if { $diffname || $GMWPMapChg } {
			ChangeWPInRTWindows $oldname $newname
		    }
		} elseif { "$wh" == "RT" } {
		    SetWPRoute $newname "[lindex $data 3]"
		}
		destroy $w
	    }
	}
	revert {
	    if { [GMConfirm $MESS(askrevert)] } {
		Revert$wh
	    }
	}
	forget {
	                           # $GM??Index assumed > -1
	    if { [GMConfirm [format $MESS(askforget) $TXT(name$wh)]] && \
		 [Forget $wh [set GM${wh}Index]] } { destroy $w }
	}
    }
    return
}

# Positions

proc FillPos {w pformt posn state chgvar} {
    # create widgets to display position
    #  $w is path to parent window
    #  $pformt is the position format (see proc PosType, compute.tcl)
    #  $posn is the position (see proc CreatePos, compute.tcl)
    #  $state in {normal, disabled}
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types or pastes in any entry
    global TXT

    pack $w.pfmt -side left -padx 3
    set ep [lrange $posn 2 end]
    switch [PosType $pformt] {
	latlong {
	    set ns [list $TXT(lat) $TXT(long)]
	    FillEntries $w "lat long" $ns "12 12" $ep $state $chgvar
	}
	utm {
	    set ns [list $TXT(ze) $TXT(zn) $TXT(eastng) $TXT(nrthng)]
	    FillEntries $w "ze zn eng nng" $ns "3 3 8 8" $ep $state $chgvar
	}
	grid {
	    set ns [list $TXT(zone) $TXT(eastng) $TXT(nrthng)]
	    FillEntries $w "zn eng nng" $ns "5 8 8" $ep $state $chgvar
	}
	nzgrid {
	    set ns [list $TXT(eastng) $TXT(nrthng)]
	    FillEntries $w "eng nng" $ns "8 8" $ep $state $chgvar
	}
	mh {
	    FillEntries $w mh "" 8 $ep $state $chgvar
	}
    }
    return
}

proc PosnGetCheck {w datum errproc chgvar} {
    # get and check position in edit/show window
    #  $w is path to parent window
    #  $errproc is procedure to call on error
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types in any entry and that contains the
    #     current position otherwise
    # returns "nil" on error or if there are empty fields
    global MESS

    if { "[set r [PosnGetCheckEmpty $w $datum $errproc $chgvar]]" == \
	    "empty" } {
	$errproc $MESS(emptypos)
	return nil
    }
    return $r
}

proc PosnGetCheckEmpty {w datum errproc chgvar} {
    # get and check position in edit/show window
    #  $w is path to parent window
    #  $errproc is procedure to call on error
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types in any entry and that contains
    #     the current position otherwise (possibly "")
    # returns "empty" if there are empty fields, and "nil" on error
    global MESS TXT INVTXT

    if { "$chgvar" != "nil" } {
	global $chgvar

	if { "[set p [set $chgvar]]" == "" } { return empty }
	if { $p != 1 } { return $p }
    }
    set pf $INVTXT([$w.pfmt cget -text])
    switch [set ptype [PosType $pf]] {
	latlong {
	    set wlat [$w.lat get] ; set wlong [$w.long get]
	    if { "$wlat" == "" && "$wlong" == "" } { return empty }
	    if { [CheckLat $errproc $wlat $pf] && \
		    [CheckLong $errproc $wlong $pf] } {
		set latdeg [Coord $pf $wlat S]
		set longdeg [Coord $pf $wlong W]
		set p [list $latdeg $longdeg $wlat $wlong]
	    } else { return nil }
	}
	utm {
	    foreach m "ze zn eng nng" c "ZE ZN Number Number" {
		set $m [$w.$m get]
		if { "[set $m]" == "" } {
		    return empty
		} elseif { ! [Check$c $errproc [set $m]] } { return nil }
	    }
	    if { $eng != 0 } { set eng [string trimleft $eng "0"] }
	    if { $nng != 0 } { set nng [string trimleft $nng "0"] }
	    set pd [UTMToDegrees $ze $zn $eng $nng $datum]
	    set p [list [lindex $pd 0] [lindex $pd 1] $ze $zn $eng $nng]
	}
	grid -
	nzgrid {
	    foreach m "eng nng" {
		set $m [$w.$m get]
		if { "[set $m]" == "" } {
		    return empty
		} elseif { ! [CheckNumber $errproc [set $m]] } { return nil }
	    }
	    if { $eng != 0 } { set eng [string trimleft $eng "0"] }
	    if { $nng != 0 } { set nng [string trimleft $nng "0"] }
	    if { "$ptype" == "grid" } {
		set gr 1
		set zn [$w.zn get]
		if { ! [CheckZone $errproc $zn $pf] } {
		    if { "$zn" == "" } { return empty }
		    return nil
		}
	    } else { set gr 0 ; set zn "" }
	    set pd [GridToDegrees $pf $zn $eng $nng $datum]
	    if { $pd == 0 } {
		$errproc $MESS(outofgrid)
		return nil
	    } elseif { $pd == -1 } {
		$errproc [format $MESS(badgriddatum) $TXT($pf) [GridDatum $pf]]
		return nil
	    }
	    if { $gr } {
		set p [list [lindex $pd 0] [lindex $pd 1] $zn $eng $nng]
	    } else { set p [list [lindex $pd 0] [lindex $pd 1] $eng $nng] }
	}
	mh {
	    set mh [string trim "[$w.mh get]" " "]
	    if { "$mh" == "" } { return empty }
	    if { ! [CheckMHLocator $errproc $mh] } { return nil }
	    set p [linsert [MHLocToDegrees $mh] end $mh]
	}
    }
    if { "$chgvar" != "nil" } {
	global $chgvar

	set $chgvar $p
    }
    return $p
}

proc RevertPos {w pformt ptype posn} {
    # change position values keeping its format
    #  $w is path to parent window
    #  $pformt is position format (see proc PosType, compute.tcl)
    #  $ptype is type of position format (see proc PosType, compute.tcl)
    #  $posn is the position (see proc CreatePos, compute.tcl)
    global TXT

    $w.pfmt configure -text $TXT($pformt)
    switch $ptype {
	latlong {
	    set bs {lat long} ; set is {2 3}
	}
	utm {
	    set bs {ze zn eng nng} ; set is {2 3 4 5}
	}
	grid {
	    set bs {zn eng nng} ; set is {2 3 4}
	}
	nzgrid {
	    set bs {eng nng} ; set is {2 3}
	}
	mh {
	    set bs mh ; set is 2
	}
    }
    foreach b $bs k $is {
	$w.$b delete 0 end
	$w.$b insert 0 [lindex $posn $k]
    }
    return
}

proc RedrawPos {w oldtype pformt posn chgvar} {
    # display position under a new format
    #  $w is path to parent window
    #  $oldtype is type of old format (see proc PosType, compute.tcl)
    #  $pformt is the new format (see proc PosType, compute.tcl)
    #  $posn is the position (see proc CreatePos, compute.tcl)
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types in any entry and that contains the
    #     current position otherwise (possibly "")
    global TXT

    switch $oldtype {
	latlong { DestroyAll $w "lattitle lat longtitle long" }
	utm { DestroyAll $w "zetitle ze zntitle zn engtitle eng nngtitle nng" }
	grid { DestroyAll $w "zntitle zn engtitle eng nngtitle nng" }
	nzgrid { DestroyAll $w "zn engtitle eng nngtitle nng" }
	mh { DestroyAll $w "mhtitle mh" }
    }
    $w.pfmt configure -text $TXT($pformt)
    FillPos $w $pformt $posn normal $chgvar
    return
}

proc ChangePFormt {pformt dvar w chgvar} {
    # change format of position in window
    #  $w is path to parent window
    #  $pformt is position format (see proc PosType, compute.tcl)
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types in any entry and that contains the
    #     current position otherwise (possibly "")
    global INVTXT $dvar MESS

    set opf $INVTXT([$w.pfmt cget -text])
    if { "$opf" == "$pformt" } { return }
    set ot [PosType $opf] ; set nt [PosType $pformt]
    set op [PosnGetCheckEmpty $w [set $dvar] GMMessage $chgvar]
    if { "$op" == "nil" } { return }
    if { "$nt" == "grid" || "$nt" == "nzgrid" } {
	set gr 1
    } else { set gr 0 }
    if { "$op" == "empty" } {
	set np ""
	if { $gr && "[set gdatum [GridDatum $pformt]]" != "" && \
		"[set $dvar]" != "$gdatum" } {
	    set $dvar $gdatum
	}
    } else {
	set datum [set $dvar]
	if { $gr && "[set gdatum [GridDatum $pformt]]" != "" && \
		"$datum" != "$gdatum" } {
	    set op [ConvertDatum [lindex $op 0] [lindex $op 1] \
		                 $datum $gdatum DDD]
	    set datum [set $dvar $gdatum]
	}
	set np [CreatePos [lindex $op 0] [lindex $op 1] $pformt $nt $datum]
	if { $gr && "[lindex $np 2]" == "--" } {
	    GMMessage $MESS(outofgrid)
	    return
	}
    }
    if { "$chgvar" != "nil" } {
	global $chgvar

	set $chgvar $np
    }
    if { "$ot" == "$nt" } {
	RevertPos $w $pformt $nt $np
    } else {
	RedrawPos $w $ot $pformt $np $chgvar
    }
    return
}

proc GMWPChangeDatum {datum args} {
    # change datum of WP being edited
    #  $args is not used but is needed as this is called-back from a menu
    global GMWPDatum INVTXT MESS ChangedPosn

    if { "$GMWPDatum" == "$datum" } { return }
    set op [PosnGetCheck .gmWP.fr.frp $GMWPDatum Ignore ChangedPosn]
    set pformt $INVTXT([.gmWP.fr.frp.pfmt cget -text])
    if { ("[set pt [PosType $pformt]]" == "grid" || "$pt" == "nzgrid") && \
	    "[set gdatum [GridDatum $pformt]]" != "" && \
	    "$datum" != "$gdatum" } {
	GMMessage [format $MESS(cantchggriddatum) $gdatum]
	return
    }
    if { "$op" != "nil" } {
	set np [ConvertDatum [lindex $op 0] [lindex $op 1] \
		$GMWPDatum $datum $pformt]
	set ChangedPosn $np
	RevertPos .gmWP.fr.frp $pformt $pt "$np"
    }
    set GMWPDatum $datum
    return
}

 # track utilities

proc GMTRChangeDatum {datum args} {
    # change datum of TR being edited
    #  $args is not used but is needed as this is called-back from a menu
    global GMTRDatum GMTRTPs

    if { "$GMTRDatum" == "$datum" } { return }
    set GMTRTPs [ChangeTPsDatum $GMTRTPs $GMTRDatum $datum]
    foreach n "n d lat long" {
	.gmTR.fr.fr3.frbx.bx$n delete 0 end
    }
    FillTPs .gmTR $GMTRTPs
    set GMTRDatum $datum
    return
}

proc FillTPs {w tps} {
    # insert TPs in $tps in listboxes in TR edit/show window $w

    set i 1
    foreach tp $tps {
	$w.fr.fr3.frbx.bxn insert end "$i."
	$w.fr.fr3.frbx.bxd insert end [lindex $tp 4]
	$w.fr.fr3.frbx.bxlat insert end [lindex $tp 2]
	$w.fr.fr3.frbx.bxlong insert end [lindex $tp 3]
	$w.fr.fr3.frbx.bxalt insert end [lindex $tp 6]
	$w.fr.fr3.frbx.bxdep insert end [lindex $tp 7]
	incr i
    }
    return
}

proc MarkPoint {wh w ix} {
    # use a position at index $ix in listbox of window $w to create
    #  a new WP
    #  $wh in {TR, PVT}
    #    ==TR: use TP with given index in TR edit/show window
    #    ==PVT: use point in real-time track log window (Garmin)
    global GMTRTPs TRTPoints CREATIONDATE DEFAULTSYMBOL DEFAULTDISPOPT \
	    PVTPosns

    switch $wh {
	TR {
	    set dat [$w.fr.frd.datum cget -text]
	    if { "$w" == ".gmTR" } {
		set p [lindex $GMTRTPs $ix]
	    } else {
		set ixt [IndexNamed TR [$w.fr.fr1.id get]]
		set p [lindex $TRTPoints($ixt) $ix]
	    }
	    set alt [lindex $p 6]
	}
	PVT {
	    if { "[set p [lindex $PVTPosns $ix]]" == "" } {
		bell ; return
	    }
	    set dat "WGS 84"
	    set alt [$w.fri.frtbx.bxalt get $ix]
	}
    }
    set opts "create revert cancel"
    if { $CREATIONDATE } {
	GMWPoint -1 $opts [FormData WP "PFrmt Posn Datum Alt Date" \
		            [list DMS $p $dat $alt [Now]]]
    } else {
	GMWPoint -1 $opts [FormData WP "Commt PFrmt Posn Datum Alt" \
		            [list [DateCommt [Now]] DMS $p $dat $alt]]
    }
    return
}

### varia

proc ShowPosnDatum {w pformt posn dproc dvar st ed chgvar} {
    # show position and datum under parent window $w
    #  $pformt is position format
    #  $posn is position representation
    #  $dproc is proc to be called when changing datum (if editing is allowed)
    #  $dvar is either name of global variable with either datum if editing,
    #   or datum if not
    #  $st is state of editable widgets
    #  $ed is 1 if edition is allowed
    #  $chgvar is either "nil" or name of global variable to set to
    #     1 if the user types in any entry and that contains the
    #     current position otherwise (see procs ChangePFormt and
    #     PosnGetCheckEmpty)
    # frames $w.frp and $w.frd are created that should be packed by the caller
    # widgets created here are used elsewhere
    global TXT

    frame $w.frp -relief flat -borderwidth 0
    # path to this menubutton used elsewhere
    menubutton $w.frp.pfmt -text $TXT($pformt) -relief raised -width 8 \
	    -direction below -menu $w.frp.pfmt.m -state $st
    menu $w.frp.pfmt.m -tearoff 0
    if { $ed } {
	FillPFormtMenu $w.frp.pfmt.m ChangePFormt $dvar $w.frp $chgvar
    }
    FillPos $w.frp $pformt $posn $st $chgvar

    frame $w.frd -relief flat -borderwidth 0
    menubutton $w.frd.dttitle -text Datum -relief raised \
	    -direction below -menu $w.frd.dttitle.m -state $st
    menu $w.frd.dttitle.m -tearoff 0
    if { $ed } {
	global $dvar

	FillDatumMenu $w.frd.dttitle.m $dproc
	label $w.frd.datum -text [set $dvar] -textvariable $dvar \
		-width [Measure "Astro B4 Sorol Atoll"]
    } else {
	label $w.frd.datum -text $dvar \
		-width [Measure "Astro B4 Sorol Atoll"]
    }
    pack $w.frd.dttitle $w.frd.datum -side left -padx 3
    return
}

proc AskForName {name} {
    # ask for replacement of WP $name which is not valid
    #  but check first that there is not a replacement in list $ReplacedNames
    # on success add pair $name and new name to ReplacedNames
    # callers of this proc should initialize ReplacedNames to empty before
    #  first call
    # names already in use are not accepted!
    # create modal dialog for displaying message
    #  buttons: OK, Cancel
    #  binding: return to accept
    global MYGPS RECNAMECHARS NAMELENGTH Temp COLOUR EPOSX EPOSY MESS TXT \
	    ReplacedNames

    foreach p $ReplacedNames {
	if { "$name" == "[lindex $p 0]" } { return [lindex $p 1] }
    }
    toplevel .askname
    wm protocol .askname WM_DELETE_WINDOW "set Temp cnc ; set wait2 0"
    wm title .askname "GPS Manager: $TXT(change)"
    wm transient .askname .
    wm geometry .askname +$EPOSX+$EPOSY
    bind .askname <Key-Return> "set Temp ok ; set wait2 0"

    frame .askname.fr -relief flat -borderwidth 5 -bg $COLOUR(confbg)
    label .askname.fr.title -text "!!!" -relief sunken
    message .askname.fr.text -aspect 1000 \
	    -text [format $MESS(replname) $name $NAMELENGTH \
	             $RECNAMECHARS($MYGPS,mess)]
    entry .askname.fr.name -width $NAMELENGTH
    TextBindings .askname.fr.name
    frame .askname.fr.bs -relief flat -borderwidth 0
    button .askname.fr.bs.ok -text Ok -command "set Temp ok ; set wait2 0"
    button .askname.fr.bs.cancel -text $TXT(cancel) \
	    -comman "set Temp cnc ; set wait2 0"
    pack .askname.fr.bs.ok .askname.fr.bs.cancel -side left -pady 5
    pack .askname.fr.title .askname.fr.text .askname.fr.name .askname.fr.bs \
	    -side top -pady 5
    pack .askname.fr -side top
    update idletasks
    grab .askname
    RaiseWindow .askname
    while 1 {
	tkwait variable wait2
	switch "$Temp" {
	    ""  { }
	    ok {
		set nn [string trim [.askname.fr.name get]]
		if { [CheckName Ignore $nn] } {
		    if { [IndexNamed WP $nn] != -1 } {
			GMMessage $MESS(idinuse) ; continue
		    }
		    lappend ReplacedNames [list $name $nn]
		    destroy .askname
		    update idletasks
		    return $nn
		}
		bell
	    }
	    cnc {
		destroy .askname
		update idletasks
		return ""
	    }
	}
    }
}

## opening and updating edit/show windows

proc OpenItem {wh index} {
    # edit or display item with given index; $wh in $TYPES
    global EdWindow Proc GM${wh}Index MESS TXT

    if { $index == -1 } { GMMessage $MESS(notlisted) ; return "" }
    set w $EdWindow($wh)
    if { [winfo exists $w] } {
	if { [set GM${wh}Index] == $index } {
	    Raise $w ; bell
	    return ""
	} else { set w [$Proc($wh) $index "" [ItemData $wh $index]] }
    } else {
	set w [$Proc($wh) $index "change revert create forget cancel" \
		[ItemData $wh $index]]
    }
    return $w
}

proc UpdateItemWindows {wh ix} {
    # redraw edit/show windows for item of type $wh and index $ix
    global EdWindow Proc GM${wh}Index

    set w $EdWindow($wh)
    if { [winfo exists $w] && [set GM${wh}Index] == $ix } {
	destroy $w
	$Proc($wh) $ix "change revert create forget cancel" [ItemData $wh $ix]
    }
    if { [winfo exists .gm${wh}sh$ix] } {
	$Proc($wh) $ix "" [ItemData $wh $ix]
    }
    return
}

proc SetDisplShowWindow {wh ix sel} {
    # set display check-button in show window according to $sel
    #  for item of type $wh and index $ix
    #  $sel in {select, deselect}

    if { [winfo exists .gm${wh}sh$ix] } {
	set w .gm${wh}sh$ix
	$w.fr.displayed configure -state normal
	$w.fr.displayed $sel
	$w.fr.displayed configure -state disabled
    }
    return
}
