#
# This file is part of:
#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
# Copyright (c) 1998-2011 Miguel Filgueiras migf@portugalmail.pt
#
#    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 3 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 July 2011
#

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

    set stypes $TYPES
    if { $SUPPORTLAPS } { lappend stypes LAP }
    set SearchSpec(_types) $stypes
    # absolute paths for this window used elsewhere
    set w .gmsearch
    if { [winfo exists $w] } { Raise $w ; bell ; return }

    set SDatum $Datum
    set SearchWPSymbol $DEFAULTSYMBOL

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

    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
    # this will make easy some tests even if laps are not supported
    set SearchSpec(LAP) 0
    foreach wh $stypes {
	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 ""
    button $w.fr.fWPTR.dist.wpp.wp.it -textvariable SearchWPName \
	    -width $NAMEWIDTH \
	    -command "ChItemsCall WP single AssignGlobal SearchWPName"
    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 [list ""] \
	    SearchChangeDatum SDatum 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.fWPsy -relief flat -borderwidth 0
    set SearchSpec(symbol) 0
    checkbutton $w.fr.fWPsy.b -text "$TXT(nameWP) $TXT(with)" -anchor w \
	    -variable SearchSpec(symbol) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -state disabled
    menubutton $w.fr.fWPsy.symb -text $TXT(symbol) -relief raised \
	    -direction below -menu $w.fr.fWPsy.symb.m
    set mw $w.fr.fWPsy.symb.m
    menu $mw -tearoff 0
    FillSymbolsMenu $mw SearchChangeSymbol
    canvas $w.fr.fWPsy.symbim -width $ICONWIDTH -height [expr $ICONHEIGHT+2]
    $w.fr.fWPsy.symbim create image 1 5 -anchor nw \
	    -image $SYMBOLIMAGE($SearchWPSymbol)
    label $w.fr.fWPsy.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 ""
    button $w.fr.fRT.wp -textvariable SearchRTWP -width $NAMEWIDTH \
	    -command "ChItemsCall WP single AssignGlobal SearchRTWP"

    if { $SUPPORTLAPS } {
	set trlaps "TR LAP"
    } else { set trlaps TR }
    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 wh $trlaps {
	set f $w.fr.f$wh
	frame $f -relief flat -borderwidth 0
	set SearchSpec($wh,date) 0
	checkbutton $f.b -text "$TXT(name$wh) $TXT(started)" -anchor w \
	    -variable SearchSpec($wh,date) -onvalue 1 -offvalue 0 \
	    -selectcolor $COLOUR(check) -state disabled
	foreach de "d m y" vs [list $ds $MONTHNAMES $ys] {
	    set SearchDate($wh,$de) ""
	    menubutton $f.$de -textvariable SearchDate($wh,$de) \
		-relief raised -direction below -width 5 -menu $f.$de.m
	    menu $f.$de.m -tearoff 0
	    set mw $f.$de.m
	    $f.$de.m add command -label "($TXT(any))" \
		-command "set SearchDate($wh,$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($wh,$de) $v"
		incr n
	    }
	}
	set SearchWidgets($wh) $w.fr.f$wh.b
    }
    set SearchWidgets(WP) "$w.fr.fWPsy.b TR/$w.fr.fWPTR.its.tit"
    set SearchWidgets(RT) "$w.fr.fRT.b"
    lappend SearchWidgets(TR) WP/$w.fr.fWPTR.its.tit
    set SearchWidgets(LN) ""
    set SearchWidgets(GR) ""

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

    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.fWPsy.b $w.fr.fWPsy.symb $w.fr.fWPsy.symbim \
	$w.fr.fWPsy.symbname -side left -padx 2

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

    pack $w.fr.tit $w.fr.wh $w.fr.dom $w.fr.strs -side top -pady 5
    pack $w.fr.fWPTR $w.fr.fWPsy $w.fr.fRT -side top -pady 5 \
	-anchor w
    foreach wh $trlaps {
	set f $w.fr.f$wh
	pack $f.b $f.d $f.m $f.y -side left -padx 2
	pack $f -side top -pady 5 -anchor w
    }
    pack $w.fr.frbt.ok $w.fr.frbt.cnc -side left -padx 5
    pack $w.fr.frbt -side top -pady 5
    pack $w.fr

    update idletasks
    # apparently there is no need for a grab here
    #  but if there is must take care of previous grabs (proc DestroyRGrabs)
    # cannot use RaiseWindow because of the menus
    return
}

proc DoSearch {w} {
    # begin search
    global SearchSpec SearchPattern SearchDistance SDatum SearchWPName \
	SearchRTWP WPDatum WPPosn AzimuthDegrees DSCALE MESS INVTXT

    if { [set stypes $SearchSpec(_types)] == "" } {
	GMMessage $MESS(mustchoose1type)
	return
    }
    set whs ""
    foreach wh $stypes {
	if { $SearchSpec($wh) } { lappend whs $wh }
    }
    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)
		    return
		}
	    }
	    fromPos {
		set z $w.fr.fWPTR.dist.wpp.p.p.frp
		set p [PosnGetCheck $z.frp1 $SDatum GMMessage nil]
		if { $p == "nil" } { return }
		set SearchDistance(pos) $p
		set SearchDistance(pformt) $INVTXT([$z.pfmt cget -text])
	    }
	}
	if { $SearchDistance(bearing) != "" } {
	    if { ! [CheckNumber GMMessage $SearchDistance(open)] } { return }
	    if { $SearchDistance(open) > 359 || $SearchDistance(open) == 0 } {
		GMMessage $MESS(badangle)
		return
	    }
	    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)] } {
		    return
		}
		set SearchDistance(low) 0
		set SearchDistance(up) [expr $DSCALE*$SearchDistance(dist)]
	    }
	    between {
		if { ! [CheckFloat GMMessage $SearchDistance(from)] || \
			 ! [CheckFloat GMMessage $SearchDistance(to)]} { 
		    return
		}
		set SearchDistance(low) [expr $DSCALE*$SearchDistance(from)]
		set SearchDistance(up) [expr $DSCALE*$SearchDistance(to)]
	    }
	}
    }
    if { $SearchSpec(RTWP) && $SearchRTWP == "" } {
	GMMessage $MESS(badWP)
	return
    }
    if { [SearchFor $whs] } { destroy $w }
    return
}

proc SearchDescription {} {
    # return a text with 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 SUPPORTLAPS

    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(TR,date) } {
	set dtrdt \
	 "${f}$TXT(date) $SearchDate(TR,d) $SearchDate(TR,m) $SearchDate(TR,y)"
	set f ";\n"
    }
    set dlapdt ""
    if { $SUPPORTLAPS && $SearchSpec(LAP,date) } {
	set dlapdt \
      "${f}$TXT(date) $SearchDate(LAP,d) $SearchDate(LAP,m) $SearchDate(LAP,y)"
	set f ";\n"
    }
    if { $f != "" } { set f "\n" }
    return \
 "${ddom}\n${dstrs}${fstrs}${ddist}${dsymb}${drtwp}${dtrdt}${dlapdt}.$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, LAP:
    #   $SearchSpec($wh,date) boolean
    #     $SearchDate($wh,d) day of month (range not checked) or ""
    #     $SearchDate($wh,m) month name or ""
    #     $SearchDate($wh,y) year or ""
    # return list of names
    global SearchSpec SearchPattern SDatum SearchWPSymbol \
	    SearchRTWP SearchDate WPPosn WPDatum RTWPoints TRTPoints TRDatum \
	    LAPStart

    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) } {
		set ixs [SearchFilterId 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(TR,date) } {
		set s 3
		foreach a "d m y" {
		    if { [set $a $SearchDate(TR,$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
	    }
	}
	LAP {
	    if { $SearchSpec(LAP,date) } {
		set s 3
		foreach a "d m y" {
		    if { [set $a $SearchDate(LAP,$a)] == "" } {
			set $a *
			incr s -1
		    }
		}
		if { $s > 0 } {
		    set nixs ""
		    foreach ix $ixs {
			set secs [lindex $LAPStart($ix) 1]
			if { [CompatibleDates $y $m $d $secs] } {
			    lappend nixs $ix
			}
		    }
		    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 [ToDatum [lindex $pos 0] [lindex $pos 1] $datum $SDatum]
    }
    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 SearchFilterId {arr ixs str} {
    # select indices from $ixs in array $arr whose contents is $str
    global $arr

    set nixs ""
    foreach ix $ixs {
	if { [set [set arr]($ix)] == $str } {
	    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 GRName

    set ix [CreateGRFor srchres [SearchDescription] $lp]
    set w [OpenItem GR $ix]
    switch [GMSelect "$MESS(resultsin) $GRName($ix)" \
	    [list $TXT(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]
    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 or LAP) 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

    ChangeDatum $datum SDatum SDatum nil .gmsearch.fr.fWPTR.dist.wpp.p.p.frp
    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 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.fWPsy
    $fr.symbim delete all
    $fr.symbim create image 1 5 -anchor nw -image $SYMBOLIMAGE($symbol)
    $fr.symbname configure -text $TXT(SY$symbol)
    return
}


