#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2001 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: search.tcl
#  Last change:  19 December 2001
#

proc Search {} {
    # create dialog for searching items
    global TempSearch PositionFormat SDatum Datum SearchWidgets SearchPattern \
	    SearchDistance SearchWPName SearchWPSymbol SearchRTWP \
	    SearchDate SearchSpec SearchDomain SearchDomGR \
	    WPPosn WPDatum DEFAULTSYMBOL DPOSX DPOSY COLOUR TXT MESS TYPES \
	    ICONWIDTH ICONHEIGHT SYMBOLIMAGE NAMEWIDTH DSCALE AzimuthDegrees \
	    YEAR0 MONTHNAMES MAXMENUITEMS INVTXT

    # absolute paths for this window used elsewhere
    set w .gmsearch
    if { [winfo exists $w] } { Raise $w ; bell ; return }

    set SDatum $Datum
    set SearchWPSymbol $DEFAULTSYMBOL
    set waitsearch 1

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "set TempSearch cnc ; set waitsearch 0"
    wm title $w "GPS Manager: $TXT(search)"
    wm transient $w .
    wm geometry $w +$DPOSX+$DPOSY
    bind $w <Key-Return> "set TempSearch ok ; set waitsearch 0"

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

    frame $w.fr.wh -relief flat -borderwidth 0
    label $w.fr.wh.tit -text $TXT(what):
    set pre $w.fr.wh.tit
    foreach wh $TYPES {
	set SearchSpec($wh) 0
	checkbutton $w.fr.wh.b$wh -text $TXT(name$wh) -anchor w \
		-variable SearchSpec($wh) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check) -command "SearchToggle $wh"
	eval pack $pre $w.fr.wh.b$wh -side left -padx 2
	set $pre ""
    }

    frame $w.fr.dom -relief flat -borderwidth 0
    set SearchDomain Data
    label $w.fr.dom.tit -text $TXT(where):
    frame $w.fr.dom.db -relief sunken -borderwidth 1
    checkbutton $w.fr.dom.db.b -text $TXT(database) -anchor w \
		-variable SearchDomain -onvalue Data -offvalue GR \
		-selectcolor $COLOUR(check) -command SearchToggleDomain
    frame $w.fr.dom.gr -relief sunken -borderwidth 1
    checkbutton $w.fr.dom.gr.b -text $TXT(nameGR)(s) -anchor w \
	    -variable SearchDomain -onvalue GR -offvalue Data \
	    -selectcolor $COLOUR(check) -command SearchToggleDomain
    menubutton $w.fr.dom.gr.mb -relief raised -anchor w -width 15 \
	    -direction below -menu $w.fr.dom.gr.mb.m
    menu $w.fr.dom.gr.mb.m -tearoff 0

    frame $w.fr.strs -relief flat -borderwidth 0
    foreach s "name cmmt rmrk" {
	set SearchSpec($s) 0
	frame $w.fr.strs.f$s
	checkbutton $w.fr.strs.f$s.b -text $TXT($s) -anchor w -width 11 \
		-variable SearchSpec($s) -onvalue 1 -offvalue 0 \
		-selectcolor $COLOUR(check)
	entry $w.fr.strs.f$s.e -width 30 -exportselection 1
	pack $w.fr.strs.f$s.b $w.fr.strs.f$s.e -side left
	pack $w.fr.strs.f$s
    }

    frame $w.fr.fWPTR -relief flat -borderwidth 0
    frame $w.fr.fWPTR.dist -relief flat -borderwidth 0
    label $w.fr.fWPTR.dist.tit -text $TXT(distance)
    frame $w.fr.fWPTR.dist.wpp -relief flat -borderwidth 0
    set SearchDistance(how) fromWP
    frame $w.fr.fWPTR.dist.wpp.wp -relief flat -borderwidth 0
    checkbutton $w.fr.fWPTR.dist.wpp.wp.b -text $TXT(fromWP): -width 13 \
	    -variable SearchDistance(how) -onvalue fromWP -offvalue fromPos \
	    -selectcolor $COLOUR(check) -anchor w
    set SearchWPName ""
    menubutton $w.fr.fWPTR.dist.wpp.wp.it -textvariable SearchWPName \
	    -relief raised -direction below -width $NAMEWIDTH \
	    -menu $w.fr.fWPTR.dist.wpp.wp.it.m
    FillItemsMenu $w.fr.fWPTR.dist.wpp.wp.it.m it WP SearchWP
    frame $w.fr.fWPTR.dist.wpp.p -relief flat -borderwidth 0
    checkbutton $w.fr.fWPTR.dist.wpp.p.b -text $TXT(fromPos): -width 13 \
	    -variable SearchDistance(how) -onvalue fromPos -offvalue fromWP \
	    -selectcolor $COLOUR(check) -anchor w
    frame $w.fr.fWPTR.dist.wpp.p.p -relief sunken -borderwidth 1
    ShowPosnDatum $w.fr.fWPTR.dist.wpp.p.p $PositionFormat "" \
	    SearchChangeDatum SDatum normal 1 nil
    frame $w.fr.fWPTR.dir -relief flat -borderwidth 0
    label $w.fr.fWPTR.dir.tit -text $TXT(azimuth):
    set SearchDistance(bearing) ""
    menubutton $w.fr.fWPTR.dir.dm -textvariable SearchDistance(bearing) \
	    -relief raised -direction below -width 6 \
	    -menu $w.fr.fWPTR.dir.dm.m
    menu $w.fr.fWPTR.dir.dm.m -tearoff 0
    foreach b "N NE E SE S SW W NW" {
	$w.fr.fWPTR.dir.dm.m add command -label $b \
		-command "set SearchDistance(bearing) $b ; \
		          $w.fr.fWPTR.dir.ang configure -state normal"
    }
    $w.fr.fWPTR.dir.dm.m add command -label "($TXT(any))" \
	    -command "set SearchDistance(bearing) {} ; \
	              $w.fr.fWPTR.dir.ang configure -state disabled"
    label $w.fr.fWPTR.dir.angtit -text $TXT(opening):
    set SearchDistance(opendef) 45
    set SearchDistance(open) $SearchDistance(opendef)
    entry $w.fr.fWPTR.dir.ang -textvariable SearchDistance(open) \
	    -width 4 -exportselection 1 -state disabled
    label $w.fr.fWPTR.dir.angdtit -text "$TXT(degrees) \($TXT(suggested):"
    label $w.fr.fWPTR.dir.angd -textvariable SearchDistance(opendef)
    label $w.fr.fWPTR.dir.angdtit2 -text "\)"
    frame $w.fr.fWPTR.its -relief flat -borderwidth 0
    set SearchSpec(dist) 0
    checkbutton $w.fr.fWPTR.its.tit -text "$TXT(nameWP), $TXT(nameTR):" \
	    -variable SearchSpec(dist) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -anchor w -state disabled
    frame $w.fr.fWPTR.its.spc -relief flat -borderwidth 0
    frame $w.fr.fWPTR.its.spc.wt -relief sunken -borderwidth 1
    set SearchDistance(range) within
    checkbutton $w.fr.fWPTR.its.spc.wt.b -text $TXT(within): -width 15 \
	    -variable SearchDistance(range) -onvalue within -offvalue between \
	    -selectcolor $COLOUR(check) -anchor w -command SearchBearingAngle
    set SearchDistance(dist) 10
    entry $w.fr.fWPTR.its.spc.wt.d -textvariable SearchDistance(dist) \
	    -width 5 -exportselection 1
    frame $w.fr.fWPTR.its.spc.bt -relief sunken -borderwidth 1
    set SearchDistance(from) 10
    set SearchDistance(to) 20
    checkbutton $w.fr.fWPTR.its.spc.bt.b -text $TXT(between): -width 15 \
	    -variable SearchDistance(range) -onvalue between -offvalue within \
	    -selectcolor $COLOUR(check) -anchor w -command SearchBearingAngle
    entry $w.fr.fWPTR.its.spc.bt.d1 -textvariable SearchDistance(from) \
	    -width 5 -exportselection 1
    bind $w.fr.fWPTR.its.spc.bt.d1 <Leave> { SearchBearingAngle }
    label $w.fr.fWPTR.its.spc.bt.sep -text "-"
    entry $w.fr.fWPTR.its.spc.bt.d2 -textvariable SearchDistance(to) \
	    -width 5 -exportselection 1
    bind $w.fr.fWPTR.its.spc.bt.d2 <Leave> { SearchBearingAngle }

    frame $w.fr.fWP -relief flat -borderwidth 0
    set SearchSpec(symbol) 0
    checkbutton $w.fr.fWP.b -text "$TXT(nameWP) $TXT(with)" -anchor w \
	    -variable SearchSpec(symbol) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -state disabled
    menubutton $w.fr.fWP.symb -text $TXT(symbol) -relief raised \
	    -direction below -menu $w.fr.fWP.symb.m
    set mw $w.fr.fWP.symb.m
    menu $mw -tearoff 0
    FillSymbolsMenu $mw SearchChangeSymbol
    canvas $w.fr.fWP.symbim -width $ICONWIDTH -height $ICONHEIGHT
    $w.fr.fWP.symbim create image 1 1 -anchor nw \
	    -image $SYMBOLIMAGE($SearchWPSymbol)
    label $w.fr.fWP.symbname -text $TXT(SY$SearchWPSymbol)

    frame $w.fr.fRT -relief flat -borderwidth 0
    set SearchSpec(RTWP) 0
    checkbutton $w.fr.fRT.b -text "$TXT(nameRT) $TXT(with)" -anchor w \
	    -variable SearchSpec(RTWP) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -state disabled
    set SearchRTWP ""
    menubutton $w.fr.fRT.wp -textvariable SearchRTWP \
	    -relief raised -direction below -width $NAMEWIDTH \
	    -menu $w.fr.fRT.wp.m
    FillItemsMenu $w.fr.fRT.wp.m wp WP SearchRTWP

    frame $w.fr.fTR -relief flat -borderwidth 0
    set SearchSpec(date) 0
    checkbutton $w.fr.fTR.b -text "$TXT(nameTR) $TXT(started)" -anchor w \
	    -variable SearchSpec(date) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -state disabled
    for { set ds "" ; set d 1 } { $d < 32 } { incr d } {
	lappend ds $d
    }
    scan [Today YYYYMMDD] %d cy
    for { set ys "" ; set y $YEAR0 } { $y <= $cy } { incr y } {
	lappend ys $y
    }
    foreach de "d m y" vs [list $ds $MONTHNAMES $ys] {
	set SearchDate($de) ""
	menubutton $w.fr.fTR.$de -textvariable SearchDate($de) \
		-relief raised -direction below -width $NAMEWIDTH \
		-menu $w.fr.fTR.$de.m
	menu $w.fr.fTR.$de.m -tearoff 0
	set mw $w.fr.fTR.$de.m
	$w.fr.fTR.$de.m add command -label "($TXT(any))" \
		-command "set SearchDate($de) {}"
	set n 1 ; set m 0
	foreach v $vs {
	    if { $n > $MAXMENUITEMS } {
		$mw add cascade -label "$TXT(more) ..." -menu $mw.m$m
		set mw $mw.m$m ; menu $mw -tearoff 0
		set n 0 ; incr m
	    }
	    $mw add command -label $v -command "set SearchDate($de) $v"
	    incr n
	}
    }

    set SearchWidgets(WP) "$w.fr.fWP.b TR/$w.fr.fWPTR.its.tit"
    set SearchWidgets(RT) "$w.fr.fRT.b"
    set SearchWidgets(TR) "$w.fr.fTR.b WP/$w.fr.fWPTR.its.tit"
    set SearchWidgets(GR) ""

    frame $w.fr.frbt -relief flat -borderwidth 0
    button $w.fr.frbt.ok -text $TXT(search) \
	    -command "set TempSearch ok ; set waitsearch 0 ; \
	              $w.fr.frbt.ok configure -state normal"
    button $w.fr.frbt.cnc -text $TXT(cancel) \
	    -command "set TempSearch cnc ; set waitsearch 0"

    pack $w.fr.dom.db.b
    pack $w.fr.dom.gr.b $w.fr.dom.gr.mb -side left -padx 1
    pack $w.fr.dom.tit $w.fr.dom.db $w.fr.dom.gr -side left -padx 3

    pack $w.fr.fWPTR.dist.wpp.wp.b $w.fr.fWPTR.dist.wpp.wp.it -side left
    pack $w.fr.fWPTR.dist.wpp.p.p.frp $w.fr.fWPTR.dist.wpp.p.p.frd -side top
    pack $w.fr.fWPTR.dist.wpp.p.b $w.fr.fWPTR.dist.wpp.p.p -side left
    pack $w.fr.fWPTR.dist.wpp.wp $w.fr.fWPTR.dist.wpp.p -side top -anchor w
    pack $w.fr.fWPTR.dist.tit $w.fr.fWPTR.dist.wpp -side left -padx 2
    pack $w.fr.fWPTR.dir.tit $w.fr.fWPTR.dir.dm $w.fr.fWPTR.dir.angtit \
	    $w.fr.fWPTR.dir.ang $w.fr.fWPTR.dir.angdtit \
	    $w.fr.fWPTR.dir.angd $w.fr.fWPTR.dir.angdtit2 -side left -padx 2
    pack $w.fr.fWPTR.its.spc.wt.b $w.fr.fWPTR.its.spc.wt.d -side left -padx 2
    pack $w.fr.fWPTR.its.spc.bt.b $w.fr.fWPTR.its.spc.bt.d1 \
	    $w.fr.fWPTR.its.spc.bt.sep $w.fr.fWPTR.its.spc.bt.d2 -side left \
	    -padx 2
    pack $w.fr.fWPTR.its.spc.wt $w.fr.fWPTR.its.spc.bt -side top -pady 2 \
	    -anchor w
    pack $w.fr.fWPTR.its.tit $w.fr.fWPTR.its.spc -side left -padx 2
    pack $w.fr.fWPTR.dist $w.fr.fWPTR.dir $w.fr.fWPTR.its -side top -anchor w

    pack $w.fr.fWP.b $w.fr.fWP.symb $w.fr.fWP.symbim $w.fr.fWP.symbname \
	    -side left -padx 2

    pack $w.fr.fRT.b $w.fr.fRT.wp -side left -padx 2

    pack $w.fr.fTR.b $w.fr.fTR.d $w.fr.fTR.m $w.fr.fTR.y -side left -padx 2

    pack $w.fr.frbt.ok $w.fr.frbt.cnc -side left -padx 5
    pack $w.fr.tit $w.fr.wh $w.fr.dom $w.fr.strs -side top -pady 5
    pack $w.fr.fWPTR $w.fr.fWP $w.fr.fRT $w.fr.fTR -side top -pady 5 -anchor w
    pack $w.fr.frbt -side top -pady 5
    pack $w.fr

    update idletasks
    grab $w
    # RaiseWindow $w
    while 1 {
	tkwait variable waitsearch

	switch $TempSearch {
	    ""  { }
	    cnc { 
		destroy $w
		update idletasks
		return
	    }
	    ok {
		set whs ""
		foreach wh $TYPES {
		    if { $SearchSpec($wh) } { lappend whs $wh }
		}
		if { "$whs" == "" } {
		    GMMessage $MESS(mustchoose1type)
		    continue
		}
		foreach s "name cmmt rmrk" {
		    if { $SearchSpec($s) } {
			set SearchPattern($s) [$w.fr.strs.f$s.e get]
		    }
		}
		if { $SearchSpec(dist) } {
		    switch $SearchDistance(how) {
			fromWP {
			    set ix [IndexNamed WP $SearchWPName]
			    if { $ix != -1 } {
				set SearchDistance(pos) $WPPosn($ix)
				set Sdatum $WPDatum($ix)
			    } else {
				GMMessage $MESS(badWP)
				continue
			    }
			}
			fromPos {
			    set z $w.fr.fWPTR.dist.wpp.p.p.frp
			    set p [PosnGetCheck $z $SDatum GMMessage nil]
			    if { "$p" != "nil" } {
				set SearchDistance(pos) $p
				set SearchDistance(pformt) \
					$INVTXT([$z.pfmt cget -text])
			    } else { continue }
			}
		    }
		    if { "$SearchDistance(bearing)" != "" } {
			if { ! [CheckNumber GMMessage \
				$SearchDistance(open)] } { continue }
			if { $SearchDistance(open)>359 || \
				$SearchDistance(open)==0 } {
			    GMMessage $MESS(badangle)
			    continue
			}
			set SearchDistance(hangle) [expr \
				$SearchDistance(open)/2.0]
			set SearchDistance(azimuth) \
				$AzimuthDegrees($SearchDistance(bearing))
		    } else {
			set SearchDistance(hangle) 180
			set SearchDistance(azimuth) 0
		    }
		    switch $SearchDistance(range) {
			within {
			    if { ! [CheckFloat GMMessage \
				    $SearchDistance(dist)] } { continue }
			    set SearchDistance(low) 0
			    set SearchDistance(up) \
				    [expr $DSCALE*$SearchDistance(dist)]
			}
			between {
			    if { ! [CheckFloat GMMessage \
				    $SearchDistance(from)] || ! [CheckFloat \
				    GMMessage $SearchDistance(to)]} { 
				continue
			    }
			    set SearchDistance(low) \
				    [expr $DSCALE*$SearchDistance(from)]
			    set SearchDistance(up) \
				    [expr $DSCALE*$SearchDistance(to)]
			}
		    }
		}
		if { $SearchSpec(RTWP) && "$SearchRTWP" == "" } {
		    GMMessage $MESS(badWP)
		    continue
		}
		if { [SearchFor $whs] } {
		    destroy $w
		    return
		}
	    }
	}
    }
}

proc SearchDescription {} {
    # return a text describing the search constraints which are described
    #  by global variables (see proc SearchForAny) and the state of the
    #  search window
    global SearchDomain SearchDomGR GRName SearchSpec SearchPattern \
	    SearchDistance SearchWPName SDatum SearchWPSymbol SearchRTWP \
	    SearchDate TXT

    if { "$SearchDomain" == "GR" } {
	set ddomgr "" ; set p ""
	foreach ix $SearchDomGR {
	    lappend ddomgr "${p}$GRName($ix)"
	    set p ", "
	}
    } else { set ddomgr "" }
    set ddom "$TXT(srchdd1) $TXT(srchdd2$SearchDomain)${ddomgr}"
    set dstrs "" ; set p "" ; set fstrs ""
    foreach s "name cmmt rmrk" {
	if { $SearchSpec($s) } {
	    lappend dstrs "${p}$TXT($s):$SearchPattern($s)"
	    set p ", " ; set fstrs "\n"
	}
    }
    set ddist "" ; set f ""
    if { $SearchSpec(dist) } {
	switch $SearchDistance(how) {
	    fromWP {
		set ddistfrom "$TXT(from) $SearchWPName"		
	    }
	    fromPos {
		set pf $TXT($SearchDistance(pformt))
		set p [lrange $SearchDistance(pos) 2 end]
		set ddistfrom "$TXT(from) $pf $p ($SDatum)"
	    }
	}
	if { "[set b $SearchDistance(bearing)]" != "" } {
	    set ddistbear \
		    " $TXT(azimuth) $b, $TXT(opening) $SearchDistance(open);\n"
	} else { set ddistbear "" }
	switch $SearchDistance(range) {
	    within {
		set ddistrge " $TXT(within) $SearchDistance(dist)"
	    }
	    between {
		set ddistrge \
		  " $TXT(between) $SearchDistance(from)..$SearchDistance(to)"
	    }
	}
	set ddist "${ddistfrom};\n${ddistbear}$ddistrge"
	set f ";\n"
    }
    set dsymb ""
    if { $SearchSpec(symbol) } {
	set dsymb "${f}$TXT(with) $TXT(symbol) $TXT(SY$SearchWPSymbol)"
	set f ";\n"
    }
    set drtwp ""
    if { $SearchSpec(RTWP) } {
	set drtwp "${f}$TXT(with) $TXT(nameWP) $SearchRTWP"
	set f ";\n"
    }
    set dtrdt ""
    if { $SearchSpec(date) } {
	set dtrdt "${f}$TXT(date) $SearchDate(d) $SearchDate(m) $SearchDate(y)"
	set f ";\n"
    }
    if { "$f" != "" } { set f "\n" }
    return "${ddom}\n${dstrs}${fstrs}${ddist}${dsymb}${drtwp}${dtrdt}.$f"
}

proc SearchFor {whs} {
    # search for items of given types
    # search constraints are described by global variables (see proc
    #  SearchForAny)
    # if constraints are void the search will produce all items
    # search domain is given by $SearchDomain (in {Data, GR}), and
    #  $SearchDomGr (indices of groups to be searched)
    # search in groups will always be recursive
    # return 0 if no item found or user wants a new search, 1 otherwise
    global SearchDomain SearchDomGR Storage Number MESS

    set lp ""
    foreach wh $whs {
	set ids [lindex $Storage($wh) 0]
	global $ids

	if { "$SearchDomain" == "GR" } {
	    set ixs [GRsElements $SearchDomGR 1 $wh]
	} else {
	    set ixs [array names $ids]
	}
	if { "[set ns [SearchForAny $ids $ixs $wh]]" != "" } {
	    lappend lp [list $wh $ns]
	}
    }
    if { "$lp" != "" } { return [SearchResults $lp] }
    GMMessage $MESS(nosuchitems)
    return 0
}

proc SearchForAny {ids ixs wh} {
    # search for items of type $wh in $TYPES
    #  $ids is storage name for item identifiers
    #  $ixs is indices representing the search domain
    # constraints on search given by:
    #  $SearchSpec($s), $s in {name, cmmt, rmrk} boolean
    #    $SearchPattern($s) gives pattern to match against identifier,
    #    comment (not for TR, GR), or remark
    #  for WP and TR:
    #   $SearchSpec(dist) boolean
    #     $SearchDistance(pos) is position with $SDatum
    #     $SearchDistance(azimuth) compass direction in degrees
    #     $SearchDistance(hangle) half angle for azimuth range in degrees
    #     $SearchDistance(low) in kms
    #     $SearchDistance(up) in kms
    #  for WP:
    #   $SearchSpec(symbol) boolean, $SearchWPSymbol
    #  for RT:
    #   $SearchSpec(RTWP) boolean, $SearchRTWP
    #  for TR:
    #   $SearchSpec(date) boolean
    #     $SearchDate(d) day of month (range not checked) or ""
    #     $SearchDate(m) month name or ""
    #     $SearchDate(y) year or ""
    # return list of names
    global SearchSpec SearchPattern SDatum SearchWPSymbol SearchRTWP \
	    SearchDate WPPosn WPDatum RTWPoints TRTPoints TRDatum

    if { "$ixs" == "" } { return "" }
    foreach s "name rmrk" a "$ids ${wh}Obs" {
	if { $SearchSpec($s) } {
	    set ixs [SearchFilter $a $ixs $SearchPattern($s)]
	}
    }
    if { "$ixs" == "" } { return "" }
    switch $wh {
	WP {
	    if { $SearchSpec(cmmt) } {
		set ixs [SearchFilter WPCommt $ixs $SearchPattern(cmmt)]
	    }
	    if { $SearchSpec(symbol) } {
		# this is safe only if symbol names are alpha-numeric
		set ixs [SearchFilter WPSymbol $ixs $SearchWPSymbol]
	    }
	    if { $SearchSpec(dist) } {
		SetDatumData $SDatum
		set nixs ""
		foreach ix $ixs {
		    if { [SearchDistConstr $WPPosn($ix) $WPDatum($ix)] } {
			lappend nixs $ix
		    }
		}
		set ixs $nixs
	    }
	}
	RT {
	    if { $SearchSpec(cmmt) } {
		set ixs [SearchFilter RTCommt $ixs $SearchPattern(cmmt)]
	    }
	    if { $SearchSpec(RTWP) } {
		set nixs ""
		foreach ix $ixs {
		    foreach wpn $RTWPoints($ix) {
			if { "$SearchRTWP" == "$wpn" } {
			    lappend nixs $ix
			    break
			}
		    }
		}
		set ixs $nixs
	    }
	}
	TR {
	    if { $SearchSpec(date) } {
		set s 3
		foreach a "d m y" {
		    if { "[set $a $SearchDate($a)]" == "" } {
			set $a *
			incr s -1
		    }
		}
		if { $s > 0 } {
		    set nixs ""
		    foreach ix $ixs {
			set p [lindex $TRTPoints($ix) 0]
			if { [CompatibleDates $y $m $d [lindex $p 5]] } {
			    lappend nixs $ix
			}
		    }
		    set ixs $nixs
		}
	    }
	    if { $SearchSpec(dist) } {
		SetDatumData $SDatum
		set nixs ""
		foreach ix $ixs {
		    set d $TRDatum($ix)
		    foreach p $TRTPoints($ix) {
			if { [SearchDistConstr $p $d] } {
			    lappend nixs $ix
			    break
			}
		    }
		}
		set ixs $nixs
	    }
	}
    }
    return [lsort [Apply $ixs NameOf $wh]]
}

proc SearchDistConstr {pos datum} {
    # check search distance constraint defined as described in proc
    #  SearchForAny
    # assume SetDatumData $SDatum has been called
    global SearchDistance SDatum

    if  { "$SDatum" != "$datum" } {
	set pos [ConvertDatum [lindex $pos 0] [lindex $pos 1] \
		$datum $SDatum DDD]
    }
    set db [ComputeDistBearFD $SearchDistance(pos) $pos]
    set d [lindex $db 0]
    if { $SearchDistance(low)>$d || $d>$SearchDistance(up) } { return 0 }
    if { [set dd [expr abs($SearchDistance(azimuth)-[lindex $db 1])]] > 180 } {
	set dd [expr 360-$dd]
    }
    if { $dd > $SearchDistance(hangle) } { return 0 }
    return 1
}

proc SearchFilter {arr ixs patt} {
    # select indices from $ixs in array $arr whose contents follow pattern
    global $arr

    set nixs ""
    foreach ix $ixs {
	if { [string match $patt [set [set arr]($ix)]] } {
	    lappend nixs $ix
	}
    }
    return $nixs
}

proc SearchResults {lp} {
    # present search results described by list of pairs: type, names
    # this is currently done by creating and opening a group
    # return 0 if a new search is asked for by the user
    global TXT MESS EdWindow

    set grname [format "$TXT(srchres) %d" 0] ; set n 0
    while { [IndexNamed GR $grname] != -1 } {
	incr n ; set grname [format "$TXT(srchres) %d" $n]
    }
    set data [FormData GR "Name Obs Conts" \
	         [list $grname [SearchDescription] $lp]]
    set ix [CreateItem GR $data]
    set w [OpenItem GR $ix]
    switch [GMSelect "$MESS(resultsin) $grname" \
	    [list Ok $TXT(another) $TXT(change) $TXT(cancel)] \
	    "ok another change cancel"] {
	ok {
	}
	another {
	    return 0
	}
	change {
	    destroy $w
	    Forget GR $ix
	    return 0
	}
	cancel {
	    destroy $w
	    Forget GR $ix
	}	
    }
    return 1
}

proc SearchToggleDomain {} {
    # make user select groups whose flatenning will be the search domain
    #  if domain is now a set of groups
    # if selection is cancelled search domain reverts to data-base
    global SearchDomain SearchDomGR GRName TXT

    if { "$SearchDomain" == "Data" } { return }
    set w .gmsearch
    $w.fr.dom.gr.mb.m delete 0 end
    set SearchDomGR [ChooseItems GR]
    grab $w
    # RaiseWindow $w
    switch [llength $SearchDomGR] {
	0 {
	    set SearchDomain Data
	    $w.fr.dom.gr.mb configure -text ""
	}
	1 {
	    $w.fr.dom.gr.mb configure -text $GRName($SearchDomGR)
	}
	default {
	    $w.fr.dom.gr.mb configure -text "; $TXT(list) ..."
	    foreach ix $SearchDomGR {
		$w.fr.dom.gr.mb.m add command -label $GRName($ix)
	    }
	}
    }
    return
}

proc SearchToggle {wh} {
    # enable/disable checkbuttons in search window that are specific to
    #  search of $wh (in $TYPES) items
    global SearchSpec SearchWidgets

    if { $SearchSpec($wh) } {
	set st normal
    } else { set st disabled }
    foreach w $SearchWidgets($wh) {
	switch -glob $w {
	    */* {
		set l [split $w /]
		set wh2 [lindex $l 0] ; set w [lindex $l 1]
		if { $SearchSpec($wh) == $SearchSpec($wh2) } {
		    if {! $SearchSpec($wh) } { $w deselect }
		    $w configure -state $st
		} else {
		    $w configure -state normal
		}
	    }
	    * {
		if {! $SearchSpec($wh) } { $w deselect }
		$w configure -state $st
	    }
	}
    }
    return
}

proc SearchChangeDatum {datum args} {
    # change datum of search window
    #  $args is not used but is needed as this is called-back from a menu
    global SDatum INVTXT MESS

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

proc GMSearchWPChange {menubutton name} {
    # change WP name in WP/TR search window
    # name of proc reflects use of FillItemsMenu
    global SearchWPName

    set SearchWPName $name
    return
}

proc SearchBearingAngle {} {
    # change suggested default for bearing opening-angle in WP/TR search window
    global SearchDistance

    switch $SearchDistance(range) {
	within {
	    set SearchDistance(opendef) 45
	}
	between {
	    if { [CheckFloat Ignore $SearchDistance(from)] && \
		    [CheckFloat Ignore $SearchDistance(to)] } {
		if { $SearchDistance(to) <= 1e-20 } {
		    set SearchDistance(opendef) 45
		} else {
		    set SearchDistance(opendef) [expr round(atan( \
			    1.0*($SearchDistance(to)-$SearchDistance(from)) \
			    /$SearchDistance(to))*57.29577951308232087684)]
		}
	    } else {
		set SearchDistance(opendef) ???
	    }
	}
    }
    return
}

proc GMSearchRTWPChange {menubutton name} {
    # change WP name in RT search window
    # name of proc reflects use of FillItemsMenu
    global SearchRTWP

    set SearchRTWP $name
    return
}

proc SearchChangeSymbol {symbol args} {
    # change symbol of search window
    #  $args not used, but called back like this
    global SearchWPSymbol SYMBOLIMAGE TXT

    set SearchWPSymbol $symbol
    set fr .gmsearch.fr.fWP
    $fr.symbim delete all
    $fr.symbim create image 0 0 -anchor nw -image $SYMBOLIMAGE($symbol)
    $fr.symbname configure -text $TXT(SY$symbol)
    return
}

