#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
# Copyright (c) 1998-2009 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:  22 October 2009
#
# Includes contributions by
#  - Brian Baulch (baulchb _AT_ onthenet.com.au) marked "BSB contribution"
#  - Alessandro Palmas (alpalmas _AT_ tin.it) marked "AP contribution"
#  - Stefan Heinen (stefan.heinen _AT_ djh-freeweb.de) marked "SH contribution"
#  - Martin Buck (m _AT_ rtin-buck.de) marked "MB contribution"
#  - Jean H Theoret (ve2za _AT_ rac.ca) marked "JHT contribution"
#  - Benoit Steiner (benetsteph _AT_ free.fr) marked "BS 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 GMEd MapLoading COLOUR DPOSX DPOSY NAMEWIDTH COMMENTWIDTH \
	    CREATIONDATE OBSWIDTH OBSHEIGHT TXT ICONWIDTH ICONHEIGHT \
	    MAXMENUITEMS DISPOPTS ChangedPosn UNIX ALUNIT

    foreach "name num commt obs pformt posn datum date symbol dispopt \
	    alt mbak hidden displ" $data {}
    if { $options != "" } {
	if { [winfo exists .gmWP] } { Raise .gmWP ; bell ; return .gmWP }
	set ed 1 ; set st normal
	set w .gmWP
	set GMEd(WP,Index) $index ; set GMEd(WP,Num) $num
	set GMEd(WP,Datum) $datum ; set GMEd(WP,Displ) $displ
	set GMEd(WP,Symbol) $symbol ; set GMEd(WP,DispOpt) $dispopt
	set GMEd(WP,Alt) $alt ; set GMEd(WP,MBack) $mbak
	set GMEd(WP,Hidden) $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 GMEd(WP,Data) $data
	set GMEd(WP,MapChg) 0
	set x $DPOSX ; set y $DPOSY
    } else {
	set ed 0 ; set st disabled
	set w .gmWPsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(WP,Show)
	set x [expr $DPOSX+45*(1+$GMEd(WP,Show) % 5)]
	set y [expr $DPOSY+45*(1+$GMEd(WP,Show) % 5)]
    }

    GMToplevel $w waypoint +$x+$y {} {} {}
    if { ! $UNIX } {
	# SH contribution
	focus $w
    }
    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 [list $posn] GMWPChangeDatum GMEd \
	    GMEd(WP,Datum) $st $ed ChangedPosn
    } else {
	ShowPosnDatum $w.fr $pformt [list $posn] "" "" $datum $st 0 nil
    }

    frame $w.fr.fr11 -relief flat -borderwidth 0
    label $w.fr.fr11.atit -text "$TXT(alt) ($ALUNIT):"
    entry $w.fr.fr11.alt -width 7 -exportselection 1
    set valt [UserAltitude $alt]
    ShowTEdit $w.fr.fr11.alt $valt $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
    if { $hidden != "" } {
  	button $w.fr.fr2.hidd -text $TXT(hiddendata) \
  		-command "$w.fr.fr2.hidd configure -state normal ; \
 		          ShowHiddenData WP {$hidden}"
    }

    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 [expr $ICONHEIGHT+2]
    foreach "syim sytxt" [SymbolImageName $symbol] {}
    $w.fr.fr4.symbim create image 1 5 -anchor nw -image $syim
    label $w.fr.fr4.symbname -text $sytxt
    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.fr5
    CreateMBackWidgets WP $w.fr.fr5 $mbak $ed

    # frame used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    frame $w.fr.fr6
    button $w.fr.fr6.route -text $TXT(nameRT) \
	    -command "ShowRTForWP $index"
    menubutton $w.fr.fr6.comp -text $TXT(comp) -relief raised \
	    -direction below -menu $w.fr.fr6.comp.m
    menu $w.fr.fr6.comp.m -tearoff 0
    menu $w.fr.fr6.comp.m.mothers -tearoff 0
    $w.fr.fr6.comp.m add command -label $TXT(distazim) \
	    -command "ChItemsCall WP single GMCompDistBearWP $w $ed"
    $w.fr.fr6.comp.m add command -label $TXT(nearestWPs) \
	    -command "GMWPNearest $w $ed"
    button $w.fr.fr6.newat -text $TXT(newWPatdb) \
	    -command "CreateWPAtDistBear $index"

    frame $w.fr.frsel -relief flat -borderwidth 0 -background $COLOUR(selbg)
    frame $w.fr.frdw
    if { $ed } {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-variable GMEd(WP,Displ) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	if { $MapLoading != 0 } {
	    $w.fr.frdw.displayed configure -state disabled
	}
	set c -1
	set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		    -command "$b$e configure -state normal ; GMButton WP $e"
	    grid $b$e -row 0 -column [incr c] -padx 3
	}
    } else {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-selectcolor $COLOUR(check) -state disabled
	if { $displ } { $w.fr.frdw.displayed select }
	button $w.fr.frsel.b -text $TXT(ok) -command "destroy $w"
	pack $w.fr.frsel.b
    }

    pack $w.fr -side top
    grid $w.fr.fr1.ntitle -row 0 -column 0 -sticky nesw
    grid $w.fr.fr1.id -row 0 -column 1 -sticky nesw
    if { $CREATIONDATE } {
	grid $w.fr.fr1.dtitle -row 1 -column 0 -sticky nesw
	grid $w.fr.fr1.date -row 1 -column 1 -sticky nesw
    }
    grid $w.fr.fr11.atit -row 0 -column 0 -sticky nesw
    grid $w.fr.fr11.alt -row 0 -column 1 -sticky nesw
    set r 0
    if { $hidden != "" } {
	grid $w.fr.fr2.hidd -row 0 -column 0 -sticky nesw
	incr r
    }
    grid $w.fr.fr2.ctitle -row $r -column 0 -sticky nesw
    grid $w.fr.fr2.commt -row $r -column 1 -sticky nesw

    grid $w.fr.fr3.obstit -row 0 -column 0 -sticky nesw
    grid $w.fr.fr3.obs -row 0 -column 1 -sticky nesw

    set c -1
    foreach x "symb symbim symbname dispopt dispo" {
	grid $w.fr.fr4.$x -row 0 -column [incr c] -sticky nesw -padx 3
    }

    set c -1
    foreach x "route comp newat" {
	grid $w.fr.fr6.$x -row 0 -column [incr c] -sticky nesw -padx 10
    }
    pack $w.fr.frdw.displayed

    set r -1
    set d 2
    foreach x "fr1 frp frd fr11 fr2 fr3 fr4 fr5 fr6 frdw frsel" \
	    y "$d $d 0 0 $d 0 $d $d $d $d $d" {
	if { $y == 0 } {
	    grid $w.fr.$x -row [incr r] -column 0 -sticky nesw
	} else {
	    grid $w.fr.$x -row [incr r] -column 0 -sticky nesw -pady $y
	}
    }

    AttachPlugIns $w

    update idletasks
    return $w
}

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

    set data $GMEd(WP,Data)
    .gmWP.fr.fr1.id delete 0 end
    .gmWP.fr.fr1.id insert 0 [lindex $data 0]
    .gmWP.fr.fr2.commt delete 0 end
    .gmWP.fr.fr2.commt insert 0 [lindex $data 2]
    .gmWP.fr.fr3.obs delete 1.0 end
    .gmWP.fr.fr3.obs insert 1.0 [lindex $data 3]
    set pft $POSTYPE($INVTXT([.gmWP.fr.frp.pfmt cget -text]))
    set opf [lindex $data 4] ; set t $POSTYPE($opf)
    set p [lindex $data 5]
    if { $pft == $t } {
	RevertPos .gmWP.fr.frp.frp1 $opf $t $p
    } else {
	RedrawPos .gmWP.fr.frp.frp1 $opf $p ChangedPosn normal
    }
    set ChangedPosn $p
    set GMEd(WP,Datum) [lindex $data 6]
    .gmWP.fr.fr1.date delete 0 end
    .gmWP.fr.fr1.date insert 0 [lindex $data 7]
    ChangeWPSymbol [lindex $data 8]
    ChangeWPDispOpt [lindex $data 9]
    .gmWP.fr.fr11.alt delete 0 end
    .gmWP.fr.fr11.alt insert 0 [UserAltitude [lindex $data 10]]
    set GMEd(WP,MBack) [lindex $data 11]
    # hidden: lindex $data 12
    set GMEd(WP,Displ) [lindex $data end]
    if { $GMEd(WP,Displ) } {
	.gmWP.fr.frdw.displayed select
    } else {
	.gmWP.fr.frdw.displayed deselect
    }
    return
}

proc GMWPCheck {} {
    # check validity of data in WP edit window
    # this depends on Storage(WP)
    global GMEd INVTXT KEEPHIDDEN MESS
    # BSB contribution: WPNum

    set r [CheckEntries GMMessage nil "{.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.frp1 $GMEd(WP,Datum) GMMessage \
	       ChangedPosn]
    if { $p == "nil" } { return nil }
    set valt [string trim [.gmWP.fr.fr11.alt get]]
    if { [set alt [AltitudeList $valt]] == "nil" } {
	GMMessage $MESS(badalt)
	return nil
    }
    if { $GMEd(WP,Hidden) != "" } {
	switch $KEEPHIDDEN {
	    never { set GMEd(WP,Hidden) "" }
	    always { }
	    ask {
		if { [GMConfirm $MESS(nohidden)] } { set GMEd(WP,Hidden) "" }
	    }
	}
    }
    lappend r $GMEd(WP,Symbol) $GMEd(WP,DispOpt) $alt $GMEd(WP,MBack) \
	$GMEd(WP,Hidden) $GMEd(WP,Displ)
    set nb [CheckNB [.gmWP.fr.fr3.obs get 0.0 end]]
    set GMEd(WP,MapChg) 1
    set r [linsert $r 2 $nb $INVTXT([.gmWP.fr.frp.pfmt cget -text]) \
	                 $p $GMEd(WP,Datum)]
    # BSB contribution
    return [linsert $r 1 $GMEd(WP,Num)]
}

proc ShowRTForWP {ix} {
    # let user select and open RT having WP of given index
    global WPRoute TXT LISTWIDTH

    if { $ix == -1 } { return }
    set rtname [GMChooseFrom single [list $TXT(select) $TXT(nameRT)] \
		    $LISTWIDTH $WPRoute($ix) $WPRoute($ix)]
    if { $rtname == "" } { return }
    if { [set ix [IndexNamed RT $rtname]] != -1 } {
	OpenItem RT $ix
    } else { bell }
    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

    ChangeDatum $datum GMEd GMEd(WP,Datum) ChangedPosn .gmWP.fr.frp normal
    return
}

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

    set GMEd(WP,Symbol) $symbol
    foreach "syim sytxt" [SymbolImageName $symbol] {}
    set w .gmWP
    $w.fr.fr4.symbim delete all
    $w.fr.fr4.symbim create image 1 5 -anchor nw -image $syim
    $w.fr.fr4.symbname configure -text $sytxt
    return
}

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

    set GMEd(WP,DispOpt) $opt
    .gmWP.fr.fr4.dispo configure -text $TXT(DISP$opt)
    return
}

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

    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.frp1 $GMEd(WP,Datum) GMMessage \
		ChangedPosn]
	if { $p1 == "nil" } { return }
	set d1 $GMEd(WP,Datum)
	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]]

    GMToplevel $w distazim +[expr $DPOSX+100]+[expr $DPOSY+100] {} \
        [list WM_DELETE_WINDOW "destroy $w"] {}

    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 $FixedFont \
	-anchor w
    label $w.fr.fr1.bear -text "$bear $TXT(degrees)" -width 15 \
	-font $FixedFont -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 $TXT(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 GMEd WPName WPPosn WPDatum DPOSX DPOSY COLOUR MESS TXT LsW \
	    DSCALE FixedFont

    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.frp1 $GMEd(WP,Datum) GMMessage \
		ChangedPosn]
	if { $p1 == "nil" } { return }
	set d1 $GMEd(WP,Datum) ; set ix1 -1
	if { $wp1 == "" } { set wp1 "(???)" }
    } else {
	set ix1 [IndexNamed WP $wp1]
	set p1 $WPPosn($ix1) ; set d1 $WPDatum($ix1)
    }

    GMToplevel $w distazim +[expr $DPOSX+100]+[expr $DPOSY+100] {} \
        [list WM_DELETE_WINDOW "destroy $w"] {}
  
    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 $FixedFont
    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 $FixedFont
	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 $FixedFont
	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 

    set wpixs [array names WPName]
    if { [lindex $wpixs 100] != "" } {
	set slow 1
	set slowid [SlowOpWindow $TXT(comp)]
    } else {
	set slow 0
	SetCursor . watch
    }
    SetDatumData $d1
    set i 0
    foreach ix2 $wpixs {
	if { $wp1 != $WPName($ix2) } {
	    if { $slow && [SlowOpAborted] } {
		destroy $w
		SlowOpFinish $slowid ""
		return
	    }
	    set p2 $WPPosn($ix2) ; set d2 $WPDatum($ix2)
	    if  { $d1 != $d2 } {
		set p2 [ToDatum [lindex $p2 0] [lindex $p2 1] $d2 $d1]
	    }
	    set db [ComputeDistBearFD $p1 $p2]
	    set d [expr [lindex $db 0]*$DSCALE]
	    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
	}
    }
    if { $slow } {
	SlowOpFinish $slowid ""
    } else { ResetCursor . }

    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 $TXT(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
	    
    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 GMEd DPOSX DPOSY DPOSRTMAP COLOUR LISTHEIGHT NAMEWIDTH \
	    COMMENTWIDTH OBSWIDTH OBSHEIGHT TXT DTUNIT MapMakingRT MapLoading \
	    Map MapWidth UNIX FixedFont

    foreach "number commt obs wps stages width colour mbak displ" $data {}
    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 GMEd(RT,Index) $index ; set GMEd(RT,Width) $width
	set GMEd(RT,Colour) $colour ; set GMEd(RT,MBack) $mbak
	set GMEd(RT,WPoints) $wps ; set GMEd(RT,Displ) $displ
	set GMEd(RT,MapChg) 0
	# this depends on Storage(RT)
	set GMEd(RT,Data) $data
    } else {
	set ed 0 ; set st disabled ; set stmm disabled
	set w .gmRTsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(RT,Show)
	set x [expr $DPOSX+50*(1+$GMEd(RT,Show) % 5)]
	set y [expr $DPOSY+50*(1+$GMEd(RT,Show) % 5)]
    }

    GMToplevel $w route +$x+$y {} {} {}
    if { ! $UNIX } {
	# SH contribution
	if {$MapMakingRT} {lower $w $Map} {focus $w}
    }
    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(numberid):"
    entry $w.fr.fr1.id -width 10 -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 $FixedFont
    frame $frb.frbx -relief flat -borderwidth 0
    set boxes ""
    foreach b "xn ox xd xb xda xsc xsl" \
	    m "4 $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 $FixedFont
	listbox $frb.frbx.b$b -height 15 -width $m -relief flat \
	    -yscrollcommand "$frb.frbx.bscr set" \
 	    -selectmode extended -exportselection false -font $FixedFont
	bind $frb.frbx.b$b <<ListboxSelect>> \
	    "MultExtSelect $frb.frbx.b$b {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
	    }
	}
    }
    # $boxes defined in the foreach loop that creates them
    if { $ed } { set GMEd(RT,boxes) $boxes }
    # BSB contribution: wheelmouse scrolling
    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 "%3d." $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)
    foreach a "insb insa repl" {
	button $w.fr.fr3.frbt.$a -text $TXT($a) -state $stmm \
	    -command "ChItemsCall WP single GMRTChange $a"
    }
    button $w.fr.fr3.frbt.del -text $TXT(del) -state $stmm \
	    -command { GMRTChange del }
    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 }
    foreach a "incb inca" {
	$w.fr.fr3.frbt.chg.m add command -label $TXT($a) \
		-command "ChItemsCall RT single GMRTChange $a"
    }
    $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"
    menubutton $w.fr.fr3.frbt.cnv -text $TXT(convert) -relief raised \
	-direction right -menu $w.fr.fr3.frbt.cnv.m
    menu $w.fr.fr3.frbt.cnv.m -tearoff 0
    $w.fr.fr3.frbt.cnv.m add command -label $TXT(mkTR) -command "RTToTR $w"
    $w.fr.fr3.frbt.cnv.m add command -label $TXT(split) \
	-command "SplitPolyLine RT sel $w $ed $index"
    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
    # frame used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    frame $w.fr.frdw
    set mn $w.fr.frdw.mw.m
    menubutton $w.fr.frdw.mw -text $TXT(width) -relief raised \
	    -direction below -menu $mn -state $st
    menu $mn -tearoff 0
    button $w.fr.frdw.b -text $TXT(Colour) -relief raised \
	    -command "ChooseColour GMEd GMEd(RT,Colour) $w.fr.frdw $w" \
	    -state $st
    label $w.fr.frdw.bc -relief groove -background $colour -width 2

    frame $w.fr.frmb
    CreateMBackWidgets RT $w.fr.frmb $mbak $ed

    if { $ed } {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-variable GMEd(RT,Displ) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	foreach i "1 2 3 4 5 6 7 8" {
	    $mn add command -label $i -command "set GMEd(RT,Width) $i"
	}
	label $w.fr.frdw.wv -width 3 -textvariable GMEd(RT,Width)
	if { $MapLoading != 0 } {
	    foreach i "displayed mw" {
		$w.fr.frdw.$i 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 RT $e"
	    pack $b$e -side left
	}
    } else {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.frdw.displayed select }
	label $w.fr.frdw.wv -width 3 -text $width
	button $w.fr.frsel.b -text $TXT(ok) -command "destroy $w"
	pack $w.fr.frsel.b
    }
    # AP contribution ; changed by MF
    set mn $w.fr.fr3.frbt.hgraph.m
    menubutton $w.fr.fr3.frbt.hgraph -text $TXT(elevation) -relief raised \
	    -menu $mn
    menu $mn -tearoff 0
    $mn add command -label $TXT(sideview) -command "GMRTHgraph $w"
    $mn add command -label $TXT(persptv) -command "RTHG3D $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
    eval pack $boxes $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.cnv $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.frdw.displayed $w.fr.frdw.mw -side left -padx 3
    pack $w.fr.frdw.wv -side left -padx 0
    pack $w.fr.frdw.b -side left -padx 10
    pack $w.fr.frdw.bc -side left -padx 0
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.fr21 $w.fr.fr3 $w.fr.frdw -side top -pady 5
    pack $w.fr.frmb -side top
    pack $w.fr.frsel -side top -pady 5

    AttachPlugIns $w

    update idletasks
    return $w
}

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

    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
    }
    foreach i "displayed mw b" {
	.gmRT.fr.frdw.$i configure -state disabled
    }
    if { ! $UNIX } {
	# SH contribution
	lower .gmRT $Map
    }
    return
}

proc GMRouteMapEditEnd {} {
    # change RT edit window when the RT stops being edited on the map
    # assume the RT has changed
    global RT

    set GMEd(RT,MapChg) 1
    foreach b "insb insa repl chg edmap" {
	.gmRT.fr.fr3.frbt.$b configure -state normal
    }
    foreach i "displayed mw b" {
	.gmRT.fr.frdw.$i configure -state normal
    }
    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 GMEd MapMakingRT

    if { $MapMakingRT } { MapCancelRT dontask dontclose }
     set GMEd(RT,MapChg) 0 ; set data $GMEd(RT,Data)
    .gmRT.fr.fr1.id delete 0 end
    .gmRT.fr.fr1.id insert 0 [lindex $data 0]
    .gmRT.fr.fr2.commt delete 0 end
    .gmRT.fr.fr2.commt insert 0 [lindex $data 1]
    .gmRT.fr.fr21.obs delete 1.0 end
    .gmRT.fr.fr21.obs insert 1.0 [lindex $data 2]
    set frb .gmRT.fr.fr3.fr31
    foreach box $GMEd(RT,boxes) { $box delete 0 end }
    set wps $GMEd(RT,WPoints) ; set stages [lindex $data 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 "%3d." $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

    set GMEd(RT,Width) [lindex $data 5]
    set GMEd(RT,Colour) [lindex $data 6]
    .gmRT.fr.frdw.bc configure -background $GMEd(RT,Colour)
    set GMEd(RT,MBack) [lindex $data 7]
    if { [set d [lindex $data end]] && ! $GMEd(RT,Displ) } {
	PutMapRT $GMEd(RT,Index)
    }
    if { [set GMEd(RT,Displ) $d] } {
	.gmRT.fr.frdw.displayed select
    } else {
	.gmRT.fr.frdw.displayed deselect
    }
    return
}

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

    set r [CheckEntries GMMessage nil [list {.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 { [set id [.gmRT.fr.fr1.id get]] == "" } {
	GMMessage $MESS(namevoid)
	return nil
    }
    if { $id == 0 && ! [GMConfirm $MESS(activeRT)] } {
	return nil
    }
    if { $r != "nil" } {
	set r [linsert $r 0 $id]
	set nb [CheckNB [.gmRT.fr.fr21.obs get 0.0 end]]
	set stages [GMRTStages .gmRT.fr.fr3.fr31.frbx]
	if { ! $GMEd(RT,MapChg) && \
		( $stages != [lindex $GMEd(RT,Data) 4] || \
		  $GMEd(RT,Width) != [lindex $GMEd(RT,Data) 5] || \
		  $GMEd(RT,Colour) != [lindex $GMEd(RT,Data) 6] ) } {
	    set GMEd(RT,MapChg) 1
	}
	lappend r [.gmRT.fr.fr3.fr31.frbx.box get 0 end] $stages \
	    $GMEd(RT,Width) $GMEd(RT,Colour) $GMEd(RT,MBack) $GMEd(RT,Displ)
	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 GMEd

    if { $i == [$sfr.bxn size] } { return }
    set w .gmRS
    if { [winfo exists $w] } { Raise $w ; bell ; return }
    set GMEd(RT,RSgrabs) [set gs [grab current]]
    GMToplevel $w stage +$DPOSX+$DPOSY .gmRT \
        [list WM_DELETE_WINDOW  [list DestroyRGrabs $w $gs]] {}

    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 $TXT(ok) -command "GMRTStageFinish $sfr $i"
    button $w.fr.bs.cancel -text $TXT(cancel) \
	    -command [list DestroyRGrabs $w $gs]

    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)
    global GMEd

    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
    }
    DestroyRGrabs .gmRS $GMEd(RT,RSgrabs)
    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 first selected WP in list or at the beginning
    #      insa  insert WP after last selected WP in list or at the end
    #      repl  replace the first selected WP by another one
    #      del   delete all selected WPs
    #      inv   invert RT
    #      chh   chop head: delete all WPs from first to first selected
    #              inclusive or just the first one if there is no selection
    #      cht   chop tail: delete all WPs from last selected to end or only
    #              last one
    #      incb  include RT before first selected or at beginning
    #      inca  include RT after last 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 GMEd MapMakingRT MapRTLast RTWPoints RTStages

    set GMEd(RT,MapChg) 1
    set frbx .gmRT.fr.fr3.fr31.frbx
    # more than one WP can now be selected
    set sel [lsort -integer -increasing [$frbx.box curselection]]
    set sel0 [lindex $sel 0] ; set sell [lindex $sel end]
    switch $how {
	insb {
	    set owp [lindex $args 0]
	    if { $sel0 != "" } {
		set nxt [expr $sel0+1]
	    } else { set nxt [set sel0 0] }
	    set tddef 1
	    if { $sel0 != 0 } {
		set p [expr $sel0-1]
		set wpp [$frbx.box get $p]
		set d [GMRTDistBearDAltWP $frbx $p 1 $wpp $owp]
		if { $d == "---" } {
		    set tddef 0
		}
	    }
	    set d [GMRTDistBearDAltWP $frbx $sel0 0 $owp [$frbx.box get $sel0]]
	    if { $d == "---" } {
		set tddef 0
	    }
	    $frbx.box insert $sel0 $owp
	    $frbx.bxn insert end [format %3d. [$frbx.box size]]
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    $frbx.bxsc insert $sel0 "" ; $frbx.bxsl insert $sel0 ""
 	    if { $nxt } {
		$frbx.bxn selection clear $sel0 end
		foreach ix $sel {
		    if { $ix >= $sel0 } {
			incr ix
			$frbx.bxn selection set $ix
		    }
 		}
 	    }
	}
	insa {
	    set owp [lindex $args 0]
	    set tddef 1
	    if { $sel == "" || \
		    [set last [expr [$frbx.bxn size]-1]] == $sell } {
		set sell [set nxt end]
		set wpnxt ""
	    } else {
		set nxt [expr $sell+1]
		set wpnxt [$frbx.box get $nxt]
	    }
	    if { [set cwp [$frbx.box get $sell]] == "" } {
		set d "---" ; set tddef 0
	    } else {
		set d [GMRTDistBearDAltWP $frbx $sell 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 %3d. [$frbx.box size]]
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	    $frbx.bxsc insert $nxt "" ; $frbx.bxsl insert $nxt ""
	    if { $sell == "end" } { set sell [expr [$frbx.box size]-2] }
	    foreach b {bxd bxb bxda} {
		$frbx.$b selection clear $nxt
		if { $sell >= 0 } { $frbx.$b selection set $sell }
	    }
	    if { $nxt != "end" } {
		$frbx.bxn selection clear $nxt end
		foreach ix $sel {
		    if { $ix > $nxt } {
			incr ix -1
			$frbx.bxn selection set $ix
		    }
 		}
	    }
	}
	repl {
	    set owp [lindex $args 0]
	    if { $sel0 == "" } { return }
	    ReplaceWPInRTWindow $sel0 $frbx .gmRT.fr.fr3.fr31.frt $owp 1
	}
	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
	    foreach sl [lsort -integer -decreasing $sel] {
		if { $sl > 0 } {
		    set p [expr $sl-1]
		    set prev [$frbx.box get $p]
		    if { $sl == $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 $sl+1]]]
			if { $d == "---" } { set tddef 0 }
		    }
		}
		foreach b "ox xd xb xda xsc xsl" {
		    $frbx.b$b delete $sl
		    $frbx.b$b selection clear 0 end
		}
		$frbx.bxn delete end
		if { $MapMakingRT } {
		    if { $sel == 0 } {
			MapDelRT1st $delwp
		    } else { MapDelRTPrevious $prev $delwp }
		}
	    }
	    $frbx.bxn selection clear 0 end
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt $tddef
	}
	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 first selected, or first one
	    if { $sel == "" } { set sel0 0 }
	    foreach b "ox xd xb xda xsc xsl" {
		$frbx.b$b delete 0 $sel0
	    }
	    $frbx.bxn delete [$frbx.box size] end
	    $frbx.bxn selection clear 0 end
	    foreach sl $sel {
		if { $sl > $sel0 } {
		    set sl [expr $sl-$sel0-1]
		    $frbx.bxn selection set $sl
		}
	    }
	    GMRTConfigTDist $frbx .gmRT.fr.fr3.fr31.frt 1
	}
	cht {
	    # chop tail: delete all WPs from last selected to end, or last one
	    if { $sel == "" } { set sell end }
	    foreach b "xn ox xd xb xda xsc xsl" {
		$frbx.b$b delete $sell 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 first selected or first
	    if { [set ixrt [IndexNamed RT [lindex $args 0]]] == -1 } {
		return
	    }
	    if { $sel == "" } { set sel0 0 }
	    set wps $RTWPoints($ixrt)
	    set tddef 1
	    if { $sel0 != 0 } {
		set pr [expr $sel0-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 $sel0]
	    set i [$frbx.bxn size]
	    foreach wp $wps nxt $nwps stg $RTStages($ixrt) {
		incr i
		$frbx.bxn insert end [format "%3d." $i]
		$frbx.box insert $sel0 $wp
		set d [GMRTDistBearDAltWP $frbx $sel0 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 sel0
	    }
	    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 last selected or at end
	    if { [set ixrt [IndexNamed RT [lindex $args 0]]] == -1 } {
		return
	    }
	    set i [$frbx.bxn size]
	    if { $sel == "" } { set sell [expr $i-1] }
	    set wps $RTWPoints($ixrt)
	    set wp [$frbx.box get $sell]
	    set d [GMRTDistBearDAltWP $frbx $sell 1 $wp [lindex $wps 0]]
	    if { $d == "---" } {
		set tddef 0
	    } else { set tddef 1 }
	    set nwps [lrange $wps 1 end]
	    if { $sell < $i-1 } {
		lappend nwps [$frbx.box get [expr $sell+1]]
	    }
	    foreach wp $wps nxt $nwps stg $RTStages($ixrt) {
		incr i ; incr sell
		$frbx.bxn insert end [format "%3d." $i]
		$frbx.box insert $sell $wp
		set d [GMRTDistBearDAltWP $frbx $sell 0 $wp $nxt]
		if { $d == "---" } { set tddef 0 }
		# $stg may be ""
		$frbx.bxsc insert $sell [lindex $stg 0]
		$frbx.bxsl insert $sell [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 recomp} {
    # replace WP at position $i in boxes framed by $frbx by WP named $wpn
    #  $frt is the frame where the total distance is shown
    #  $recomp is set if distances must be recalculated, in which case
    #     edited WP is assumed to have already been stored in the data-base
    # selection state is kept

    set selected [$frbx.box selection includes $i]
    $frbx.box insert $i $wpn
    $frbx.box delete [expr $i+1]
    if { $recomp } {
	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 { $selected } {
	foreach b "xn ox xd xb xda xsc xsl" {
	    $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 ALSCALE 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 [lindex $WPAlt($ix1) 0]] != "" && \
		[set a2 [lindex $WPAlt($ix2) 0]] != "" } {
	    set da [format "%7.1f" [expr ($a2-$a1)/$ALSCALE]]
	}
    } 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 { [string first "---" $d] == 0 } {
		$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 recomp} {
    # replace name of WP in RT windows and/or recompute distances
    # a WP name may occur several times
    #  $recomp is set if distances must be recalculated, in which case
    #     edited WP is assumed to have already been stored in the data-base
    # change data on RT being edited if WP belongs to it and was renamed
    global GMEd

    set diffname [string compare $oldname $newname]
    if { [winfo exists .gmRT] && $diffname } {
	# window update is done below
	# this depends on Storage(RT)
	set wps [lindex $GMEd(RT,Data) 3] ; set chg 0
	foreach i [lsearch -exact -all $wps $oldname] {
	    set wps [lreplace $wps $i $i $newname]
	    incr chg
	}
	if { $chg } { set GMEd(RT,Data) [lreplace $GMEd(RT,Data) 3 3 $wps] }
    }
    set ws [winfo children .]
    while { [set i [lsearch -glob $ws ".gmRT*"]] != -1 } {
	set w [lindex $ws $i]
	set ws [lrange $ws [expr $i+1] end]
	set frbx $w.fr.fr3.fr31.frbx
	foreach i [lsearch -exact -all [$frbx.box get 0 end] $oldname] {
	    ReplaceWPInRTWindow $i $frbx $w.fr.fr3.fr31.frt $newname $recomp
	}
    }
    return
}

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

    set tpfs "latd longd latDMS longDMS"
    set tpfsa "alt latd longd latDMS longDMS"
    set tps ""
    foreach wpn [$w.fr.fr3.fr31.frbx.box get 0 end] {
	if { [set wpix [IndexNamed WP $wpn]] != -1 } {
	    set p $WPPosn($wpix)
	    set p [lindex [FormatPosition [lindex $p 0] [lindex $p 1] \
			       $WPDatum($wpix) DMS $Datum] 0]
	    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 }
    if { [winfo exists $EdWindow(TR)] } {
	set name [NewName TR]
	set data [FormData TR "Name Datum TPoints" [list $name $Datum $tps]]
	CreateItem TR $data
	GMMessage [format $MESS(convres) $TXT(TR) $name]
    } else {
	set opts "create revert cancel"
	GMTrack -1 $opts [FormData TR "Datum TPoints" [list $Datum $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 GMEd MapLoading DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	    DATEWIDTH OBSWIDTH OBSHEIGHT TXT DATUMWIDTH

    foreach "name obs datum tps segsts hidden width colour mbak 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 GMEd(TR,Index) $index ; set GMEd(TR,Displ) $displ
	set GMEd(TR,Datum) $datum ; set GMEd(TR,TPs) $tps
	set GMEd(TR,Hidden) $hidden ; set GMEd(TR,MapChg) 0
	set GMEd(TR,SgSts) $segsts ; set GMEd(TR,Width) $width
	set GMEd(TR,Colour) $colour ; set GMEd(TR,MBack) $mbak
	# this depends on Storage(TR)
	set GMEd(TR,Data) $data
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmTRsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(TR,Show)
	set x [expr $DPOSX+50*((1+$GMEd(TR,Show)) % 5)]
	set y [expr $DPOSY+50*((1+$GMEd(TR,Show)) % 5)]
    }

    GMToplevel $w track +$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 $GMEd(TR,Datum) \
		-textvariable GMEd(TR,Datum) \
		-width $DATUMWIDTH
    } else {
	label $w.fr.frd.datum -text $datum -width $DATUMWIDTH
    }

    if { $hidden != "" } {
  	button $w.fr.hidd -text $TXT(hiddendata) \
  		-command "$w.fr.hidd configure -state normal ; \
 		          ShowHiddenData TR {$hidden}"
    }

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    set boxes ""
    foreach b "n d lat long alt dep seg" m "5 $DATEWIDTH 12 12 7 7 1" {
	listbox $w.fr.fr3.frbx.bx$b -height 15 -width $m -relief flat \
	    -yscrollcommand "$w.fr.fr3.frbx.bscr set" \
 	    -selectmode extended -exportselection false
	lappend boxes $w.fr.fr3.frbx.bx$b 
	bind $w.fr.fr3.frbx.bx$b <<ListboxSelect>> \
   "MultExtSelect $w.fr.fr3.frbx.bx$b {bxn bxd bxlat bxlong bxalt bxdep bxseg}"
	bind $w.fr.fr3.frbx.bx$b <Double-1> \
	    { MarkPoint TR [winfo toplevel %W] [%W nearest %y] }
    }
    if { $ed } {
	set GMEd(TR,boxes) $boxes
	bind $w.fr.fr3.frbx.bxseg <Button-3> {
	    GMToggleSegStart TR %W [%W nearest %y]
	}
    }
    # BSB contribution: wheelmouse scrolling
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes
    FillTPs $w $tps $segsts

    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 }
    foreach a "incb app" {
	button $w.fr.fr3.frbt.$a -text $TXT($a) -state $st \
	    -command "ChItemsCall TR single GMTRChange $a"
    }
    # MB contribution
    button $w.fr.fr3.frbt.del -text $TXT(del) -state $st \
	    -command { GMTRChange del }
    #----
    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"
    set mnc $w.fr.fr3.frbt.cnv.m
    menubutton $w.fr.fr3.frbt.cnv -text $TXT(convert) -relief raised \
	    -direction right -menu $mnc
    menu $mnc -tearoff 0
    $mnc add command -label $TXT(mkavgWP) -command "GMTRtoWP $w"
    set mn $mnc.mnsm
    $mnc add cascade -label $TXT(simplTRto) -menu $mn
    menu $mn -tearoff 0
    foreach wh "RT TR LN" {
	$mn add command -label $TXT(name$wh) -command "GMTRtoLine $wh $w"
    }
    $mnc add cascade -label $TXT(split) -menu $mnc.mns
    menu $mnc.mns -tearoff 0
    $mnc.mns add command -label $TXT(bysel) \
	-command "SplitPolyLine TR sel $w $ed $index"
    $mnc.mns add command -label $TXT(byseg) \
	-command "SplitPolyLine TR segm $w $ed $index"

    frame $w.fr.frsel -relief flat -borderwidth 0
    # frame used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    frame $w.fr.frdw
    set mn $w.fr.frdw.mw.m
    menubutton $w.fr.frdw.mw -text $TXT(width) -relief raised \
	    -direction below -menu $mn -state $st
    menu $mn -tearoff 0
    button $w.fr.frdw.b -text $TXT(Colour) -relief raised \
	    -command "ChooseColour GMEd GMEd(TR,Colour) $w.fr.frdw $w" \
	    -state $st
    label $w.fr.frdw.bc -relief groove -background $colour -width 2

    frame $w.fr.frmb
    CreateMBackWidgets TR $w.fr.frmb $mbak $ed

    if { $ed } {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-variable GMEd(TR,Displ) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	foreach i "1 2 3 4 5 6 7 8" {
	    $mn add command -label $i -command "set GMEd(TR,Width) $i"
	}
	label $w.fr.frdw.wv -width 3 -textvariable GMEd(TR,Width)
	if { $MapLoading != 0 } {
	    foreach i "displayed mw" {
		$w.fr.frdw.$i 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 TR $e"
	    pack $b$e -side left
	}
    } else {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.frdw.displayed select }
	label $w.fr.frdw.wv -width 3 -text $width
	button $w.fr.frsel.b -text $TXT(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
    eval pack $boxes $w.fr.fr3.frbx.bscr -side left -fill y
    # includes MB contribution
    pack $w.fr.fr3.frbt.chh $w.fr.fr3.frbt.cht $w.fr.fr3.frbt.incb \
	    $w.fr.fr3.frbt.app $w.fr.fr3.frbt.del $w.fr.fr3.frbt.clear \
	    $w.fr.fr3.frbt.sep $w.fr.fr3.frbt.anim $w.fr.fr3.frbt.comp \
	    $w.fr.fr3.frbt.cnv -side top -pady 2 -fill x
    pack $w.fr.fr3.frbx $w.fr.fr3.frbt -side left -padx 5
    pack $w.fr.frdw.displayed $w.fr.frdw.mw -side left -padx 3
    pack $w.fr.frdw.wv -side left -padx 0
    pack $w.fr.frdw.b -side left -padx 10
    pack $w.fr.frdw.bc -side left -padx 0
    if { $hidden != "" } {
	pack $w.fr.fr1 $w.fr.fr2 $w.fr.frd -side top -pady 5
	pack $w.fr.hidd -side top -pady 2
	pack $w.fr.fr3 $w.fr.frdw -side top -pady 5
	pack $w.fr.frmb -side top
	pack $w.fr.frsel -side top -pady 5
    } else {
	pack $w.fr.fr1 $w.fr.fr2 $w.fr.frd $w.fr.fr3 $w.fr.frdw -side top \
	    -pady 5
	pack $w.fr.frmb -side top
	pack $w.fr.frsel -side top -pady 5
    }

    AttachPlugIns $w

    update idletasks
    return $w
}

proc RevertTR {} {
    # reset data in TR edit window to initial values
    # this depends on Storage(TR)
    global GMEd

    set GMEd(TR,MapChg) 0 ; set data $GMEd(TR,Data)
    .gmTR.fr.fr1.id delete 0 end
    .gmTR.fr.fr1.id insert 0 [lindex $data 0]
    .gmTR.fr.fr2.obs delete 1.0 end
    .gmTR.fr.fr2.obs insert 1.0 [lindex $data 1]
    foreach box $GMEd(TR,boxes) { $box delete 0 end }
    foreach e "Datum TPs SgSts" v [lrange $data 2 4] {
	set GMEd(TR,$e) $v
    }
    # hidden attributes: [lindex $GMEd(TR,Data) 5]
    foreach e "Width Colour MBack Displ" v [lrange $data 6 end] {
	set GMEd(TR,$e) $v
    }
    FillTPs .gmTR $GMEd(TR,TPs) $GMEd(TR,SgSts)
    .gmTR.fr.frdw.bc configure -background $GMEd(TR,Colour)
    if { $GMEd(TR,Displ) } {
	.gmTR.fr.frdw.displayed select
    } else {
	.gmTR.fr.frdw.displayed deselect
    }
    return
}

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

    set id [.gmTR.fr.fr1.id get]
    if { ! [CheckString GMMessage $id] } {
	focus .gmTR.fr.fr1.id
	return nil
    }
    if { [llength $GMEd(TR,TPs)] == 0 } {
	GMMessage $MESS(voidTR)
	return nil
    }
    if { $GMEd(TR,Hidden) != "" } {
	switch $KEEPHIDDEN {
	    never { set GMEd(TR,Hidden) "" }
	    always { }
	    ask {
		if { [GMConfirm $MESS(nohidden)] } { set GMEd(TR,Hidden) "" }
	    }
	}
    }
    if { ! $GMEd(TR,MapChg) && \
	    ( $GMEd(TR,Width) != [lindex $GMEd(TR,Data) 6] || \
	      $GMEd(TR,Colour) != [lindex $GMEd(TR,Data) 7] ) } {
	set GMEd(TR,MapChg) 1
    }
    set r [list $id [CheckNB [.gmTR.fr.fr2.obs get 0.0 end]]]
    foreach e "Datum TPs SgSts Hidden Width Colour MBack Displ" {
	lappend r $GMEd(TR,$e)
    }
    return $r
}

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 GMEd TXT FixedFont

    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]]

    GMToplevel .gmTRnd date +$EPOSX+$EPOSY .gmTR \
        {WM_DELETE_WINDOW {set TmpTR cnc}} \
        {<Key-Return> {set TmpTR ok}}

    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 $FixedFont
    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 $FixedFont
    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 $GMEd(TR,Datum)]
	    set dst [expr $dst+$d]
	    .gmTRnd.fr.frbxp.bxl insert end [format "%8.2f" $d]
	}
    }
    set d [ComputeDist [lindex $tpsa end] [lindex $tpsb 0] $GMEd(TR,Datum)]
    .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 $FixedFont
    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 $FixedFont
    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 $GMEd(TR,Datum)]
	    .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 $TXT(ok) -command { set TempTR ok }
    button .gmTRnd.fr.bs.cnc -text $TXT(cancel) -command { set TempTR cnc }

    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
    set gs [grab current]
    grab .gmTRnd
    RaiseWindow .gmTRnd
    while 1 {
	tkwait variable TempTR

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

proc GMTRChange {how args} {
    # perform edit operations on TR

    GMPolyChange TR $how $args
    return
}

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

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

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

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

    if { $window == ".gmTR" } {
	set tps $GMEd(TR,TPs)
	if { $tps == "" } {
	    GMMessage $MESS(voidTR)
	    return
	}
	set segsts $GMEd(TR,SgSts)
	set datum $GMEd(TR,Datum)
    } else {
	set ixt [IndexNamed TR [$window.fr.fr1.id get]]
	set tps $TRTPoints($ixt) ; set segsts $TRSegStarts($ixt)
	set datum $TRDatum($ixt)
    }
    if { [lindex $tps 1000] != ""  && ! [GMConfirm $MESS(timeconsmg)] } {
	return
    }
    SetCursor . watch
    GMToplevel $w TRcomp +[expr $DPOSX+100]+[expr $DPOSY+100] {} {} {}

    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 $FixedFont
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    set h [$window.fr.fr3.frbx.bxn size]
    if { $h > 15 } { set h 15 }
    set boxes ""
    foreach b "xn xd xlat xlong al xl tl dt sp bg" \
	    m "5 $DATEWIDTH 12 12 7 8 8 8 6 4" t $TXT(TRcompflds) {
	label $w.fr.fr3.frtits.tit$b -width $m -text $t -font $FixedFont
	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 $FixedFont
	lappend boxes $w.fr.fr3.frbx.b$b
	bind $w.fr.fr3.frbx.b$b <Button-1> {
	    MultSelect [winfo parent %W] [%W nearest %y] \
		    {bxn bxd bxlat bxlong bal bxl btl bdt bsp bbg}
	}
    }
    # $boxes computed in the previous loop
    # BSB contribution: wheelmouse scrolling
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes

    # distance from first TP, its maximum and index of corresponding TP
    set d0 0 ; set mxd0 0 ; set ismxd0 ""
    # total distance/time including segment gaps
    set td 0 ; set tt 0
    # total distance without segment gaps
    set tdng 0 ; set ttng 0
    # extreme speed values and indices of corresponding TPs
    set mxsp 0 ; set mnsp 1e70 ; set imx 0 ; set imn 0
    # lists of: total distance and speed; seconds, altitude and start
    #  segment flag; seconds and speed
    set speeds "" ; set salts "" ; set sspeeds ""
    SetDatumData $datum
    set tp0 [lindex $tps 0]
    # AP contribution
    #======
    set dbTot 0
    #get start point alt and put it in lllist with 0 km.
    # MF change: inserted start segment flag
    set lll [list [list 0.0 [UserAltitude [lindex $tp0 6]] 0]]
    # MF change: moved computation of $tmp3D $tmp3D2 to next foreach
    set tmp3D "" ; set tmp3D2 ""
    # 3D elevation graph: call   HG3D $tmp3D $tmp3D2 $datum
    #======
    set secs2 [lindex $tp0 5]
    set nsgst [lindex $segsts 0] ; set segsts [lreplace $segsts 0 0]
    set endsegm 0
    set i 1
    # BS contribution: definition variable
    set cumul 0
    set cumul_temp 0
    #======
    # MF contribution: also for cumulative descent
    set cumuld 0 ; set cumuld_temp 0
    #======
    set maxalt -1e10 ; set minalt 1e10
    foreach tp $tps nxt [lreplace $tps 0 0] {
	# $tp altitude in user units and time-stamp in seconds
	if { [set al [UserAltitude [lindex $tp 6]]] != "" } {
	    if { $al > $maxalt } { set maxalt $al }
	    if { $al < $minalt } { set minalt $al }
	}
	set secs $secs2
	# $tp starts a new segment if the previous TP ended one
	set newsegm $endsegm
	lappend salts [list $secs $al $newsegm]
	# AP contribution (MF: from above)
	lappend tmp3D [list [lindex $tp 0] [lindex $tp 1] $datum]
	lappend tmp3D2 [list $al]
	#---
	if { $nsgst == $i } {
	    # $nxt starts a new segment, so $tp ends one
	    set endsegm 1
	    set nsgst [lindex $segsts 0] ; set segsts [lreplace $segsts 0 0]
	} else { set endsegm 0 }
	if { $nxt != "" } {
	    set db [ComputeDistBearFD $tp $nxt]
	    # distance to next in user units
	    set d [expr [lindex $db 0]*$DSCALE]
	    # bearing to next in degrees
	    set b [format "%4d" [lindex $db 1]]
	    # time difference to next in seconds
	    set dt [expr [set secs2 [lindex $nxt 5]]-$secs]
	    # distance from start to next in user units
	    set d0 [expr [ComputeDistFD $tp0 $nxt]*$DSCALE]
	    # maximum distance from first to next
	    if { $d0 > $mxd0 } {
		set mxd0 $d0 ; set ismxd0 [expr $i+1]
	    } elseif { $d0 == $mxd0 } { lappend ismxd0 [expr $i+1] }
	    # AP contribution
	    #=======
	    set al2 [UserAltitude [lindex $nxt 6]]
	    set dbTot [expr $dbTot + $d]
	    # MF change: add start segment flag for $nxt
	    lappend lll [list $dbTot $al2 $endsegm]
	    #=======
	    # BS contribution
	    #  with MF change to cover cumulative descent
	    if { $al != "" && $al2 != "" } {
		if { $al2 > $al } {
		    set cumul_temp [expr $cumul_temp+$al2-$al]
		    if { $cumuld_temp >= 11 } {
			set cumuld [expr $cumuld+$cumuld_temp]
		    }
		    set cumuld_temp 0
		} elseif { $al2 < $al } {
		    if { $cumul_temp >= 11 } {
			set cumul [expr $cumul+$cumul_temp]
		    }
		    set cumul_temp 0
		    set cumuld_temp [expr $cumuld_temp+$al-$al2]
		}
	    }
	    #=======
	    # while $lll was updated with values for $nxt, $salts was
	    #  updated with values for $tp
	    if { $endsegm } {
		# speed is meaningless
		set sp "======"
	    } else {
		if { $dt != 0 } {
		    # compute speed from current to next
		    set sp [expr 3600*$d/$dt]
		    lappend speeds [list $dbTot $sp]
		    lappend sspeeds [list $secs $sp]
		    if { $sp > $mxsp } { set mxsp $sp ; set imx $i }
		    if { $sp < $mnsp } { set mnsp $sp ; set imn $i }
		    set sp [format "%6.2f" $sp]
		} else { set sp "======" }
		set tdng [expr $tdng+$d] ; incr ttng $dt
	    }
	    set td [expr $td+$d]
	    incr tt $dt
	    set d [format "%8.2f" $d]
	    set sd [format "%8.2f" $td]
	    set dt [FormatTime $dt]
	} else {
	    set d "========" ; set dt "========"
	    set sp "======" ; set b "===="
	    set sd "========"
	}
	foreach box $boxes \
		v [list [format "%4d." $i] [lindex $tp 4] \
		       [lindex $tp 2] [lindex $tp 3] $al $d $sd $dt $sp $b] {
	    $box insert end $v
	}
	incr i
    }
    # AP contribution
    #=======
    #From this point on you can call:
    #Hgraph $lll
    #=======
    # BS contribution
    set cumul [expr $cumul+$cumul_temp]
    #=======
    # MF contribution: same for descent
    set cumuld [expr $cumuld+$cumuld_temp]
    #=======
    set td [format "%8.2f" $td] ; set tdng [format "%8.2f" $tdng]
    if { $tt == 0 } {
	set avsp "======"
	set mxp "======" ; set mnsp "======"
	# BS contribution
	set cumul "======"
	#=======
	# MF contribution: same for descent
	set cumuld "======"
	#=======
    } else {
	set avsp [format "%6.2f" [expr 3600*$td/$tt]]
	set mxsp [format "%6.2f" $mxsp] ; set mnsp [format "%6.2f" $mnsp]
	# BS contribution
	set cumul [format "%6.0f" $cumul]
	#=======
	# MF contribution: same for descent
	set cumuld [format "%6.0f" $cumuld]
	#=======
    }
    set tt [FormatTime $tt] ; set ttng [FormatTime $ttng]
    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"

    if { $tdng != $td || $ttng != $tt } {
	set gaps 1
	frame $w.fr.fr3.frtng -relief flat -borderwidth 0
	label $w.fr.fr3.frtng.td -text "$TXT(totdstng): $tdng $DTUNIT"
	label $w.fr.fr3.frtng.tt -text "$TXT(tottimeng): $ttng"
    } else { set gaps 0 }

    frame $w.fr.fr3.frsp -relief flat -borderwidth 0
    label $w.fr.fr3.frsp.avg -text "$TXT(avgsp): $avsp $SPUNIT"
    label $w.fr.fr3.frsp.max -text "$TXT(maxsp): $mxsp (@$imx)"
    label $w.fr.fr3.frsp.min -text "$TXT(minsp): $mnsp (@$imn)"
    
    # MF contribution: frame for cummulative ascent and descent
    frame $w.fr.fr3.frcad -relief flat -borderwidth 0
    label $w.fr.fr3.frcad.cumuld -text "$TXT(alt_cumuld): $cumuld $SUBDTUNIT"
    #=======
    # BS contribution
    label $w.fr.fr3.frcad.cumul -text "$TXT(alt_cumul): $cumul $SUBDTUNIT"  
    #======

    if { $maxalt > -1e10 } {
	set hasalt 1
	set maxalt [expr int(round($maxalt))]
	set minalt [expr int(round($minalt))]
	frame $w.fr.fr3.frmxnalt -relief flat -borderwidth 0
	label $w.fr.fr3.frmxnalt.mx -text "$TXT(maxalt): $maxalt $SUBDTUNIT"
	label $w.fr.fr3.frmxnalt.mn -text "$TXT(minalt): $minalt $SUBDTUNIT"
    } else { set hasalt 0 }

    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 $TXT(ok) -command "destroy $w"
    # AP contribution; changed by MF
    #-----
    if { [llength $lll] < 3 } {
	set elevstate disabled
    } else { set elevstate normal }
    # menu used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    set mn $w.fr.frsel.hgraph.mn
    menubutton $w.fr.frsel.hgraph -text $TXT(namePlot) -relief raised \
	    -menu $mn
    menu $mn -tearoff 0
    # menu used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    set mn1 $mn.el
    $mn add cascade -label $TXT(elevation) -menu $mn1 -state $elevstate
    menu $mn1 -tearoff 0
    $mn1 add command -label "$TXT(sideview)/$TXT(TRVdist)" \
	-command "Hgraph {$lll} elevation"
    $mn1 add command -label "$TXT(sideview)/$TXT(TRVhour)" \
	-command "Hgraph {$salts} elevation time"
    $mn1 add command -label $TXT(persptv) \
	-command "HG3D {$tmp3D} {$tmp3D2} {$datum}"
    #-----
    $mn1 add command -label $TXT(climbrate) \
	-command "Hgraph {$salts} climbrate time"
    $mn add command -label "$TXT(speed)/$TXT(TRVdist)" \
	-command "Hgraph {$speeds} speed"
    $mn add command -label "$TXT(speed)/$TXT(TRVhour)" \
	-command "Hgraph {$sspeeds} speed time"

    pack $w.fr -side top
    pack $w.fr.fr1.ntitle -side left
    pack $w.fr.fr3.frtits.titxn $w.fr.fr3.frtits.titxd \
	$w.fr.fr3.frtits.titxlat  $w.fr.fr3.frtits.titxlong \
	$w.fr.fr3.frtits.tital $w.fr.fr3.frtits.titxl $w.fr.fr3.frtits.tittl \
	$w.fr.fr3.frtits.titdt $w.fr.fr3.frtits.titsp $w.fr.fr3.frtits.titbg \
	$w.fr.fr3.frtits.fill -side left
    eval pack $boxes $w.fr.fr3.frbx.bscr -side left -fill y
    pack $w.fr.fr3.frt.td $w.fr.fr3.frt.tt -side left -padx 5
    if { $gaps } {
	pack $w.fr.fr3.frtng.td $w.fr.fr3.frtng.tt -side left -padx 5
    }
    pack $w.fr.fr3.frsp.avg $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
    # BS contribution: $w.fr.fr3.frcad.cumul
    # MF change: using frame for cummulative ascent and descent and showing
    #  altitude information only when available
    if { $hasalt } {
	pack $w.fr.fr3.frmxnalt.mx $w.fr.fr3.frmxnalt.mn -side left -padx 5
	pack $w.fr.fr3.frcad.cumul $w.fr.fr3.frcad.cumuld -side left -padx 5
	pack $w.fr.fr3.frt -side top -fill y -pady 5
	if { $gaps } {
	    pack $w.fr.fr3.frtng -side top -fill y -pady 5
	}
	pack $w.fr.fr3.frsp $w.fr.fr3.frmxnalt $w.fr.fr3.frcad $w.fr.fr3.frd0 \
	    -side top -fill y -pady 5	
    } else {
	pack $w.fr.fr3.frt -side top -fill y -pady 5
	if { $gaps } {
	    pack $w.fr.fr3.frtng -side top -fill y -pady 5
	}
	pack $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

    AttachPlugIns $w

    ResetCursor .
    return
}

proc GMLine {index options data} {
    # create dialog window for editing/showing data of LN with given index
    #  $options is a list of buttons to display;
    #  $index is -1 if this is a new LN
    #	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(LN)
    #  which is used below
    # return window path
    global GMEd MapLoading DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	OBSWIDTH OBSHEIGHT DATEWIDTH TXT DATUMWIDTH

    foreach "name obs datum pformt lps segsts width colour mbak displ" $data {}
    set ed 0 ; set st disabled
    if { $options != "" } {
	if { [winfo exists .gmLN] } { Raise .gmLN ; bell ; return .gmLN }
	set ed 1 ; set st normal
	set w .gmLN
	set GMEd(LN,Index) $index ; set GMEd(LN,Displ) $displ
	set GMEd(LN,Datum) $datum ; set GMEd(LN,PFrmt) $pformt
	set GMEd(LN,LPs) $lps ; set GMEd(LN,SgSts) $segsts
	set GMEd(LN,Colour) $colour ; set GMEd(LN,Width) $width
	set GMEd(LN,MapChg) 0 ; set GMEd(LN,MBack) $mbak
	# this depends on Storage(LN)
	set GMEd(LN,Data) $data
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmLNsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(LN,Show)
	set x [expr $DPOSX+50*((1+$GMEd(LN,Show)) % 5)]
	set y [expr $DPOSY+50*((1+$GMEd(LN,Show)) % 5)]
    }

    GMToplevel $w nameLN +$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 LN 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.frdpf -relief flat -borderwidth 0
    menubutton $w.fr.frdpf.dttitle -text Datum -relief raised \
	    -direction below -menu $w.fr.frdpf.dttitle.m -state $st
    menu $w.fr.frdpf.dttitle.m -tearoff 0
    menubutton $w.fr.frdpf.pfmt -text $TXT($pformt) -relief raised -width 8 \
	    -direction below -menu $w.fr.frdpf.pfmt.m -state $st
    menu $w.fr.frdpf.pfmt.m -tearoff 0
    if { $ed } {
	FillDatumMenu $w.fr.frdpf.dttitle.m GMLNChangeDatum
	label $w.fr.frdpf.datum -text $GMEd(LN,Datum) \
	    -textvariable GMEd(LN,Datum) -width $DATUMWIDTH
	$w.fr.frdpf.pfmt configure -textvariable GMEd(LN,PFrmt)
	FillPFormtMenu $w.fr.frdpf.pfmt.m GMLNChangePFormt
    } else {
	label $w.fr.frdpf.datum -text $datum -width $DATUMWIDTH
    }

    frame $w.fr.fr3 -relief flat -borderwidth 0
    frame $w.fr.fr3.frbx -relief flat -borderwidth 0
    set boxes ""
    foreach b "n pos alt seg" m "5 30 7 1" {
	listbox $w.fr.fr3.frbx.bx$b -height 15 -width $m -relief flat \
	    -yscrollcommand "$w.fr.fr3.frbx.bscr set" \
 	    -selectmode extended -exportselection false
	lappend boxes $w.fr.fr3.frbx.bx$b
	bind $w.fr.fr3.frbx.bx$b <<ListboxSelect>> \
	    "MultExtSelect $w.fr.fr3.frbx.bx$b {bxn bxpos bxalt bxseg}"
    }
    if { $ed } {
	set GMEd(LN,boxes) $boxes
	foreach box $boxes {
	    bind $box <Double-1> { GMLinePoint [%W nearest %y] }
	}
	bind $w.fr.fr3.frbx.bxseg <Button-3> {
	    GMToggleSegStart LN %W [%W nearest %y]
	}
    }
    scrollbar $w.fr.fr3.frbx.bscr -command [list ScrollMany $boxes]
    Mscroll $boxes
    FillLPs $w $lps $segsts

    frame $w.fr.fr3.frbt -relief flat -borderwidth 0
    button $w.fr.fr3.frbt.chh -text $TXT(chophd) -state $st \
	    -command { GMLNChange chh }
    button $w.fr.fr3.frbt.cht -text $TXT(choptl) -state $st \
	    -command { GMLNChange cht }
    foreach a "incb app" {
	button $w.fr.fr3.frbt.$a -text $TXT($a) -state $st \
	    -command "ChItemsCall LN single GMLNChange $a"
    }
    button $w.fr.fr3.frbt.loop -text $TXT(loop) -state $st \
	    -command { GMLNChange loop }
    button $w.fr.fr3.frbt.del -text $TXT(del) -state $st \
	    -command { GMLNChange del }
    button $w.fr.fr3.frbt.clear -text $TXT(clear) -state $st \
	    -command { GMLNChange clear }

    frame $w.fr.fr3.frbt.sep -height 6 -bg $COLOUR(dialbg) \
	    -relief flat -borderwidth 0
    set mnc $w.fr.fr3.frbt.cnv.m
    menubutton $w.fr.fr3.frbt.cnv -text $TXT(convert) -relief raised \
	    -direction right -menu $mnc
    menu $mnc -tearoff 0
    $mnc add command -label $TXT(mkTR) -command "LNToTR $w"
    $mnc add cascade -label $TXT(split) -menu $mnc.mns
    menu $mnc.mns -tearoff 0
    $mnc.mns add command -label $TXT(bysel) \
	-command "SplitPolyLine LN sel $w $ed $index"
    $mnc.mns add command -label $TXT(byseg) \
	-command "SplitPolyLine LN segm $w $ed $index"

    frame $w.fr.frsel -relief flat -borderwidth 0
    # frame used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    frame $w.fr.frdw
    set mn $w.fr.frdw.mw.m
    menubutton $w.fr.frdw.mw -text $TXT(width) -relief raised \
	    -direction below -menu $mn -state $st
    menu $mn -tearoff 0
    button $w.fr.frdw.b -text $TXT(Colour) -relief raised \
	    -command "ChooseColour GMEd GMEd(LN,Colour) $w.fr.frdw $w" \
	    -state $st
    label $w.fr.frdw.bc -relief groove -background $colour -width 2

    frame $w.fr.frmb
    CreateMBackWidgets LN $w.fr.frmb $mbak $ed

    if { $ed } {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-variable GMEd(LN,Displ) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	foreach i "1 2 3 4 5 6 7 8" {
	    $mn add command -label $i -command "set GMEd(LN,Width) $i"
	}
	label $w.fr.frdw.wv -width 3 -textvariable GMEd(LN,Width)
	if { $MapLoading != 0 } {
	    foreach i "displayed mw b" {
		$w.fr.frdw.$i 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 LN $e"
	    pack $b$e -side left
	}
    } else {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) -state disabled \
		-selectcolor $COLOUR(check)
	if { $displ } { $w.fr.frdw.displayed select }
	label $w.fr.frdw.wv -width 3 -text $width
	button $w.fr.frsel.b -text $TXT(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.frdpf.dttitle $w.fr.frdpf.datum -side left -padx 3
    pack $w.fr.frdpf.pfmt -side left -padx 10
    eval pack $boxes $w.fr.fr3.frbx.bscr -side left -fill y
    pack $w.fr.fr3.frbt.chh $w.fr.fr3.frbt.cht $w.fr.fr3.frbt.incb \
	    $w.fr.fr3.frbt.app $w.fr.fr3.frbt.loop $w.fr.fr3.frbt.del \
	    $w.fr.fr3.frbt.clear $w.fr.fr3.frbt.sep \
	    $w.fr.fr3.frbt.cnv -side top -pady 2 -fill x
    pack $w.fr.fr3.frbx $w.fr.fr3.frbt -side left -padx 5
    pack $w.fr.frdw.displayed $w.fr.frdw.mw -side left -padx 3
    pack $w.fr.frdw.wv -side left -padx 0
    pack $w.fr.frdw.b -side left -padx 10
    pack $w.fr.frdw.bc -side left -padx 0
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.frdpf $w.fr.fr3 $w.fr.frdw -side top -pady 5
    pack $w.fr.frmb -side top
    pack $w.fr.frsel -side top -pady 5

    AttachPlugIns $w

    update idletasks
    return $w
}

proc RevertLN {} {
    # reset data in LN edit window to initial values
    # this depends on Storage(LN)
    global GMEd

    set GMEd(LN,MapChg) 0 ; set data $GMEd(LN,Data)
    foreach box $GMEd(LN,boxes) { $box delete 0 end }
    .gmLN.fr.fr1.id delete 0 end
    .gmLN.fr.fr1.id insert 0 [lindex $data 0]
    .gmLN.fr.fr2.obs delete 1.0 end
    .gmLN.fr.fr2.obs insert 1.0 [lindex $data 1]
    foreach e "Datum PFrmt LPs SgSts Width Colour MBack Displ" \
	    v [lreplace $data 0 1] {
	set GMEd(LN,$e) $v
    }
    FillLPs .gmLN $GMEd(LN,LPs) $GMEd(LN,SgSts)
    .gmLN.fr.frdw.bc configure -background $GMEd(LN,Colour)
    if { $GMEd(LN,Displ) } {
	.gmLN.fr.frdw.displayed select
    } else {
	.gmLN.fr.frdw.displayed deselect
    }
    return
}

proc GMLNCheck {} {
    # check validity of data in LN edit window
    # this depends on Storage(LN)
    global GMEd MESS

    set id [.gmLN.fr.fr1.id get]
    if { ! [CheckString GMMessage $id] } {
	focus .gmLN.fr.fr1.id
	return nil
    }
    if { [llength $GMEd(LN,LPs)] < 2 } {
	GMMessage $MESS(voidLN)
	return nil
    }
    if { ! $GMEd(LN,MapChg) && \
	    ( $GMEd(LN,Width) != [lindex $GMEd(LN,Data) 6] || \
	      $GMEd(LN,Colour) != [lindex $GMEd(LN,Data) 7] ) } {
	set GMEd(LN,MapChg) 1
    }
    set r [list $id [CheckNB [.gmLN.fr.fr2.obs get 0.0 end]]]
    foreach e "Datum PFrmt LPs SgSts Width Colour MBack Displ" {
	lappend r $GMEd(LN,$e)
    }
    return $r
}

proc GMLNChange {how args} {
    # perform edit operations on LN
    #  $how is either one of the operations supported by GMPolyChange, or
    #      loop    add first point as last
    global GMEd

    switch $how {
	loop {
	    set boxes $GMEd(LN,boxes)
	    set bxn [lindex $boxes 0]
	    if { [set n [$bxn size]] < 2 } { return }
	    lappend GMEd(LN,LPs) [lindex $GMEd(LN,LPs) 0]
	    $bxn insert end [format "%4d." [incr n]]
	    foreach box [lreplace $boxes 0 0] {
		$box insert end [$box get 0]
	    }	    
	}
	default {
	    GMPolyChange LN $how $args
	}
    }
    return
}

proc GMLPChangeDatum {datum args} {
    # change datum of LP being edited
    #  $args is not used but is needed as this is called-back from a menu

    ChangeDatum $datum GMEd GMEd(LP,Datum) nil .gmLP.fr.fr1.frp normal
    return
}

proc GMLinePoint {i} {
    # edit LP at position $i of listboxes in LN edit window
    # assume that the LN datum is kept compatible with LN position format
    global GMEd TXT INVTXT DPOSX DPOSY COLOUR ALUNIT

    if { [set lp [lindex $GMEd(LN,LPs) $i]] == "" } { return }
    foreach "posn alt" [lindex $GMEd(LN,LPs) $i] { break }
    if { $posn == "" } { return }

    set w .gmLP
    if { [winfo exists $w] } { Raise $w ; bell ; return }
    set pformt $GMEd(LN,PFrmt)
    set datum [set GMEd(LP,Datum) $GMEd(LN,Datum)]

    GMToplevel $w nameLP +$DPOSX+$DPOSY .gmLN \
        [list WM_DELETE_WINDOW "destroy $w"] \
        {<Key-Return> {set GMEd(temp) 1 ; break}}

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.title -text $TXT(nameLP)

    frame $w.fr.fr1 -relief flat -borderwidth 0
    ShowPosnDatum $w.fr.fr1 $pformt [list $posn] GMLPChangeDatum GMEd \
	GMEd(LP,Datum) normal 1 nil

    frame $w.fr.fr11 -relief flat -borderwidth 0
    label $w.fr.fr11.atit -text "$TXT(alt) ($ALUNIT):"
    entry $w.fr.fr11.alt -width 7 -exportselection 1
    set valt [UserAltitude $alt]
    ShowTEdit $w.fr.fr11.alt $valt 1

    frame $w.fr.frb -relief flat -borderwidth 0
    button $w.fr.frb.ok -text $TXT(ok) -command "set GMEd(temp) 1"
    button $w.fr.frb.cnc -text $TXT(cancel) -command "set GMEd(temp) 0"

    pack $w.fr.fr11.atit $w.fr.fr11.alt -side left -padx 3
    pack $w.fr.fr1.frp $w.fr.fr1.frd -side top -pady 3
    pack $w.fr.frb.ok $w.fr.frb.cnc -side left
    pack $w.fr.title $w.fr.fr1 $w.fr.fr11 $w.fr.frb -side top -pady 5
    pack $w.fr

    update idletasks
    set gs [grab current]
    grab $w
    raise $w
    while 1 {
	tkwait variable GMEd(temp)
	if { $GMEd(temp) == 0 } { set ok 0 ; break }
	set p [PosnGetCheck $w.fr.fr1.frp.frp1 $GMEd(LP,Datum) GMMessage nil]
	if { $p == "nil" } { continue }
	set valt [string trim [$w.fr.fr11.alt get]]
	if { [set alt [AltitudeList $valt]] != "nil" } { set ok 1 ; break }
	GMMessage $MESS(badalt)
    }
    if { $ok } {
	# this depends on what proc ShowPosnDatum does!
	set pf $INVTXT([$w.fr.fr1.frp.pfmt cget -text])
	if { $pf != $pformt || $GMEd(LP,Datum) != $datum } {
	    # LN datum is always compatible with LN position format
	    set p [lindex [FormatPosition [lindex $p 0] [lindex $p 1] \
			       $GMEd(LP,Datum) $pformt $datum] 0]
	}
	set lp [list $p $alt]
	set GMEd(LN,LPs) [lreplace $GMEd(LN,LPs) $i $i $lp]
	foreach "x boxp boxa" $GMEd(LN,boxes) { break }
	set selected [$boxp selection includes $i]
	$boxp delete $i ; $boxp insert $i [lrange $p 2 end]
	$boxa delete $i ; $boxa insert $i [UserAltitude $alt]
	if { $selected } {
	    foreach b $GMEd(LN,boxes) { $b selection set $i }
	}
    }
    DestroyRGrabs $w $gs
    update idletasks
    return
}

proc GMLNChangeDatum {datum args} {
    # change datum of LN being edited
    #  $args not used but needed for call-back
    # this may fail if the current position format requires a fixed datum
    global GMEd MESS

    if { $datum == [set od $GMEd(LN,Datum)] || \
	    ( [lindex $GMEd(LN,LPs) 200] != "" && \
	    ! [GMConfirm $MESS(timeconsmg)] ) } { return }
    SetCursor . watch
    if { [set pts [ChangeLPsDatum $GMEd(LN,LPs) $od $datum $GMEd(LN,PFrmt)]] \
	    == -1 } {
	ResetCursor .
	return
    }
    set box [lindex $GMEd(LN,boxes) 1]
    set sel [$box curselection]
    $box delete 0 end
    foreach lp $pts {
	$box insert end [lrange [lindex $lp 0] 2 end]
    }
    foreach s $sel { $box selection set $s }
    foreach box $GMEd(LN,boxes) { $box see 0 }
    set GMEd(LN,LPs) $pts
    set GMEd(LN,Datum) $datum
    ResetCursor .
    return
}

proc ChangeLPsPFormt {lps pformt dvar dvref} {
    # change position format of a LN list of points
    #  $dvar is global variable or array name containg current datum
    #  $dvref is corresponding variable or array(element) name to use
    #   in changing the datum if needs be
    global $dvar

    set odatum [set $dvref]
    if { [set gdatum [BadDatumFor $pformt $odatum Ignore]] != 0 } {
	set $dvref $gdatum
	return [ChangeLPsDatum $lps $odatum $gdatum $pformt]
    }
    set l ""
    foreach lp $lps {
	foreach "latd longd" [lindex $lp 0] { break }
	set p [lindex [FormatPosition $latd $longd $odatum $pformt $odatum] 0]
	lappend l [lreplace $lp 0 0 $p]
    }
    return $l
}

proc GMLNChangePFormt {pformt} {
    # change position format of LN being edited
    global GMEd TXT MESS

    if { $pformt == $GMEd(LN,PFrmt) || \
	( [lindex $GMEd(LN,LPs) 200] != "" && \
          ! [GMConfirm $MESS(timeconsmg)] ) } { return }
    SetCursor . watch
    set pts [ChangeLPsPFormt $GMEd(LN,LPs) $pformt GMEd GMEd(LN,Datum)]
    set box [lindex $GMEd(LN,boxes) 1]
    set sel [$box curselection]
    $box delete 0 end
    foreach lp $pts {
	$box insert end [lrange [lindex $lp 0] 2 end]
    }
    foreach s $sel { $box selection set $s }
    foreach box $GMEd(LN,boxes) { $box see 0 }
    set GMEd(LN,LPs) $pts
    set GMEd(LN,PFrmt) $TXT($pformt)
    ResetCursor .
    return
}

proc FillLPs {w lps sgsts} {
    # insert LPs with segments $sgsts, in listboxes in LN edit/show
    #  window $w

    set i 0 ; set nxt [lindex $sgsts 0]
    foreach lp $lps {
	if { $nxt == $i } {
	    set seg "@"
	    set sgsts [lreplace $sgsts 0 0]
	    set nxt [lindex $sgsts 0]
	} else { set seg "" }
	incr i
	$w.fr.fr3.frbx.bxn insert end [format "%4d." $i]
	$w.fr.fr3.frbx.bxpos insert end [lrange [lindex $lp 0] 2 end]
	$w.fr.fr3.frbx.bxalt insert end [UserAltitude [lindex $lp 1]]
	$w.fr.fr3.frbx.bxseg insert end $seg
    }
    return
}

proc LNToTR {w} {
    # make a TR from the LN in window $w
    global GMEd LNLPoints LNDatum LNPFrmt LNSegStarts EdWindow TXT MESS 

    set tpfsa "alt latd longd latDMS longDMS"
    set tps ""
    if { $w == ".gmLN" } {
	if { [set lps $GMEd(LN,LPs)] == "" } {
	    GMMessage $MESS(voidLN)
	    return
	}
	set datum $GMEd(LN,Datum) ; set pformt $GMEd(LN,PFrmt)
	set sgsts $GMEd(LN,SgSts)
    } else {
	set ix [IndexNamed LN [$w.fr.fr1.id get]]
	set lps $LNLPoints($ix) ; set datum $LNDatum($ix)
	set pformt $LNPFrmt($ix) ; set sgsts $LNSegStarts($ix)
    }
    foreach lp $lps {
	foreach "p alt" $lp { break }
	if { $pformt != "DMS" } {
	    set p [FormatLatLong [lindex $p 0] [lindex $p 1] DMS]
	}
	set p [lrange $p 0 3]
	lappend tps [FormData TP $tpfsa [linsert $p 0 $alt]]
    }
    if { $tps == "" } { bell ; return }
    if { [winfo exists $EdWindow(TR)] } {
	set name [NewName TR]
	set data [FormData TR "Name Datum TPoints SegStarts" \
		      [list $name $datum $tps $sgsts]]
	CreateItem TR $data
	GMMessage [format $MESS(convres) $TXT(TR) $name]
    } else {
	set opts "create revert cancel"
	GMTrack -1 $opts [FormData TR "Datum TPoints SegStarts" \
			      [list $datum $tps $sgsts]]
    }
    return
}

proc GMLap {index options data} {
    # create dialog window for editing/showing data of LAP with given index
    #  $options is a list of buttons to display;
    #  $index is never -1, as LAPs cannot be created
    #	an empty list means no editing; supported options are:
    #     cancel, change, revert, forget
    #     change and forget assume $index != -1, always true here
    #     see proc GMButton for further details
    #  if $options is empty, $index cannot be -1 as this is not a new LAP
    #   the only button is OK, and only binding: return to destroy
    # the only things that can be changed in a LAP are the name, the
    #   remark, the position format and the datum
    # order of elements in $data list reflects order in $Storage(LAP)
    #  which is used below
    # return window path
    global GMEd DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	OBSWIDTH OBSHEIGHT DATEWIDTH TXT SUBDSCALE DTUNIT SPUNIT \
	ChangedLAPPos1 ChangedLAPPos2

    foreach \
	"name obs start dur dist begpos endpos cals trix pformt datum displ" \
	$data {}
    set ed 0 ; set st disabled
    if { $options != "" } {
	if { [winfo exists .gmLAP] } { Raise .gmLAP ; bell ; return .gmLAP }
	set ed 1 ; set st normal
	set w .gmLAP
	set GMEd(LAP,Index) $index
	set GMEd(LAP,Datum) $datum ; set GMEd(LAP,PFrmt) $pformt
	# for uniformity, meaningless
	set GMEd(LAP,MapChg) 0 ; set GMEd(LAP,Displ) $displ
	# this depends on Storage(LAP)
	set GMEd(LAP,Data) $data
	set ChangedLAPPos1 $begpos ; set ChangedLAPPos2 $endpos
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmLAPsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(LAP,Show)
	set x [expr $DPOSX+50*((1+$GMEd(LAP,Show)) % 5)]
	set y [expr $DPOSY+50*((1+$GMEd(LAP,Show)) % 5)]
    }

    if { $start != "" && $dur != "" } {
	foreach "startdate startsecs" $start { break }
	set dsecs [TimeToSecs $dur]
	set enddate [DateFromSecs [expr $startsecs+round($dsecs)]]
	if { $dist != "" } {
	    set dist [expr $dist*$SUBDSCALE]
	    set avgspeed [format "%.2f $SPUNIT" [expr 3600.0*$dist/$dsecs]]
	    set dist [format "%.3f $DTUNIT" $dist]
	} else { set avgspeed "" }
    } else {
        foreach v "startdate enddate avgspeed" { set $v "" }
    }

    GMToplevel $w nameLAP +$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 LAP cancel }
    }

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

    frame $w.fr.fr1 -relief flat -borderwidth 0
    label $w.fr.fr1.id -text "$TXT(name): $name"

    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

    set frtm $w.fr.frtm
    frame $frtm -relief flat -borderwidth 0
    label $frtm.strt -text "$TXT(start): $startdate"
    label $frtm.dur -text "$TXT(duration): $dur"
    label $frtm.end -text "$TXT(stop): $enddate"

    set frdi $w.fr.frdi
    frame $frdi -relief flat -borderwidth 0
    label $frdi.dst -text "$TXT(distance): $dist"
    label $frdi.spd -text "$TXT(avgsp): $avgspeed"
    label $frdi.cls -text "$TXT(calrs): $cals"

    if { $ed } {
	ShowPosnDatum $w.fr $pformt [list $begpos $endpos] GMLAPChangeDatum \
	    GMEd GMEd(LAP,Datum) disabled 1 =ChangedLAPPos
	$w.fr.frp.pfmt configure -state normal
	$w.fr.frd.dttitle configure -state normal
    } else {
	ShowPosnDatum $w.fr $pformt [list $begpos $endpos] "" "" $datum $st \
	    0 nil
    }

    set frtr $w.fr.frtr
    frame $frtr -relief flat -borderwidth 0
    label $frtr.tit -text "$TXT(nameTR):"
    if { $trix < 253 && [set trixix [IndexNamed TR $trix]] != -1 } {
	set trst normal
    } else { set trst disabled ; set trixix -1 }
    button $frtr.tr -text $trix -command "OpenItem TR $trixix" -state $trst
    button $frtr.disp -text $TXT(displ) -command "GMLAPDisplayTR $trixix" \
	-state $trst

    frame $w.fr.frsel -relief flat -borderwidth 0
    if { $ed } {
	set b $w.fr.frsel.b
	foreach e $options {
	    button $b$e -text $TXT($e) \
		-command "$b$e configure -state normal ; GMButton LAP $e"
	    pack $b$e -side left
	}
    } else {
	button $w.fr.frsel.b -text $TXT(ok) -command "destroy $w"
	pack $w.fr.frsel.b
    }

    pack $w.fr -side top
    pack $w.fr.fr1.id -side left -padx 3
    pack $w.fr.fr2.obstit $w.fr.fr2.obs -side left -padx 3
    set r -1
    foreach x "strt dur end" {
	grid $frtm.$x -row [incr r] -column 0 -sticky w
    }
    set r -1
    foreach x "dst spd cls" {
	grid $frdi.$x -row [incr r] -column 0 -sticky w
    }
    pack $frtr.tit $frtr.tr -side left -padx 0
    pack $frtr.disp -side left -padx 5

    grid $w.fr.fr1 -row 0 -column 0 -columnspan 2
    grid $w.fr.fr2 -row 1 -column 0 -columnspan 2 -pady 5
    grid $w.fr.frtm -row 2 -column 0 -pady 5
    grid $w.fr.frdi -row 2 -column 1 -pady 5 -padx 10
    grid $w.fr.frp -row 3 -column 0 -columnspan 2 -pady 5
    grid $w.fr.frd -row 4 -column 0 -columnspan 2 -pady 5
    grid $w.fr.frtr -row 5 -column 0 -columnspan 2 -pady 5
    grid $w.fr.frsel -row 6 -column 0 -columnspan 2 -pady 5

    update idletasks
    return $w
}

proc GMLAPCheck {} {
    # check validity of data in LAP edit window
    # this depends on Storage(LAP)
    # return "nil" on error
    global GMEd INVTXT ChangedLAPPos1 ChangedLAPPos2

    set nb [CheckNB [.gmLAP.fr.fr2.obs get 0.0 end]]
    set data $GMEd(LAP,Data)
    set r [lreplace $data 1 1 $nb]
    set r [lreplace $r 5 6 $ChangedLAPPos1 $ChangedLAPPos2]
    return [lreplace $r 9 10 $INVTXT([.gmLAP.fr.frp.pfmt cget -text]) \
	       $GMEd(LAP,Datum)]
}

proc RevertLAP {} {
    # reset data in LAP edit window to initial values
    # this depends on Storage(LAP)
    global GMEd INVTXT POSTYPE

    set data $GMEd(LAP,Data)
    .gmLAP.fr.fr2.obs delete 1.0 end
    .gmLAP.fr.fr2.obs insert 1.0 [lindex $data 1]
    set pft $POSTYPE($INVTXT([.gmLAP.fr.frp.pfmt cget -text]))
    set opf [lindex $data 9] ; set t $POSTYPE($opf)
    set p1 [lindex $data 5] ; set p2 [lindex $data 6]
    if { $pft == $t } {
	RevertPos .gmLAP.fr.frp.frp1 $opf $t $p1
	RevertPos .gmLAP.fr.frp.frp2 $opf $t $p2
    } else {
	RedrawPos .gmLAP.fr.frp.frp1 $opf $p1 ChangedPosn1 disabled
	RedrawPos .gmLAP.fr.frp.frp2 $opf $p2 ChangedPosn2 disabled
    }
    set GMEd(LAP,PFormt) $opf
    set GMEd(LAP,Datum) [lindex $data 10]
    return
}

proc GMLAPChangeDatum {datum args} {
    # change datum of LAP being edited
    #  $args is not used but is needed as this is called-back from a menu

    ChangeDatum $datum GMEd GMEd(LAP,Datum) =ChangedLAPPos .gmLAP.fr.frp normal
    return
}

proc GMLAPDisplayTR {ix} {
    # display existing TR associated to LAP
    #  $ix is a valid TR index
    global TRDispl

    if { ! $TRDispl($ix) } { PutMap TR $ix }
    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
    #  otherwise a button "Forget GR&Contents" is always created
    # order of elements in $data list reflects order in $Storage(GR)
    #  which is used below
    # return window path
    global GMEd MapLoading DPOSX DPOSY COLOUR LISTHEIGHT COMMENTWIDTH \
	    OBSWIDTH OBSHEIGHT TXT TYPES SUPPORTLAPS

    set gtypes $TYPES
    if { $SUPPORTLAPS } { lappend gtypes LAP }
    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 GMEd(GR,Index) $index ; set GMEd(GR,Displ) $displ
	set GMEd(GR,MapChg) 0
	set GMEd(GR,types) $gtypes
	# this depends on Storage(GR)
	set GMEd(GR,Data) $data
	set x $DPOSX
	set y $DPOSY
    } else {
	set w .gmGRsh$index
	if { [winfo exists $w] } { destroy $w }
	incr GMEd(GR,Show)
	set x [expr $DPOSX+50*(1+$GMEd(GR,Show) % 5)]
	set y [expr $DPOSY+50*(1+$GMEd(GR,Show) % 5)]
    }

    toplevel $w
    wm title $w "$TXT(group)/GPS Manager"
    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 "4 4 $COMMENTWIDTH" {
	listbox $w.fr.fr3.frbx.$b -height 15 -width $m -relief flat \
		-yscrollcommand "$w.fr.fr3.frbx.bscr set" \
		-selectmode extended -exportselection false
	bind $w.fr.fr3.frbx.$b <Double-1> \
	    "GRActItem $w.fr.fr3.frbx.$b %y open"
	bind $w.fr.fr3.frbx.$b <Button-3> \
	    "GRActItem $w.fr.fr3.frbx.$b %y toggle"
	bind $w.fr.fr3.frbx.$b <<ListboxSelect>> \
	    "MultExtSelect $w.fr.fr3.frbx.$b {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
	    if { [IndexNamed $wh $e] == -1 } {
		$w.fr.fr3.frbx.box itemconfigure end \
		    -foreground $COLOUR(ballfg)
	    }
	    $w.fr.fr3.frbx.bxw insert end $TXT($wh)
	    $w.fr.fr3.frbx.bxn insert end [format "%3d." $i]
	    incr i
	}
    }

    frame $w.fr.fr3.frbt -relief flat -borderwidth 0
    label $w.fr.fr3.frbt.title -text $TXT(element)
    foreach wh $gtypes {
	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"
    }
    foreach a "del repl" {
	button $w.fr.fr3.frbt.$a -text $TXT($a) -state $st \
	    -command "GMGRChange $a"
    }
    frame $w.fr.fr3.frbt.sep -height 6 -bg $COLOUR(dialbg) \
	    -relief flat -borderwidth 0
    button $w.fr.fr3.frbt.join -text $TXT(joinGR) -state $st \
	    -command "ChItemsCall GR single GMGRChange join"
    menu $w.fr.fr3.frbt.join.m -tearoff 0
    button $w.fr.fr3.frbt.clear -text $TXT(clear) -state $st \
	    -command { GMGRChange clr }

    set uwpsm $w.fr.fr3.frbt.uwps.m
    menubutton $w.fr.fr3.frbt.uwps -text $TXT(usewps) -relief raised \
	    -direction right -menu $uwpsm
    menu $uwpsm -tearoff 0
    $uwpsm add command -label $TXT(mkavgWP) \
	    -command "GMGRtoWP $w"

    $uwpsm add cascade -label $TXT(chgname) -menu $uwpsm.ren
    menu $uwpsm.ren
    menu $uwpsm.ren.m -postcommand \
	[list FillDefsMenu renamethod $uwpsm.ren.m [list GMGRRenameWPs $w]]
    $uwpsm.ren add cascade -label $TXT(use) -menu $uwpsm.ren.m
    $uwpsm.ren add command -label $TXT(define) \
	-command "GMGRRenameWPs $w \[Define renamethod\]"

    # JHT contribution: Change group waypoint symbol (menu item)
    $uwpsm add cascade -label $TXT(changegroupsymbol) \
	    -menu $uwpsm.symbol
    menu $uwpsm.symbol -tearoff 0
    FillSymbolsMenu $uwpsm.symbol ChangeGroupSymbol
    #---
    $uwpsm add cascade -label $TXT(chgpfrmt) \
	    -menu $uwpsm.mpf
    menu $uwpsm.mpf -tearoff 0
    FillPFormtMenu $uwpsm.mpf GMGRChangeWPPFormt {} $w
    $uwpsm add cascade -label $TXT(chgdatum) \
	    -menu $uwpsm.mdat
    menu $uwpsm.mdat -tearoff 0
    FillDatumMenu $uwpsm.mdat GMGRChangeWPDatum
    $uwpsm add command -label $TXT(mkclusters) \
	    -command "MakeClusters $w"

    frame $w.fr.frsel -relief flat -borderwidth 0
    # frame used for plug-ins (see array PLGSWelcomed, plugins.tcl)
    frame $w.fr.frdw
    if { $ed } {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
		-variable GMEd(GR,Displ) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	if { $MapLoading != 0 } {
	    $w.fr.frds.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 GR $e"
	    pack $b$e -side left
	}
	button ${b}fgc -text $TXT(forgetGRcs) -command GRForgetConts
	pack ${b}fgc -side left -before $b$e
    } else {
	checkbutton $w.fr.frdw.displayed -text $TXT(displ) \
	    -state disabled -selectcolor $COLOUR(check)
	if { $displ } { $w.fr.frdw.displayed select }
	button $w.fr.frsel.bok -text $TXT(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 -side top -pady 2 -fill x
    foreach wh $gtypes {
	pack $f.ins$wh -side top -pady 2 -fill x
    }
    pack $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.frdw.displayed
    pack $w.fr.fr1 $w.fr.fr2 $w.fr.fr3 \
	    $w.fr.frdw $w.fr.frsel -side top -pady 5

    AttachPlugIns $w

    update idletasks
    return $w
}

proc GRActItem {box y act} {
    # action on GR item
    #  $box is the listbox causing action
    #  $act in {open, toggle}
    global INVTXT COLOUR

    set p [winfo parent $box] ; set ixl [$box nearest $y]
    set n [$p.box get $ixl]
    set wh $INVTXT([$p.bxw get $ixl])
    if { $n != "" } {
	if { [set ix [IndexNamed $wh $n]] != -1 } {
	    set cl fg
	    switch $act {
		open { OpenItem $wh $ix }
		toggle { ToggleDisplayNamed $wh $n }
	    }
	} else { set cl ballfg }
	$p.box itemconfigure $ixl -foreground $COLOUR($cl)
    }
    return
}

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

    set data $GMEd(GR,Data)
    .gmGR.fr.fr1.id delete 0 end
    .gmGR.fr.fr1.id insert 0 [lindex $GMEd(GR,Data) 0]
    .gmGR.fr.fr2.obs delete 0.0 end
    .gmGR.fr.fr2.obs insert 0.0 [lindex $data 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 $data 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 [format "%3d." $i]
	    incr i
	}
    }
    if { [set GMEd(GR,Displ) [lindex $data 3]] } {
	.gmGR.fr.frdw.displayed select
    } else {
	.gmGR.fr.frdw.displayed deselect
    }
    return
}

proc NotWellFounded {gr1 gr2} {
    global GRConts

    if { $gr1 == $gr2 } { return 1 }
    if { [set ix [IndexNamed GR $gr2]] != -1 } {
	foreach p $GRConts($ix) {
	    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 GMEd 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
    }
    set types $TYPES ; lappend types LAP
    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 { $GMEd(GR,Index) != -1 && $id == [lindex $GMEd(GR,Data) 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 \
	    $GMEd(GR,Displ)]
}

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

    if { $list == "" || ! [winfo exists .gmGR] } { return }
    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 [format "%3d." [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
	}
    }
    return
}

proc GRForgetConts {} {
    # forget a group being edited and all its contents
    global GMEd INVTXT MESS

    if { ! [GMConfirm $MESS(frgetGRcs)] || \
	     ! [winfo exists .gmGR] } { return }
    set id [.gmGR.fr.fr1.id get]
    if { [.gmGR.fr.fr3.frbx.box size] != 0 } {
	foreach wh $GMEd(GR,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 { $GMEd(GR,Index) != -1 && $id == [lindex $GMEd(GR,Data) 0] } {
	    foreach g $es_GR {
		if { [NotWellFounded $id $g] } {
		    GMMessage "$MESS(initselfGR) $g"
		    return
		}
	    }
	}
	foreach g $es_GR {
	    foreach p [GRGetElements $g] {
		set wh [lindex $p 0]
		set es_$wh [concat [lindex $p 1] [set es_$wh]]
	    }
	}
	foreach wh $GMEd(GR,types) {
	    if { [set l [set es_$wh]] != "" } {
		set l [lsort $l] ; set prev ""
		while { $l != "" } {
		    set n [lindex $l 0] ; set l [lreplace $l 0 0]
		    if { $n != $prev } {
			set prev $n
			if { [set ix [IndexNamed $wh $n]] != -1 && \
				[Forget $wh $ix] } {
			    CloseItemWindows $wh $ix
			}
		    }
		}		
	    }
	}
    }
    destroy .gmGR
    if { $GMEd(GR,Index) != -1 } {
	Forget GR $GMEd(GR,Index)
    }
    return
}

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, LAP?})
    #      del   delete all selected items
    #      repl  replace first 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 {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
	    if { [set sel [lindex $sel 0]] == "" } { return }
	    set wh $INVTXT([.gmGR.fr.fr3.frbx.bxw get $sel])
	    if { [set ix [ChooseItems $wh single]] == "" } { return }
	    set new [NameOf $wh $ix]
	    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 }
	    foreach s [lsort -integer -decreasing $sel] {
		.gmGR.fr.fr3.frbx.box delete $s
		.gmGR.fr.fr3.frbx.bxw delete $s
		.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 GRGetElements {gr} {
    # collect all elements in GR $gr, recursively
    # assume GR is not being edited and that all GRs are well-founded
    # return list of pairs with type and list names of elements
    global GRConts TYPES SUPPORTLAPS

    set gtypes $TYPES
    if { $SUPPORTLAPS } { lappend gtypes LAP }
    if { [set ix [IndexNamed GR $gr]] == -1 } { return "" }
    foreach wh $gtypes { set es_$wh "" }
    set grs ""
    foreach p $GRConts($ix) {
	set wh [lindex $p 0]
	set es_$wh [lindex $p 1]
	if { $wh == "GR" } { set grs [set es_$wh] }
    }
    foreach g $grs {
	foreach p [GRGetElements $g] {
	    set wh [lindex $p 0]
	    set es_$wh [concat [lindex $p 1] [set es_$wh]]
	}
    }
    set ps ""
    foreach wh $gtypes {
	if { [set es_$wh] != "" } { lappend ps [list $wh [set es_$wh]] }
    }
    return $ps
}

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 GMEd GMember MESS

    set id [$window.fr.fr1.id get]
    if { $window == ".gmGR" } {
	if { [.gmGR.fr.fr3.frbx.box size] == 0 } { return void }
	set gtypes $TYPES ; lappend gtypes LAP
	foreach wh $gtypes { 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 { $GMEd(GR,Index) != -1 && $id == [lindex $GMEd(GR,Data) 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 GMGRCollectWPNames {window} {
    # collect all WP names in GR shown in $window (not recursively)
    # return list of names possibly empty
    global TXT INVTXT

    set names {}
    foreach twh [$window.fr.fr3.frbx.bxw get 0 end] \
	    name [$window.fr.fr3.frbx.box get 0 end] {
	if { $INVTXT($twh) == "WP" } {
	    lappend names $name
	}
    }
    return $names
}

# JHT contribution: Change group waypoint symbol (gang edit)
proc ChangeGroupSymbol {newsymbol window} {
    # change the symbol of all WPs in GR shown in $window
    global MESS EdWindow WPSymbol WPDispl

    regexp {^(\.[^.]+)\.} $window match window
    switch [set ixs [GMGRCollectWPs $window]] {
	void {
	    GMMessage $MESS(voidGR) ; return
	}
	error {
	    return
	}
    }
    if { $ixs == "" } { bell ; return }
    foreach ix $ixs {
	if { $WPSymbol($ix) != $newsymbol } {
	    set WPSymbol($ix) $newsymbol
	    UpdateItemWindows WP $ix
	    if { $WPDispl($ix) } {
		ChangeMapWPSymbol $ix $newsymbol
	    }
	}
    }
    return
}

proc GMGRChangeWPPFormt {pformt window} {
    # change the position format of all WPs in GR shown in $window
    # this may cause the datum of some WPs to be changed
    # nothing is done on WPs for which $pformt is invalid
    global MESS EdWindow WPPosn WPDatum WPPFrmt

    switch [set ixs [GMGRCollectWPs $window]] {
	void {
	    GMMessage $MESS(voidGR) ; return
	}
	error {
	    return
	}
    }
    if { $ixs == "" } { bell ; return }
    foreach ix $ixs {
	if { [set of $WPPFrmt($ix)] != $pformt } {
	    foreach "la lo" $WPPosn($ix) { break }
	    foreach "p pfmt datum" \
		[FormatPosition $la $lo $WPDatum($ix) $pformt ""] { break }
	    if { $pfmt != $of && [lindex $p 2] != "--" } {
		set WPPosn($ix) $p ; set WPPFrmt($ix) $pfmt
		set WPDatum($ix) $datum
		UpdateItemWindows WP $ix
	    }
	}
    }
    return
}

proc GMGRChangeWPDatum {datum menu} {
    # change the datum of all WPs in GR shown in top-level window with $menu
    # 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)
	    if { [BadDatumFor $pfmt $datum Ignore] != 0 } {
		continue
	    }
	    set p $WPPosn($ix)
	    set WPPosn($ix) [lindex [FormatPosition [lindex $p 0] \
					 [lindex $p 1] $od $pfmt $datum] 0]
	    set WPDatum($ix) $datum
	    UpdateItemWindows WP $ix
	}
    }
    return
}

proc GMToggleSegStart {wh w ix} {
    # toggle segment start flag at index $ix in listbox $w of edit window
    #  $wh in {TR, LN}
    global GMEd

    if { $ix <= 0 || $ix >= [$w size] } { return }
    if { [$w get $ix] == "" } {
	set seg "@"
	set GMEd($wh,SgSts) [lsort -integer -increasing \
				 [linsert $GMEd($wh,SgSts) 0 $ix]]
    } else {
	set seg ""
	if { [set k [lsearch -exact $GMEd($wh,SgSts) $ix]] == -1 } { return }
	set GMEd($wh,SgSts) [lreplace $GMEd($wh,SgSts) $k $k]
    }
    set selected [$w selection includes $ix]
    $w delete $ix ; $w insert $ix $seg
    if { $selected } { $w selection set $ix }
    set GMEd($wh,MapChg) 1
    return
}

proc ChopPolyline {wh whpt ix1 ixn} {
    # delete all points in polyline in range $ix1 to $ixn (indices from 0)
    #  and may extend to the end
    #  $wh in {TR, LN}
    #  $whpt in {TP, LP} depending on $wh
    # adjust segments and keep selections
    global GMEd

    set boxes $GMEd($wh,boxes)
    set bxn [lindex $boxes 0]
    set sel [$bxn curselection]
    set n [$bxn size]
    if { $ixn == "end" } {
	set ixn [expr $n-1]
    }
    # MB contribution
    if { $ix1 == "end" } {
	set ix1 [expr $n-1]
    }
    #---
    set dn [expr $ixn-$ix1+1]
    if { $ix1 == $ixn } {
	set fd end
    } else {
	set fd [expr $n-$dn]
    }
    foreach box [lreplace $boxes 0 0] {
	$box delete $ix1 $ixn
	$box see $ix1
    }
    set ixnn [expr $ixn+1]
    if { $ix1 == 0 } {
	# make sure new first point is not marked as starting a segment
	set bxseg [lindex $boxes end]
	$bxseg delete 0 ; $bxseg insert 0 ""
	if { [lsearch -exact $sel $ixnn] != -1 } { $bxseg selection set 0 }
    }
    $bxn selection clear $ix1 $ixn
    $bxn delete $fd end
    $bxn see $ix1
    foreach sl $sel {
	if { $sl > $ixn } { $bxn selection set [expr $sl-$dn] }
    }
    if { [set GMEd($wh,${whpt}s) [lreplace $GMEd($wh,${whpt}s) $ix1 $ixn]] \
	    == "" } {
	set GMEd($wh,SgSts) ""
    } else {
	set sgs "" ; incr ixn
	foreach sp $GMEd($wh,SgSts) {
	    if { $sp < $ix1 } {
		lappend sgs $sp
	    } elseif { $sp > $ixn && $sp > $dn } {
		lappend sgs [expr $sp-$dn]
	    }
	}
	set GMEd($wh,SgSts) $sgs
    }
    return
}

proc SplitPolyLine {wh how w ed ix} {
    # split a polyline into similar items
    #  $wh in {RT, TR, LN}
    #  $how in {sel, segm}: split using selected points or segment starters
    #  $w         window with poolyline to split
    #  $ed        set if $w is edit window
    #  $ix        index of item to split (may be -1 if $ed)
    # create a group with the new items
    # there is a local variable Datum not to be confused with the global one!
    global GMEd RTIdNumber RTStages RTWidth RTColour RTMBack TRName TRDatum \
	TRTPoints TRSegStarts TRWidth TRColour TRMBack LNName LNDatum LNPFrmt \
	LNLPoints LNSegStarts LNWidth LNColour LNMBack GRName MESS TXT

    set newixs ""
    if { $wh == "RT" } {
	# $how must be "sel"
	set box $w.fr.fr3.fr31.frbx.box
	if { [set sel [$box curselection]] == "" || \
		 [set wpns [$box get 0 end]] == "" } { bell ; return }
	if { $ed } {
	    set Stages [GMRTStages $w.fr.fr3.fr31.frbx]
	    foreach t "Width Colour MBack" {
		set $t $GMEd(RT,$t)
	    }
	    set orgname [$w.fr.fr1.id get]
	} else {
	    foreach t "IdNumber Stages Width Colour MBack" {
		set $t [set RT[set t]($ix)]
	    }
	    set orgname $IdNumber
	}
	set fs "IdNumber WPoints Stages Width Colour MBack"
	foreach p [MakeSplit [list $wpns $Stages] $sel] {
	    foreach "wps sts" $p {}
	    if { [llength $wps] > 1 } {
		set id [NewName RT]
		set data [FormData RT $fs \
			      [list $id $wps $sts $Width $Colour $MBack]]
		CreateItem RT $data
		SetWPRoute $id $wps
		lappend newits $id
	    }
	}
    } else {
	# TR or LN
	set pre [string index $wh 0]
	set fs "Name Datum ${pre}Points Width Colour MBack"
	if { $wh == "LN" } {
	    set line 1
	    lappend fs PFrmt
	} else { set line 0 }
	if { $ed } {
	    set ptsref "${pre}Ps"
	    foreach t "Datum $ptsref SgSts Width Colour MBack" {
		    set $t $GMEd($wh,$t)
	    }
	    if { $line } { set PFrmt $GMEd($wh,PFrmt) }
	    set SegStarts $SgSts
	    set orgname [$w.fr.fr1.id get]
	} else {
	    set ptsref "${pre}Points"
	    foreach t "Name Datum $ptsref SegStarts Width Colour MBack" {
		set $t [set $wh[set t]($ix)]
	    }		
	    if { $line } { set PFrmt $LNPFrmt($ix) }
	    set orgname $Name
	}
	if { [set pts [set $ptsref]] == "" } { bell ; return }
	if { $how == "sel" } {
	    set box $w.fr.fr3.frbx.bxn
	    set sel [$box curselection]
	    set segs 1
	    lappend fs SegStarts
	    set del 0
	    set sgstnxt [lindex $SegStarts 0]
	    set SegStarts [lreplace $SegStarts 0 0]
	} else {
	    set sel $SegStarts
	    set segs 0
	}
	if { $sel == "" } { bell ; return }

	foreach xps [MakeSplit [list $pts] $sel] {
	    set xps [lindex $xps 0]
	    if { [set nxps [llength $xps]] > 1 } {
		set name [NewName $wh]
		set dt [list $name $Datum $xps $Width $Colour $MBack]
		if { $line } { lappend dt $PFrmt }
		if { $segs } {
		    set xsegs ""
		    while { [set ns [expr $sgstnxt-$del]] < $nxps } {
			if { $ns > 0 } { lappend xsegs $ns }
			if { [set sgstnxt [lindex $SegStarts 0]] == "" } {
			    break
			}
			set SegStarts [lreplace $SegStarts 0 0]
		    }
		    lappend dt $xsegs
		    incr del $nxps
		}
		set data [FormData $wh $fs $dt]
		CreateItem $wh $data
		lappend newits $name
		if { $segs && $sgstnxt == "" } {
		    set fs [Delete $fs SegStarts]
		    set segs 0
		}
	    } elseif { $segs && $nxps == 1 } {
		if { $sgstnxt-$del == 0 } {
		    if { [set sgstnxt [lindex $SegStarts 0]] == "" } {
			set fs [Delete $fs SegStarts]
			set segs 0
		    } else { set SegStarts [lreplace $SegStarts 0 0] }
		}
		incr del
	    }
	}
    }
    if { $newits != "" } {
	set obs [format $MESS(obssplit) $wh $orgname]
	set grix [CreateGRFor split $obs [list [list $wh $newits]]]
	GMMessage [format $MESS(convres) $TXT(GR) $GRName($grix)]
    }
    return
}

proc GMPolyChange {wh how args} {
    # perform edit operations on polyline
    #  $wh in {TR, LN}
    #  $how is one of
    #      chh    chop head: delete all points from the beginning to first
    #              selected or only the first one
    #      cht    chop tail: delete all points from the last selected to end
    #              or only the last one
    #      incb    include a similar polyline at the beginning
    #      app    append a polyline (to end, of course)
    #      loop   add first point to end
    #      del    delete all selected points
    #      clear  delete all points
    #  $args has the name of the other polyline when $how is in {incb, app}
    global TRTPoints TRSegStarts TRDatum LNLPoints LNSegStarts LNDatum \
	    LNPFrmt GMEd EdWindow

    set whpt $GMEd($wh,ptname)
    set GMEd($wh,MapChg) 1
    set boxes $GMEd($wh,boxes)
    set bxn [lindex $boxes 0]
    set sel [lsort -integer -increasing [$bxn curselection]]
    set sel0 [lindex $sel 0] ; set sell [lindex $sel end]
    switch $how {
	chh {
	    if { [$bxn size] == 0 } { return }
	    if { $sel == "" } { set sel0 0 }
	    ChopPolyline $wh $whpt 0 $sel0
	}
	cht {
	    if { [$bxn size] == 0 } { return }
	    if { $sel == "" } { set sell end }
	    ChopPolyline $wh $whpt $sell end
	}
	incb {
	    set name [lindex [lindex $args 0] 0]
	    if { [set ix [IndexNamed $wh $name]] != -1 } {
		set pts [set ${wh}${whpt}oints($ix)]
		set sgs [set ${wh}SegStarts($ix)]
		set datum [set ${wh}Datum($ix)]
		foreach box $boxes { $box selection clear 0 end }
		set dboxes [lreplace $boxes 0 0]
		set npts [$bxn size] ; set ins 0
		switch $wh {
		    TR {
			if { $GMEd($wh,Datum) != $datum } {
			    set pts [ChangeTPsDatum $pts $dat $GMEd($wh,Datum)]
			}
			if { $npts == 0 } {
			    FillTPs $EdWindow($wh) $pts $sgs
			    set GMEd($wh,${whpt}s) $pts
			    set GMEd($wh,SgSts) $sgs
			    return
			}
			if { [set d [GMTRNewDate $pts $GMEd(TR,TPs)]] == -1 } {
			    return
			}
			set d [expr [lindex $d 1]- \
				[lindex [lindex $GMEd(TR,TPs) 0] 5]]
			foreach tp $pts {
			    incr npts
			    $bxn insert end [format "%4d." $npts]
			    foreach box $dboxes \
				    v [list [lindex $tp 4] \
				          [lindex $tp 2] [lindex $tp 3] \
					  [UserAltitude [lindex $tp 6]] \
					  [lindex $tp 7] \
					  ""] {
				$box insert $ins $v
			    }
			    incr ins
			}
			set l $pts
			foreach tp $GMEd(TR,TPs) {
			    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 $ins
			    .gmTR.fr.fr3.frbx.bxd insert $ins $nd
			    incr ins
			}
			set pts $l
		    }
		    LN {
			if { $GMEd($wh,Datum) != $datum } {
			    set pts [ChangeLPsDatum $pts $datum \
				    $GMEd($wh,Datum) $GMEd(LN,PFrmt)]
			} elseif { $LNPFrmt($ix) != $GMEd(LN,PFrmt) } {
			    set GMEd(temp) $GMEd($wh,Datum)
			    set pts [ChangeLPsPFormt $pts $GMEd(LN,PFrmt) \
				                    GMEd GMEd(temp)]
			}
			if { $npts == 0 } {
			    FillLPs $EdWindow($wh) $pts $sgs
			    set GMEd($wh,${whpt}s) $pts
			    set GMEd($wh,SgSts) $sgs
			    return
			}
			foreach lp $pts {
			    incr npts
			    $bxn insert end [format "%4d." $npts]
			    foreach box $dboxes \
				    v [list [lrange [lindex $lp 0] 2 end] \
					  [UserAltitude [lindex $lp 1]] \
					  ""] {
				$box insert $ins $v
			    }
			    incr ins
			}
			set pts [concat $pts $GMEd($wh,${whpt}s)]
		    }
		}
		set GMEd($wh,${whpt}s) $pts
		set usgs ""
		foreach sp $GMEd($wh,SgSts) {
		    lappend usgs [expr $sp+$ins]
		}
		set GMEd($wh,SgSts) [concat $sgs $usgs]
		set bxseg [lindex $boxes end]
		foreach sp $sgs {
		    $bxseg delete $sp
		    $bxseg insert $sp "@"
		}
	    }
	}
	app {
	    set name [lindex [lindex $args 0] 0]
	    if { [set ix [IndexNamed $wh $name]] != -1 } {
		set pts [set ${wh}${whpt}oints($ix)]
		set sgs [set ${wh}SegStarts($ix)]
		set datum [set ${wh}Datum($ix)]
		foreach box $boxes { $box selection clear 0 end }
		set dboxes [lreplace $boxes 0 0]
		set ins [set npts [$bxn size]]
		switch $wh {
		    TR {
			if { $GMEd($wh,Datum) != $datum } {
			    set pts [ChangeTPsDatum $pts $dat $GMEd($wh,Datum)]
			}
			if { $npts == 0 } {
			    FillTPs $EdWindow($wh) $pts $sgs
			    set GMEd($wh,${whpt}s) $pts
			    set GMEd($wh,SgSts) $sgs
			    return
			}
			if { [set d [GMTRNewDate $GMEd(TR,TPs) $pts]] == -1 } {
			    return
			}
			set d [expr [lindex $d 1]- \
				[lindex [lindex $pts 0] 5]]

			set l $GMEd(TR,TPs)
			foreach tp $pts {
			    incr ins
			    set ns [expr [lindex $tp 5]+$d]
			    set nd [DateFromSecs $ns]
			    lappend l [lreplace $tp 4 5 $nd $ns]
			    foreach box $boxes \
				    v [list [format "%4d." $ins] \
				          $nd \
				          [lindex $tp 2] [lindex $tp 3] \
					  [UserAltitude [lindex $tp 6]] \
					  [lindex $tp 7] \
					  ""] {
				$box insert end $v
			    }
			}
			set GMEd(TR,TPs) $l
		    }
		    LN {
			if { $GMEd($wh,Datum) != $datum } {
			    set pts [ChangeLPsDatum $pts $datum \
				    $GMEd($wh,Datum) $GMEd(LN,PFrmt)]
			} elseif { $LNPFrmt($ix) != $GMEd(LN,PFrmt) } {
			    set GMEd(temp) $GMEd($wh,Datum)
			    set pts [ChangeLPsPFormt $pts $GMEd(LN,PFrmt) \
				                    GMEd GMEd(temp)]
			}
			if { $npts == 0 } {
			    FillLPs $EdWindow($wh) $pts $sgs
			    set GMEd($wh,${whpt}s) $pts
			    set GMEd($wh,SgSts) $sgs
			    return
			}
			foreach lp $pts {
			    incr ins
			    foreach box $boxes \
				    v [list [format "%4d." $ins] \
					   [lrange [lindex $lp 0] 2 end] \
					   [UserAltitude [lindex $lp 1]] \
					   ""] {
				$box insert end $v
			    }
			}
			set GMEd(LN,LPs) [concat $GMEd(LN,LPs) $pts]
		    }
		}
		set bxseg [lindex $boxes end]
		foreach sp $sgs {
		    lappend GMEd($wh,SgSts) [set i [expr $sp+$npts]]
		    $bxseg delete $i
		    $bxseg insert $i "@"
		}
	    }
	}
	del {
	    # MB contribution
	    if { [$bxn size] == 0 } { return }
	    if { $sel == "" } { set sel 0 }
	    # MF change: $sel is now a list
	    foreach sl [lsort -integer -decreasing $sel] {
		ChopPolyline $wh $whpt $sl $sl
	    }
 	}
	clear {
	    if { [$bxn size] == 0 } { return }
	    ChopPolyline $wh $whpt 0 end
	}
    }
    return
}

proc GMButton {wh button} {
    # callback to button when editing item of type $wh (in $TYPES or "LAP")
    #  $button in {cancel, create, change, revert, forget}
    global Storage EdWindow WPRoute RTIdNumber RTWPoints \
	    GRConts GMEd MESS TXT MapMakingRT DataIndex

    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 {
	    if { $MapMakingRT && $wh == "RT" } {
		MapFinishRTLastWP
	    }
	    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 { $GMEd($wh,Displ) } { PutMap $wh $ix }
		    destroy $w
		} else {
		    GMMessage $MESS(idinuse)
		    focus $w.fr.fr1.id
		    return
		}
	    }
	}
	change {
	                           # $GMEd($wh,Index) assumed > -1
	    if { $MapMakingRT && $wh == "RT" } {
		MapFinishRTLastWP
	    }
	    set data [GM${wh}Check]
	    if { $data != "nil" } {
		set ix $GMEd($wh,Index)
		set oldname [set [set ids]($ix)]
		set newname [lindex $data 0]
		set diffname [string compare $oldname $newname]
		set mapped [lindex $GMEd($wh,Data) end]
		set tomap $GMEd($wh,Displ)
		if { $diffname } {
		    if { [CheckArrayElement $ids $newname] } {
			GMMessage $MESS(idinuse)
			focus $w.fr.fr1.id
			return
		    }
		}
		if { $wh == "RT" } {
		    set oldwps $GMEd(RT,WPoints)
		    set rtwps [lindex $data 3]
		    set newwps [Subtract $rtwps $oldwps]
		    set keptwps [Subtract $rtwps $newwps]
		    set delwps [Subtract $oldwps $keptwps]
		    UnsetWPRoute $oldname $delwps
		    if { $diffname } {
			RenameWPRoute $oldname $newname $keptwps
			SetWPRoute $newname $newwps
		    } else {
			SetWPRoute $oldname $newwps
		    }
		}
		if { $mapped } {
		    if { $tomap } {
			SetItem $wh $ix $data
			if { $GMEd($wh,MapChg) } {
			    MoveOnMap $wh $ix $oldname $diffname $newname
			}
		    } elseif { [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]
				if { [lsearch -exact $es $newname] == -1 \
				    && [set j [lsearch -exact $es $oldname]] \
					 != -1 } {
				    set GRConts($grix) [lreplace \
					    $GRConts($grix) $i $i \
					    [list $wh [lreplace $es $j $j \
					               $newname]]]
				    UpdateItemWindows GR $grix
				}
				break
			    }
			    incr i
			}
		    }
		}
		if { $wh == "WP" } {
		    if { $GMEd(WP,MapChg) } {
			ChangeWPInRTWindows $oldname $newname 1
		    } elseif { $diffname } {
			ChangeWPInRTWindows $oldname $newname 0
		    }
		}
		destroy $w
	    }
	}
	revert {
	    if { [GMConfirm $MESS(askrevert)] } {
		Revert$wh
	    }
	}
	forget {
	                           # $GMEd($wh,Index) assumed > -1
	    if { [GMConfirm [format $MESS(askforget) $TXT(name$wh)]] && \
		 [Forget $wh $GMEd($wh,Index)] } { destroy $w }
	}
    }
    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 GMEd MESS

    if { $GMEd(TR,Datum) == $datum || \
	    ( [lindex $GMEd(TR,TPs) 200] != "" && \
	    ! [GMConfirm $MESS(timeconsmg)] ) } { return }
    SetCursor . watch
    set GMEd(TR,TPs) [ChangeTPsDatum $GMEd(TR,TPs) $GMEd(TR,Datum) $datum]
    set sel [.gmTR.fr.fr3.frbx.bx$n curselection]
    foreach n "n d lat long" {
	.gmTR.fr.fr3.frbx.bx$n delete 0 end
    }
    FillTPs .gmTR $GMEd(TR,TPs) $GMEd(TR,SgSts)
    foreach sl $sel {
	foreach n "n d lat long" {
	    .gmTR.fr.fr3.frbx.bx$n selection set $sl
	}
    }
    set GMEd(TR,Datum) $datum
    ResetCursor .
    return
}

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

    set i 0 ; set nxt [lindex $sgsts 0]
    foreach tp $tps {
	if { $nxt == $i } {
	    set seg "@"
	    set sgsts [lreplace $sgsts 0 0]
	    set nxt [lindex $sgsts 0]
	} else { set seg "" }
	incr i
	$w.fr.fr3.frbx.bxn insert end [format "%4d." $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 [UserAltitude [lindex $tp 6]]
	$w.fr.fr3.frbx.bxdep insert end [lindex $tp 7]
	$w.fr.fr3.frbx.bxseg insert end $seg
    }
    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 GMEd TRTPoints CREATIONDATE PVTPosns

    switch $wh {
	TR {
	    set dat [$w.fr.frd.datum cget -text]
	    if { $w == ".gmTR" } {
		set p [lindex $GMEd(TR,TPs) $ix]
	    } else {
		set ixt [IndexNamed TR [$w.fr.fr1.id get]]
		set p [lindex $TRTPoints($ixt) $ix]
	    }
	    if { $p == "" } { return }
	    set alt [lindex $p 6]
	}
	PVT {
	    if { [set p [lindex $PVTPosns $ix]] == "" } { 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 posns dproc dvar dvref st ed chgvorp} {
    # show position format menu, position and datum under parent window $w
    #  $pformt is position format
    #  $posns is a list of position representations
    #  $dproc is proc to be called when changing datum (if editing is allowed)
    #  $dvar is name of global variable or array with datum if editing,
    #   or empty string if not editing
    #  $dvref is the reference to the datum variable or array(element) to
    #   be set if editing, or the datum name if not
    #  $st is state of editable widgets
    #  $ed is 1 if edition is allowed
    #  $chgvorp is either "nil" or:
    #     - if there is a single position, the 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
    #     - else, "=PREFIX" describing global variables used in the same
    #     way for each position; each name has the prefix followed by the
    #     number from 1 of the position
    # frames $w.frp and $w.frd are created that should be packed by the caller
    # there will be frames $w.frp.frp1, $w.frp.frp2, ... with the widgets for
    #  each position
    # widgets created here are used elsewhere
    global TXT DATUMWIDTH

    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 $dvref $w.frp \
	    $chgvorp $st
    }
    pack $w.frp.pfmt -side left -padx 3
    FillPos $w.frp $pformt $posns $st $chgvorp

    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 $dvref] -textvariable $dvref \
		-width $DATUMWIDTH
    } else {
	label $w.frd.datum -text $dvref \
		-width $DATUMWIDTH
    }
    pack $w.frd.dttitle $w.frd.datum -side left -padx 3
    return
}

## opening and updating edit/show windows

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

    if { $index == -1 } { GMMessage $MESS(notlisted) ; return "" }
    set w $EdWindow($wh)
    if { [winfo exists $w] } {
	if { $GMEd($wh,Index) == $index } {
	    Raise $w ; bell
	    return ""
	} else { set w [$Proc($wh) $index "" [ItemData $wh $index]] }
    } elseif { $wh != "LAP" } {
	set w [$Proc($wh) $index "change revert create forget cancel" \
		[ItemData $wh $index]]
    } else {
	# LAPs cannot be created
	set w [$Proc($wh) $index "change revert forget cancel" \
		[ItemData $wh $index]]
    }
    return $w
}

proc OpenSelItems {wh} {
    # select and open items of given type
    #  $wh in $TYPES

    foreach ix [ChooseItems $wh] { OpenItem $wh $ix }
    return
}

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

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

proc UpdateWPsInWindows {wh ixs oldnames newnames} {
    # update edit/show windows for items of type $wh in {RT, GR} after
    #  a renaming of WPs and corresponding changes in the data-base
    #   $ixs is list of indices of the items in show windows
    # a WP name in a RT may occur several times
    # update edit window if the item there was changed or is not yet defined
    global EdWindow GMEd RTWPoints GRConts

    if { [winfo exists [set w $EdWindow($wh)]] } {
	if { [set ix $GMEd($wh,Index)] == -1 || \
		 [lsearch -exact $ixs $ix] != -1 } {
	    if { $wh == "RT" } {
		# this depends on Storage(RT)
		set dwps [lindex $GMEd(RT,Data) 3]
		set frbx .gmRT.fr.fr3.fr31.frbx
		set wwps [$frbx.box get 0 end]
	    } else {
		# this depends on Storage(GR)
		set cs [lindex $GMEd(GR,Data) 2]
		foreach {ics dwps} [GRWPNames $cs] {}
		set wwps [GMGRCollectWPNames $w]
	    }
	    if { $dwps != {} } {
		foreach {chg dwps} \
		    [ListReplace $dwps $oldnames $newnames] {}
		if { $chg } {
		    if { $wh == "RT" } {
			set GMEd(RT,Data) [lreplace $GMEd(RT,Data) 3 3 $dwps]
		    } else {
			set dwps [lsort -dictionary $dwps]
			set cs [lreplace $cs $ics $ics [list WP $dwps]]
			set GMEd(GR,Data) [lreplace $GMEd(GR,Data) 2 2 $cs]
		    }
		}
	    }
	    if { $wwps != {} } {
		foreach {chg wwps} \
		    [ListReplace $wwps $oldnames $newnames] {}
		if { $chg } { ReplaceWPsInWindow $wh $w $wwps }
	    }
	}
    }
    # update show windows
    foreach ix $ixs {
	set w .gm${wh}sh$ix
	if { [winfo exists $w] } {
	    if { $wh == "RT" } {
		set newwps $RTWPoints($ix)
	    } else {
		set newwps [lindex [GRWPNames $GRConts($ix)] 1]
	    }
	    ReplaceWPsInWindow $wh $w $newwps
	}
    }
    return
}

proc ReplaceWPsInWindow {wh w names} {
    # replace all WPs in edit/show window of item of type $wh in {RT, GR}
    #  by the given ones
    # do nothing if $names is empty
    global TXT

    if { $names == {} || ! [winfo exists $w] } { return }
    if { $wh == "RT" } {
	set frbx $w.fr.fr3.fr31.frbx
	$frbx.box delete 0 end
	foreach name $names {
	    $frbx.box insert end $name
	}
	foreach b "xn xd xb xda xsc xsl" {
	    $frbx.b$b selection clear 0 end
	}
	return
    }
    # GR
    set wptxt $TXT(WP)
    set frbx $w.fr.fr3.frbx
    foreach b {bxn bxw box} { $frbx.$b selection clear 0 end }
    # clear old WPs
    set is [lsort -integer -decreasing \
		[lsearch -exact -all [$frbx.bxw get 0 end] $wptxt]]
    foreach i $is {
	$frbx.bxn delete end end
	$frbx.bxw delete $i $i
	$frbx.box delete $i $i
    }
    # insert new
    set n [$frbx.box size]
    foreach name [lsort -dictionary -decreasing $names] {
	$frbx.bxn insert end [format "%3d." [incr n]]
	$frbx.bxw insert 0 $wptxt
	$frbx.box insert 0 $name
    }
    return
}

proc CloseItemWindows {wh ix} {
    # close edit/show windows for item of type $wh and index $ix that has
    #  been forgotten
    global EdWindow Proc GMEd

    set w $EdWindow($wh)
    if { [winfo exists $w] && $GMEd($wh,Index) == $ix } {
	destroy $w
    }
    if { [winfo exists .gm${wh}sh$ix] } {
	destroy .gm${wh}sh$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.frdw.displayed configure -state normal
	$w.fr.frdw.displayed $sel
	$w.fr.frdw.displayed configure -state disabled
    }
    return
}

### displaying hidden information

proc ShowHiddenData {wh hidden} {
    # display hidden information for an item of type $wh (in {WP, TR})
    # assume there is a proc HiddenData with arguments $wh and $hidden
    #  that returns list of pairs with name of field and its value (in
    #  a suitable form for displaying)
    global TXT DPOSX DPOSY COLOUR

    if { [set fvs [HiddenData $wh $hidden]] == "" } { bell ; return }
    set w .shidden
    if { [winfo exists $w] } { destroy $w }

    GMToplevel $w hiddendata +$DPOSX+$DPOSY . \
        [list WM_DELETE_WINDOW "destroy $w"] \
        [list <Key-Return> "destroy $w"]

    frame $w.fr -relief flat -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.tit -text $TXT(hiddendata)

    frame $w.fr.fri -relief flat -borderwidth 0
    set r 0
    foreach p $fvs {
	foreach x $p c "0 1" {
	    label $w.fr.fri.x${r}_$c -text $x -anchor w \
		    -width [expr 2+[string length $x]]
	    grid $w.fr.fri.x${r}_$c -row $r -column $c -sticky w
	}
	incr r
    }
    button $w.fr.ok -text $TXT(ok) -command "destroy $w"

    foreach x "tit fri ok" { grid $w.fr.$x -pady 5 }
    grid $w.fr
    update idletasks
    return
}

### creating WP at given distante and bearing from another one

proc CreateWPAtDistBear {ix} {
    # create WP at given distante (in user-selected unit) and bearing from
    #  WP with index $ix
    # fail if the WP edit window exists for a different WP
    global EdWindow GMEd WPatdist WPatbear WPPosn WPDatum WPPFrmt TXT MESS \
	    INVTXT CREATIONDATE DTUNIT DSCALE

    if { [winfo exists $EdWindow(WP)] } {
	if { $GMEd(WP,Index) != $ix } { bell ; return }
	# valid coordinates?
	set p [PosnGetCheck .gmWP.fr.frp.frp1 $GMEd(WP,Datum) GMMessage \
		   ChangedPosn]
	if { $p == "nil" } { return }
	set pformt $INVTXT([.gmWP.fr.frp.pfmt cget -text])
	set datum $GMEd(WP,Datum)
	set ed 1
    } else {
	set p $WPPosn($ix) ; set datum $WPDatum($ix)
	set pformt $WPPFrmt($ix)
	set ed 0
    }
    # get distance and bearing
    while 1 {
	if { ! [GMChooseParams $TXT(newWPatdb) "WPatdist WPatbear" \
		    [list "=$TXT(distance) ($DTUNIT)" "=$TXT(azimuth)"]] } {
	    return
	}
	if { [CheckFloat GMMessage $WPatdist] && \
		[CheckNumber GMMessage $WPatbear] } {
	    if { $WPatbear >= 360 } {
		GMMessage $MESS(badangle)
		continue
	    }
	    break
	}
    }
    foreach "lat long" [CoordsAtDistBear $p [expr $WPatdist/$DSCALE] \
			    $WPatbear $datum] {}
    foreach "p pformt datum" \
	[FormatPosition $lat $long $datum $pformt ""] { break }
    if { $ed } { destroy $EdWindow(WP) }
    set opts "create revert cancel"
    if { $CREATIONDATE } {
	GMWPoint -1 $opts [FormData WP "PFrmt Posn Datum Date" \
		            [list $pformt $p $datum [Now]]]
    } else {
	GMWPoint -1 $opts [FormData WP "Commt PFrmt Posn Datum" \
		            [list [DateCommt [Now]] $pformt $p $datum]]
    }
    return
}

### utilities for dealing with map backgrounds in item edit/show windows

proc CreateMBackWidgets {wh fr mbak ed} {
    # create widgets for showing/editing map background associated to item
    #  $wh in {WP, RT, TR, LN}
    #  $fr frame to be populated
    #  $mbak current map background name
    #  $ed flag set if editing is enabled
    global TXT COMMENTWIDTH

    label $fr.tit -text "$TXT(mbaktoload):" -width 25
    if { $ed } {
	menubutton $fr.mb -textvariable GMEd($wh,MBack) -menu $fr.mb.m \
	    -width $COMMENTWIDTH -relief raised
	menu $fr.mb.m -postcommand [list FillDefsMenu backgrnd $fr.mb.m \
				[list ChangeMBack $wh] "($TXT(none))" "---"]
    } else {
	menubutton $fr.mb -text $mbak -state disabled -width $COMMENTWIDTH \
	     -relief raised
    }
    pack $fr.tit $fr.mb -side left
    return
}

proc ChangeMBack {wh mbak args} {
    # called after a new map background was selected for item
    #  $args not used (but needed because of callback)
    global TXT GMEd

    if { $mbak == "($TXT(none))" } { set mbak "" }
    set GMEd($wh,MBack) $mbak
    return
}
