1###
2### search/header.tcl: Header Search routines for Scid.
3###
4
5namespace eval ::search::header {}
6
7set sTitleList [list gm im fm none wgm wim wfm w]
8foreach i $sTitleList {
9  set sTitles(w:$i) 1
10  set sTitles(b:$i) 1
11}
12set sHeaderFlagList {StdStart Promotions Comments Variations Annotations \
13      DeleteFlag WhiteOpFlag BlackOpFlag MiddlegameFlag EndgameFlag \
14      NoveltyFlag PawnFlag TacticsFlag KsideFlag QsideFlag \
15      BrilliancyFlag BlunderFlag UserFlag }
16
17set sHeaderCustomFlagList {  CustomFlag1 CustomFlag2 CustomFlag3 CustomFlag4 CustomFlag5 CustomFlag6 }
18
19set sHeaderFlagChars {S X _ _ _ D W B M E N P T K Q ! ? U 1 2 3 4 5 6}
20
21set sPgntext(1) ""
22set sPgntext(2) ""
23set sPgntext(3) ""
24
25# checkDates:
26#    Checks minimum/maximum search dates in header search window and
27#    extends them if necessary.
28proc checkDates {} {
29  global sDateMin sDateMax sEventDateMin sEventDateMax
30  if {[string length $sDateMin] == 4} { append sDateMin ".??.??" }
31  if {[string length $sDateMax] == 4} { append sDateMax ".12.31" }
32  if {[string length $sDateMin] == 7} { append sDateMin ".??" }
33  if {[string length $sDateMax] == 7} { append sDateMax ".31" }
34  if {[string length $sEventDateMin] == 4} { append sDateMin ".??.??" }
35  if {[string length $sEventDateMax] == 4} { append sDateMax ".12.31" }
36  if {[string length $sEventDateMin] == 7} { append sDateMin ".??" }
37  if {[string length $sEventDateMax] == 7} { append sDateMax ".31" }
38}
39
40proc ::search::header::defaults {} {
41  set ::sWhite "";  set ::sBlack ""
42  set ::sEvent ""; set ::sSite "";  set ::sRound ""; set ::sAnnotator ""; set ::sAnnotated 0
43  set ::sWhiteEloMin ""; set ::sWhiteEloMax ""
44  set ::sBlackEloMin ""; set ::sBlackEloMax ""
45  set ::sEloDiffMin ""; set ::sEloDiffMax ""
46  set ::sGlMin ""; set ::sGlMax ""
47  set ::sEcoMin "";  set ::sEcoMax ""; set ::sEco Yes
48  set ::sGnumMin ""; set ::sGnumMax ""
49  set ::sDateMin ""; set ::sDateMax ""
50  set ::sEventDateMin ""; set ::sEventDateMax ""
51  set ::sResWin ""; set ::sResLoss ""; set ::sResDraw ""; set ::sResOther ""
52  set ::sIgnoreCol No
53  set ::sSideToMoveW "w"
54  set ::sSideToMoveB "b"
55  foreach flag  [ concat $::sHeaderFlagList $::sHeaderCustomFlagList ] { set ::sHeaderFlags($flag) both }
56  foreach i [array names ::sPgntext] { set ::sPgntext($i) "" }
57  foreach i $::sTitleList {
58    set ::sTitles(w:$i) 1
59    set ::sTitles(b:$i) 1
60  }
61}
62
63foreach i {sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax} {
64  trace variable $i w [list ::utils::validate::Integer [sc_info limit elo] 0]
65}
66trace variable sEloDiffMin w [list ::utils::validate::Integer "-[sc_info limit elo]" 0]
67trace variable sEloDiffMax w [list ::utils::validate::Integer "-[sc_info limit elo]" 0]
68
69::search::header::defaults
70
71trace variable sDateMin w ::utils::validate::Date
72trace variable sDateMax w ::utils::validate::Date
73trace variable sEventDateMin w ::utils::validate::Date
74trace variable sEventDateMax w ::utils::validate::Date
75
76
77trace variable sGlMin w {::utils::validate::Integer 9999 0}
78trace variable sGlMax w {::utils::validate::Integer 9999 0}
79
80trace variable sGnumMin w {::utils::validate::Integer -9999999 0}
81trace variable sGnumMax w {::utils::validate::Integer -9999999 0}
82
83# Forcing ECO entry to be valid ECO codes:
84foreach i {sEcoMin sEcoMax} {
85  trace variable $i w {::utils::validate::Regexp {^$|^[A-Ea-e]$|^[A-Ea-e][0-9]$|^[A-Ea-e][0-9][0-9]$|^[A-Ea-e][0-9][0-9][a-z]$|^[A-Ea-e][0-9][0-9][a-z][1-4]$}}
86}
87
88set sHeaderFlagFrame 0
89
90# ::search::header
91#
92#   Opens the window for searching by header information.
93#
94proc ::search::header {{ref_base ""} {ref_filter "dbfilter"}} {
95  ::search::Open $ref_base $ref_filter HeaderSearch ::search::headerCreateFrame
96}
97
98proc search::headerCreateFrame { w } {
99  global sWhite sBlack sEvent sSite sRound sAnnotator sAnnotated sEventDateMin sEventDateMax sIgnoreCol
100  global sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax
101  global sEloDiffMin sEloDiffMax sSideToMoveW sSideToMoveB
102  global sEco sEcoMin sEcoMax sHeaderFlags sGlMin sGlMax sTitleList sTitles
103  global sResWin sResLoss sResDraw sResOther sPgntext
104
105  foreach frame {cWhite cBlack ignore tw tb eventsite eventround date res ano gl ends eco} {
106    ttk::frame $w.$frame
107  }
108
109  set regular font_Small
110  ttk::labelframe $w.player -text $::tr(Player)
111  pack $w.player -side top -fill x -pady 5
112  foreach color {White Black} {
113    pack $w.c$color -side top -fill x -in $w.player
114    ttk::label $w.c$color.lab -textvar ::tr($color:) -width 9 -anchor w
115    ttk::combobox $w.c$color.e -textvariable "s$color" -width 40
116    ::utils::history::SetCombobox HeaderSearch$color $w.c$color.e
117
118    ttk::label $w.c$color.space
119    ttk::label $w.c$color.elo1 -textvar ::tr(Rating:)
120    ttk::entry $w.c$color.elomin -textvar s${color}EloMin -width 6 -justify right
121    ttk::label $w.c$color.elo2 -text "-"
122    ttk::entry $w.c$color.elomax -textvar s${color}EloMax -width 6 -justify right
123    bindFocusColors $w.c$color.e
124    bindFocusColors $w.c$color.elomin
125    bindFocusColors $w.c$color.elomax
126    pack $w.c$color.lab $w.c$color.e $w.c$color.space -side left
127    pack $w.c$color.elomax $w.c$color.elo2 $w.c$color.elomin $w.c$color.elo1 -side right
128  }
129
130  pack $w.ignore -side top -fill x -in $w.player
131  ttk::checkbutton $w.ignore.yes -variable sIgnoreCol -onvalue Yes -offvalue No -textvar ::tr(IgnoreColors)
132  pack $w.ignore.yes -side left
133  ttk::label $w.ignore.rdiff -textvar ::tr(RatingDiff:)
134  ttk::entry $w.ignore.rdmin -width 6 -textvar sEloDiffMin -justify right
135  ttk::label $w.ignore.rdto -text "-"
136  ttk::entry $w.ignore.rdmax -width 6 -textvar sEloDiffMax -justify right
137  bindFocusColors $w.ignore.rdmin
138  bindFocusColors $w.ignore.rdmax
139  pack $w.ignore.rdmax $w.ignore.rdto $w.ignore.rdmin $w.ignore.rdiff -side right
140
141  pack [ttk::separator $w.sep] -side top -fill x -in $w.player
142  set spellstate normal
143  if {[lindex [sc_name read] 0] == 0} { set spellstate disabled }
144  foreach c {w b} name {White Black} {
145    pack $w.t$c -side top -fill x -in $w.player
146    ttk::label $w.t$c.label -text "$::tr($name) FIDE:" -width 14 -anchor w
147    pack $w.t$c.label -side left
148    foreach i $sTitleList {
149      set name [string toupper $i]
150      if {$i == "none"} { set name "-" }
151      ttk::checkbutton $w.t$c.b$i -text $name -variable sTitles($c:$i) -offvalue 0 -onvalue 1 -state $spellstate
152      pack $w.t$c.b$i -side left -padx "0 10"
153    }
154  }
155
156  lower $w.player
157
158  ttk::labelframe $w.tournement -text $::tr(Event)
159  pack $w.tournement -side top -fill x -pady 5
160  set f $w.eventsite
161  pack $f -side top -fill x -in $w.tournement -pady "0 3"
162  foreach i {Event Site} {
163    ttk::label $f.l$i -textvar ::tr(${i}:)
164    ttk::combobox $f.e$i -textvariable s$i -width 30
165    ::utils::history::SetCombobox HeaderSearch$i $f.e$i
166    bindFocusColors $f.e$i
167  }
168  pack $f.lEvent $f.eEvent -side left
169  pack $f.eSite -side right
170  pack $f.lSite -side right -padx "10 0"
171
172  set f $w.eventround
173  pack $f -side top -fill x -in $w.tournement
174  lower $w.tournement
175  ## Setup date of Event
176  ttk::label $f.dl1 -text "$::tr(Event)\n$::tr(Date:)"
177  ttk::label $f.dl2 -text "-"
178  ttk::label $f.dl3 -text " "
179  ttk::entry $f.demin -textvariable sEventDateMin -width 10
180  button $f.deminCal -image tb_calendar -padx 0 -pady 0 -command {
181    regsub -all {[.]} $sEventDateMin "-" newdate
182    set ndate [::utils::date::chooser $newdate]
183    if {[llength $ndate] == 3} {
184      set sEventDateMin "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
185    }
186  }
187  ttk::entry $f.demax -textvariable sEventDateMax -width 10
188  button $f.demaxCal -image tb_calendar -padx 0 -pady 0 -command {
189    regsub -all {[.]} $sEventDateMax "-" newdate
190    set ndate [::utils::date::chooser $newdate]
191    if {[llength $ndate] == 3} {
192      set sEventDateMax "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
193    }
194  }
195  bindFocusColors $f.demin
196  bindFocusColors $f.demax
197  bind $f.demin <FocusOut> +checkDates
198  bind $f.demax <FocusOut> +checkDates
199  ttk::button $f.dlyear -textvar ::tr(YearToToday) -style Pad0.Small.TButton -command {
200    set sEventDateMin "[expr [::utils::date::today year]-1].[::utils::date::today month].[::utils::date::today day]"
201    set sEventDateMax [::utils::date::today]
202  }
203  ::utils::tooltip::Set $f.dlyear $::tr(YearToTodayTooltip)
204
205  pack $f.dl1 $f.demin $f.deminCal $f.dl2 $f.demax $f.demaxCal $f.dl3 $f.dlyear -side left
206
207  ttk::label $f.lRound -textvar ::tr(Round:)
208  ttk::entry $f.eRound -textvariable sRound -width 10
209  bindFocusColors $f.eRound
210  pack $f.eRound $f.lRound -side right
211
212  set f $w.date
213  pack $f -side top -fill x -in $w.tournement -pady "0 3"
214  ## Setup Date of Game
215  ttk::label $f.l1 -text "$::tr(game)\n$::tr(Date:)"
216  ttk::label $f.l2 -text "-"
217  ttk::label $f.l3 -text " "
218  ttk::entry $f.emin -textvariable sDateMin -width 10
219  button $f.eminCal -image tb_calendar -padx 0 -pady 0 -command {
220    regsub -all {[.]} $sDateMin "-" newdate
221    set ndate [::utils::date::chooser $newdate]
222    if {[llength $ndate] == 3} {
223      set sDateMin "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
224    }
225  }
226  ttk::entry $f.emax -textvariable sDateMax -width 10
227  button $f.emaxCal -image tb_calendar -padx 0 -pady 0 -command {
228    regsub -all {[.]} $sDateMax "-" newdate
229    set ndate [::utils::date::chooser $newdate]
230    if {[llength $ndate] == 3} {
231      set sDateMax "[lindex $ndate 0].[lindex $ndate 1].[lindex $ndate 2]"
232    }
233  }
234  bindFocusColors $f.emin
235  bindFocusColors $f.emax
236  bind $f.emin <FocusOut> +checkDates
237  bind $f.emax <FocusOut> +checkDates
238  ttk::button $f.lyear -textvar ::tr(YearToToday) -style Pad0.Small.TButton -command {
239    set sDateMin "[expr [::utils::date::today year]-1].[::utils::date::today month].[::utils::date::today day]"
240    set sDateMax [::utils::date::today]
241  }
242  ::utils::tooltip::Set $f.lyear $::tr(YearToTodayTooltip)
243
244  pack $f.l1 $f.emin $f.eminCal $f.l2 $f.emax $f.emaxCal $f.l3 $f.lyear -side left
245
246  ttk::labelframe $w.result -text $::tr(Result)
247  pack $w.result -side top -fill x -pady 5
248  pack $w.res -side top -fill x -in $w.result
249  ttk::label $w.res.l1 -textvar ::tr(Result:)
250  ttk::checkbutton $w.res.ewin -text "1-0 " -variable sResWin -offvalue "1" -onvalue ""
251  ttk::checkbutton $w.res.edraw -text "1/2-1/2 " -variable sResDraw -offvalue "=" -onvalue ""
252  ttk::checkbutton $w.res.eloss -text "0-1 " -variable sResLoss -offvalue "0" -onvalue ""
253  ttk::checkbutton $w.res.eother -text "* " -variable sResOther -offvalue "*" -onvalue ""
254  pack $w.res.l1 $w.res.ewin $w.res.edraw $w.res.eloss $w.res.eother -side left
255  lower $w.result
256
257  ttk::label $w.gl.l1 -textvar ::tr(GameLength:)
258  ttk::label $w.gl.l2 -text "-"
259  ttk::label $w.gl.l3 -textvar ::tr(HalfMoves)
260  ttk::entry $w.gl.emin -textvariable sGlMin -justify right -width 4
261  ttk::entry $w.gl.emax -textvariable sGlMax -justify right -width 4
262  bindFocusColors $w.gl.emin
263  bindFocusColors $w.gl.emax
264  pack $w.gl -in $w.res -side right -fill x
265  pack $w.gl.l1 $w.gl.emin $w.gl.l2 $w.gl.emax $w.gl.l3 -side left
266
267  ttk::label $w.ends.label -textvar ::tr(EndSideToMove)
268  ttk::checkbutton $w.ends.white -textvar ::tr(White) -variable sSideToMoveW -offvalue "" -onvalue w
269  ttk::checkbutton $w.ends.black -textvar ::tr(Black) -variable sSideToMoveB -offvalue "" -onvalue b
270  pack $w.ends.label $w.ends.white $w.ends.black -side left -padx "0 5"
271  pack $w.ends -side top -fill x -in $w.result
272
273  pack $w.ano -side top -fill x
274  ttk::label $w.ano.a1 -textvar ::tr(Annotations:)
275  ttk::label $w.ano.a2 -textvar ::tr(Annotator:)
276  ttk::checkbutton $w.ano.an -textvar ::tr(Cmnts) -variable sAnnotated -offvalue 0 -onvalue 1
277  ttk::entry $w.ano.aname -textvariable sAnnotator -width 20
278  pack $w.ano.a1 $w.ano.an -side left -padx "0 5"
279  pack $w.ano.aname $w.ano.a2 -side right -padx "5 0"
280
281  addHorizontalRule $w
282
283  ttk::label $w.eco.l1 -textvar ::tr(ECOCode:)
284  ttk::label $w.eco.l2 -text "-"
285  ttk::label $w.eco.l3 -text " "
286  ttk::entry $w.eco.emin -textvariable sEcoMin -width 5
287  ttk::entry $w.eco.emax -textvariable sEcoMax -width 5
288  bindFocusColors $w.eco.emin
289  bindFocusColors $w.eco.emax
290  ttk::button $w.eco.range -text "..." -style  Pad0.Small.TButton -width 0 -command {
291    set tempResult [chooseEcoRange]
292    if {[scan $tempResult "%\[A-E0-9a-z\]-%\[A-E0-9a-z\]" sEcoMin_tmp sEcoMax_tmp] == 2} {
293      set sEcoMin $sEcoMin_tmp
294      set sEcoMax $sEcoMax_tmp
295    }
296    unset tempResult
297  }
298  ttk::checkbutton $w.eco.yes -variable sEco -onvalue Yes -offvalue No -textvar ::tr(GamesWithNoECO)
299  pack $w.eco -side top -fill x -pady "5 0"
300  pack $w.eco.l1 $w.eco.emin $w.eco.l2 $w.eco.emax -side left
301  pack $w.eco.range -side left -padx "5 10"
302  pack $w.eco.l3 $w.eco.yes -side left
303
304  set f [ttk::frame $w.gnum]
305  pack $f -side top -fill x
306  ttk::label $f.l1 -textvar ::tr(GlistGameNumber:)
307  ttk::entry $f.emin -textvariable sGnumMin -width 8 -justify right
308  ttk::label $f.l2 -text "-" -font $regular
309  ttk::entry $f.emax -textvariable sGnumMax -width 8 -justify right
310  pack $f.l1 $f.emin $f.l2 $f.emax -side left
311  bindFocusColors $f.emin
312  bindFocusColors $f.emax
313  ttk::label $f.l3 -text " "
314  ttk::button $f.all -text [::utils::string::Capital $::tr(all)] -style Pad0.Small.TButton -command {set sGnumMin ""; set sGnumMax ""}
315  ttk::menubutton $f.first -style pad0.TMenubutton -textvar ::tr(First...) -menu $f.first.m
316  ttk::menubutton $f.last -style pad0.TMenubutton -textvar ::tr(Last...) -menu $f.last.m
317  menu $f.first.m
318  menu $f.last.m
319  foreach x {10 50 100 500 1000 5000 10000} {
320    $f.first.m add command -label $x \
321        -command "set sGnumMin 1; set sGnumMax $x"
322    $f.last.m add command -label $x \
323        -command "set sGnumMin -$x; set sGnumMax -1"
324  }
325  pack $f.l3 $f.all $f.first $f.last -side left -padx 2
326
327  set f [ttk::frame $w.pgntext]
328  pack $f -side top -fill x
329  ttk::label $f.l1 -textvar ::tr(PgnContains:)
330  ttk::entry $f.e1 -textvariable sPgntext(1) -width 15
331  ttk::label $f.l2 -text "+" -font $regular
332  ttk::entry $f.e2 -textvariable sPgntext(2) -width 15
333  ttk::label $f.l3 -text "+" -font $regular
334  ttk::entry $f.e3 -textvariable sPgntext(3) -width 15
335  bindFocusColors $f.e1
336  bindFocusColors $f.e2
337  bindFocusColors $f.e3
338  pack $f.l1 $f.e1 $f.l2 $f.e2 $f.l3 $f.e3 -side left -pady "0 5"
339
340  addHorizontalRule $w
341
342  ttk::button $w.flagslabel -textvar ::tr(FindGamesWith:) -style Pad0.Small.TButton -image tb_menu -compound left -command "
343    if {\$::sHeaderFlagFrame} {
344      set ::sHeaderFlagFrame 0
345      pack forget $w.flags
346    } else {
347      set ::sHeaderFlagFrame 1
348      pack $w.flags -side top -after $w.flagslabel -pady 5 -fill x
349    }
350  "
351  pack $w.flagslabel -side top -fill x -pady "5 5"
352
353  ttk::frame $w.flags
354  if {$::sHeaderFlagFrame} {
355    pack $w.flags -side top -pady 5 -fill x
356  }
357
358  set row 0
359  set col 0
360  foreach var [concat $::sHeaderFlagList $::sHeaderCustomFlagList] {
361    grid [ttk::label $w.flags.l$var -text [::tr $var] -font font_Small] -row $row -column $col -sticky w
362    incr col
363    grid [ttk::radiobutton $w.flags.yes$var -variable sHeaderFlags($var) -value yes -text $::tr(Yes)] -row $row -column $col
364    incr col
365    grid [ttk::radiobutton $w.flags.no$var -variable sHeaderFlags($var) -value no -text $::tr(No)] -row $row -column $col
366    incr col
367    grid [ttk::radiobutton $w.flags.both$var -variable sHeaderFlags($var) -value both -text $::tr(Both)] -row $row -column $col
368    incr col -3
369    incr row
370    if {$row == 12} {
371      set col 5
372      set row 0
373    }
374  }
375  grid columnconfigure $w.flags 4 -weight 1
376
377  #TODO: ref_base should be used instead of curr_db
378  set ::curr_db [sc_base current]
379  foreach {tagname tagvalue} [sc_base extra $::curr_db] {
380    if { $tagvalue ne "" && [regexp {flag([1-6])} $tagname -> i] } {
381      $w.flags.lCustomFlag$i configure -text $tagvalue
382    }
383  }
384
385  return "::search::headerGetOptions"
386}
387
388proc ::search::headerGetOptions {{cmd ""}} {
389	if {$cmd eq "reset"} {
390		::search::header::defaults
391		return
392	}
393
394	::utils::history::AddEntry HeaderSearchWhite $::sWhite
395	::utils::history::AddEntry HeaderSearchBlack $::sBlack
396	::utils::history::AddEntry HeaderSearchEvent $::sEvent
397	::utils::history::AddEntry HeaderSearchSite $::sSite
398
399	set options {header}
400	::search::headerPlayerOptions options -white -welo -black -belo
401
402	set invert_col 0
403	if {$::sIgnoreCol == "Yes" && [llength $options] > 1} { set invert_col 1 }
404
405	::search::getSearchOptions options
406	if {! $invert_col } {
407		return [list $options]
408	}
409
410	set options2 {header}
411	::search::headerPlayerOptions options2 -black -belo -white -welo
412	::search::getSearchOptions options2
413	return [list $options $options2]
414}
415
416proc ::search::getRange {var_min var_max cmd_min cmd_max} {
417	if {[set $var_min] ne ""} {
418		if {[set $var_max] ne ""} {
419			return [list [set $var_min] [set $var_max]]
420		}
421		return [list [set $var_min] [subst $cmd_max]]
422	} elseif {[set $var_max] ne ""} {
423		return [list [subst $cmd_min] [set $var_max]]
424	}
425	return {}
426}
427
428proc ::search::headerPlayerOptions {dest_list white welo black belo} {
429	upvar $dest_list options
430
431	if {$::sWhite ne ""} { lappend options $white $::sWhite }
432
433	if {$::sBlack ne ""} { lappend options $black $::sBlack	}
434
435	set range [::search::getRange ::sWhiteEloMin ::sWhiteEloMax 0 "\[sc_info limit elo\]"]
436	if {$range ne ""} { lappend options $welo $range }
437
438	set range [::search::getRange ::sBlackEloMin ::sBlackEloMax 0 "\[sc_info limit elo\]"]
439	if {$range ne ""} { lappend options $belo $range }
440}
441
442### Read values from header search dialog. Use empty string as "all"
443proc ::search::getSearchOptions {dest_list} {
444	upvar $dest_list search
445
446	if {$::sEvent ne ""} { lappend search "-event" $::sEvent }
447
448	if {$::sSite ne ""} { lappend search "-site" $::sSite }
449
450	if {$::sRound ne ""} { lappend search "-round" $::sRound }
451
452	set range [::search::getRange ::sGnumMin ::sGnumMax 0 -1]
453	if {$range ne ""} { lappend search "-gnum" $range }
454
455	set range [::search::getRange ::sGlMin ::sGlMax 0 999]
456	if {$range ne ""} { lappend search "-length" $range }
457
458	set range [::search::getRange ::sDateMin ::sDateMax "1800.01.01" "\[sc_info limit year\].12.31"]
459	if {$range ne ""} { lappend search "-date" $range }
460
461	set range [::search::getRange ::sEventDateMin ::sEventDateMax "1800.01.01" "\[sc_info limit year\].12.31"]
462	if {$range ne ""} { lappend search "-eventdate" $range }
463
464	set range [::search::getRange ::sEloDiffMin ::sEloDiffMax "-\[sc_info limit elo\]" "\[sc_info limit elo\]"]
465	if {$range ne ""} {
466		lappend search "-delo" $range
467		if {$::sIgnoreCol == "Yes"} {
468			lassign $range elo_min elo_max
469			lappend search "-delo|" [list [expr -1 * $elo_max] [expr -1 * $elo_min]]
470		}
471	}
472
473	set range [::search::getRange ::sEcoMin ::sEcoMax A00 E99]
474	if {$range ne ""} {
475		lappend search "-eco" $range
476		if {$::sEco eq "Yes"} { lappend search "-eco|" [list 0 0] }
477	}
478	if {$::sEco ne "Yes"} { lappend search "-eco!" [list 0 0] }
479
480	set wtitles {}
481	set btitles {}
482	foreach i $::sTitleList {
483	  if $::sTitles(w:$i) { lappend wtitles $i }
484	  if $::sTitles(b:$i) { lappend btitles $i }
485	}
486	if {[llength $wtitles] != 8} { lappend search -wtitles $wtitles }
487	if {[llength $btitles] != 8} { lappend search -btitles $btitles }
488
489	if {$::sSideToMoveW eq "" || $::sSideToMoveB eq ""} {
490		lappend search -toMove "$::sSideToMoveW$::sSideToMoveB"
491	}
492
493	if {$::sAnnotated} { lappend search "-annotated" $::sAnnotated }
494
495	if {$::sAnnotator ne ""} { lappend search "-annotator" $::sAnnotator }
496
497    global sHeaderFlags
498    global sResWin sResLoss sResDraw sResOther sPgntext
499
500    set sPgnlist {}
501    foreach i {1 2 3} {
502      set temp [string trim $sPgntext($i)]
503      if {$temp != ""} { lappend sPgnlist $temp }
504    }
505
506    set flagsYes ""
507    set flagsNo ""
508    set idx -1
509    foreach i [ concat $::sHeaderFlagList $::sHeaderCustomFlagList ] {
510        incr idx
511        if {$i == "Comments"} { continue }
512        if {$i == "Variations"} { continue }
513        if {$i == "Annotations"} { continue }
514
515        if  { $sHeaderFlags($i) == "yes" } {
516            append flagsYes [lindex $::sHeaderFlagChars $idx]
517        } elseif  { $sHeaderFlags($i) == "no" } {
518            append flagsNo [lindex $::sHeaderFlagChars $idx]
519        }
520    }
521
522    set results ""
523    append results $sResWin $sResDraw $sResLoss $sResOther
524    foreach { i j} { -result! results -flag flagsYes -flag! flagsNo -pgn sPgnlist} {
525	if { [llength [set $j]] > 0 } {
526	    lappend search $i [set $j]
527	}
528    }
529    set fCounts(Variations) "-n_variations"
530    set fCountsV(Variations) ""
531    set fCounts(Comments) "-n_comments"
532    set fCountsV(Comments) ""
533    set fCounts(Annotations) "-n_nags"
534    set fCountsV(Annotations) ""
535    foreach i {"Variations" "Comments" "Annotations"} {
536        if  { $sHeaderFlags($i) == "yes" } {
537             append fCounts($i) "!"
538             set fCountsV($i) "0"
539	     lappend search $fCounts($i) $fCountsV($i)
540        } elseif  { $sHeaderFlags($i) == "no" } {
541             set fCountsV($i) "0"
542	     lappend search $fCounts($i) $fCountsV($i)
543        }
544    }
545}
546
547proc ::search::header::save {} {
548  global sWhite sBlack sEvent sSite sRound sAnnotator sAnnotated sDateMin sDateMax sIgnoreCol
549  global sWhiteEloMin sWhiteEloMax sBlackEloMin sBlackEloMax
550  global sEloDiffMin sEloDiffMax sGlMin sGlMax
551  global sEco sEcoMin sEcoMax sHeaderFlags sSideToMoveW sSideToMoveB
552  global sResWin sResLoss sResDraw sResOther sPgntext
553
554  set ftype { { "Scid SearchOptions files" {".sso"} } }
555  set fName [tk_getSaveFile -initialdir [pwd] -filetypes $ftype -title "Create a SearchOptions file"]
556  if {$fName == ""} { return }
557
558  if {[string compare [file extension $fName] ".sso"] != 0} {
559    append fName ".sso"
560  }
561
562  if {[catch {set searchF [open [file nativename $fName] w]} ]} {
563    tk_messageBox -title "Error: Unable to open file" -type ok -icon error \
564        -message "Unable to create SearchOptions file: $fName"
565    return
566  }
567  puts $searchF "\# SearchOptions File created by Scid $::scidVersion"
568  puts $searchF "set searchType Header"
569  getSearchEntries
570
571  # First write the regular variables:
572  foreach i {sWhite sBlack sEvent sSite sRound sAnnotator sAnnotated sDateMin sDateMax sResWin
573    sResLoss sResDraw sResOther sWhiteEloMin sWhiteEloMax sBlackEloMin
574    sBlackEloMax sEcoMin sEcoMax sEloDiffMin sEloDiffMax
575    sIgnoreCol sSideToMoveW sSideToMoveB sGlMin sGlMax ::search::filter::operation} {
576    puts $searchF "set $i [list [set $i]]"
577  }
578  # Now write the array values:
579  foreach i [array names sHeaderFlags] {
580    puts $searchF "set sHeaderFlags($i) [list $sHeaderFlags($i)]"
581  }
582  foreach i [array names sPgntext] {
583    puts $searchF "set sPgntext($i) [list $sPgntext($i)]"
584  }
585
586  tk_messageBox -type ok -icon info -title "Search Options saved" \
587      -message "Header search options saved to: $fName"
588  close $searchF
589}
590
591##############################
592### Selecting common ECO ranges
593
594set scid_ecoRangeChosen ""
595set ecoCommonRanges {}
596proc chooseEcoRange {} {
597  global ecoCommonRanges scid_ecoRangeChosen
598  set ecoCommonRanges [ list \
599      "A04-A09  [tr Reti]: [trans 1.Nf3]" \
600      "A10-A39  [tr English]: 1.c4" \
601      "A40-A49  1.d4, [tr d4Nf6Miscellaneous]" \
602      "A45l-A45z  [tr Trompowsky]: [trans [list 1.d4 Nf6 2.Bg5]]" \
603      "A51-A52  [tr Budapest]: [trans [list 1.d4 Nf6 2.c4 e5]]" \
604      "A53-A55  [tr OldIndian]: [trans [list 1.d4 Nf6 2.c4 d6]]" \
605      "A57-A59  [tr BenkoGambit]: [trans [list 1.d4 Nf6 2.c4 c5 3.d5 b5]]" \
606      "A60-A79  [tr ModernBenoni]: [trans [list 1.d4 Nf6 2.c4 c5 3.d5 e6]]" \
607      "A80-A99  [tr DutchDefence]: 1.d4 f5" \
608      "____________________________________________________________" \
609      "B00-C99  1.e4" \
610      "B01-B01     [tr Scandinavian]: 1.e4 d5" \
611      "B02-B05     [tr AlekhineDefence]: [trans [list 1.e4 Nf6]]" \
612      "B07-B09     [tr Pirc]: 1.e4 d6" \
613      "B10-B19     [tr CaroKann]: 1.e4 c6" \
614      "B12i-B12z      [tr CaroKannAdvance]: 1.e4 c6 2.d4 d5 3.e5" \
615      "B20-B99  [tr Sicilian]: 1.e4 c5" \
616      "B22-B22     [tr SicilianAlapin]: 1.e4 c5 2.c3" \
617      "B23-B26     [tr SicilianClosed]: [trans [list 1.e4 c5 2.Nc3]]" \
618      "B30-B39     [tr Sicilian]: [trans [list 1.e4 c5 2.Nf3 Nc6]]" \
619      "B40-B49     [tr Sicilian]: [trans [list 1.e4 c5 2.Nf3 e6]]" \
620      "B50-B59     [tr SicilianRauzer]: [trans [list 1.e4 c5 2.Nf3 d6 ... 5.Nc3 Nc6]]" \
621      "B70-B79     [tr SicilianDragon]: [trans [list 1.e4 c5 2.Nf3 d6 ... 5.Nc3 g6]]" \
622      "B80-B89     [tr SicilianScheveningen]: [trans [list 1.e4 c5 2.Nf3 d6 ... 5.Nc3 e6]]" \
623      "B90-B99     [tr SicilianNajdorf]: [trans [list 1.e4 c5 2.Nf3 d6 ... 5.Nc3 a6]]" \
624      "____________________________________________________________" \
625      "C00-C19  [tr FrenchDefence]: 1.e4 e6" \
626      "C02-C02     [tr FrenchAdvance]: 1.e4 e6 2.d4 d5 3.e5" \
627      "C03-C09     [tr FrenchTarrasch]: [trans [list 1.e4 e6 2.d4 d5 3.Nd2]]" \
628      "C15-C19     [tr FrenchWinawer]: [trans [list 1.e4 e6 2.d4 d5 3.Nc3 Bb4]]" \
629      "C20-C99  [tr OpenGame]: 1.e4 e5" \
630      "C25-C29     [tr Vienna]: [trans [list 1.e4 e5 2.Nc3]]" \
631      "C30-C39     [tr KingsGambit]: 1.e4 e5 2.f4" \
632      "C42-C43     [tr RussianGame]: [trans [list 1.e4 e5 2.Nf3 Nf6]]" \
633      "C44-C49     [tr OpenGame]: [trans [list 1.e4 e5 2.Nf3 Nc6]]" \
634      "C50-C59     [tr ItalianTwoKnights]: 1.e4 e5 2.Nf3 Nc6 3.Bc4]]" \
635      "C60-C99  [tr Spanish]: [trans [list 1.e4 e5 2.Nf3 Nc6 3.Bb5]]" \
636      "C68-C69      [tr SpanishExchange]: [trans [list 3.Bb5 a6 4.Bxc6]]" \
637      "C80-C83      [tr SpanishOpen]: [trans [list 3.Bb5 a6 4.Ba4 Nf6 5.O-O Nxe4]]" \
638      "C84-C99      [tr SpanishClosed]: [trans [list 3.Bb5 a6 4.Ba4 Nf6 5.O-O Be7]]" \
639      "____________________________________________________________" \
640      "D00-D99  [tr Queen's Pawn]: 1.d4 d5" \
641      "D10-D19  [tr Slav]: 1.d4 d5 2.c4 c6" \
642      "D20-D29  [tr QGA]: 1.d4 d5 2.c4 dxc4" \
643      "D30-D69  [tr QGD]: 1.d4 d5 2.c4 e6" \
644      "D35-D36     [tr QGDExchange]: 1.d4 d5 2.c4 e6 3.cxd5 exd5" \
645      "D43-D49     [tr SemiSlav]: [trans [list 3.Nc3 Nf6 4.Nf3 c6]]" \
646      "D50-D69     [tr QGDwithBg5]: [trans [list 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5]]" \
647      "D60-D69     [tr QGDOrthodox]: [trans [list 4.Bg5 Be7 5.e3 O-O 6.Nf3 Nbd7]]" \
648      "D70-D99  [tr Grunfeld]: [trans [list 1.d4 Nf6 2.c4 g6 with 3...d5]]" \
649      "D85-D89     [tr GrunfeldExchange]: [trans [list 3.Nc3 d5 4.e4 Nxc3 5.bxc3]]" \
650      "D96-D99     [tr GrunfeldRussian]: [trans [list 3.Nc3 d5 4.Nf3 Bg7 5.Qb3]]" \
651      "____________________________________________________________" \
652      "E00-E09  [tr Catalan]: [trans [list 1.d4 Nf6 2.c4 e6 3.g3/...]]" \
653      "E02-E05     [tr CatalanOpen]: [trans [list 3.g3 d5 4.Bg2 dxc4]]" \
654      "E06-E09     [tr CatalanClosed]: [trans [list 3.g3 d5 4.Bg2 Be7]]" \
655      "E12-E19  [tr QueensIndian]: [trans [list 1.d4 Nf6 2.c4 e6 3.Nf3 b6]]" \
656      "E20-E59  [tr NimzoIndian]: [trans [list 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4]]" \
657      "E32-E39     [tr NimzoIndianClassical]: [trans [list 4.Qc2]]" \
658      "E40-E59     [tr NimzoIndianRubinstein]: 4.e3" \
659      "E60-E99  [tr KingsIndian]: [trans [list 1.d4 Nf6 2.c4 g6]]" \
660      "E80-E89     [tr KingsIndianSamisch]: 4.e4 d6 5.f3" \
661      "E90-E99     [tr KingsIndianMainLine]: [trans [list 4.e4 d6 5.Nf3]]" \
662      ]
663
664  if {[winfo exists .ecoRangeWin]} { return }
665  set w .ecoRangeWin
666  toplevel $w
667  wm title $w "Scid: Choose ECO Range"
668  wm minsize $w 30 5
669
670  listbox $w.list -yscrollcommand "$w.ybar set" -height 20 -width 60 -background white -setgrid 1
671  foreach i $ecoCommonRanges { $w.list insert end $i }
672  ttk::scrollbar $w.ybar -command "$w.list yview" -takefocus 0
673  pack [ttk::frame $w.b] -side bottom -fill x
674  pack $w.ybar -side right -fill y
675  pack $w.list -side left -fill both -expand yes
676
677  ttk::button $w.b.ok -text "OK" -command {
678    set sel [.ecoRangeWin.list curselection]
679    if {[llength $sel] > 0} {
680      set scid_ecoRangeChosen [lindex $ecoCommonRanges [lindex $sel 0]]
681      set ::sEco No
682    }
683    destroy .ecoRangeWin
684  }
685  ttk::button $w.b.cancel -text $::tr(Cancel) -command "destroy $w"
686  pack $w.b.cancel $w.b.ok -side right -padx 5 -pady 2
687  bind $w <Escape> "
688  set scid_ecoRangeChosen {}
689  grab release $w
690  focus .
691  destroy $w
692  break"
693  bind $w <Return> "$w.b.ok invoke; break"
694  bind $w.list <Double-ButtonRelease-1> "$w.b.ok invoke; break"
695  focus $w.list
696  grab $w
697  tkwait window $w
698  return $scid_ecoRangeChosen
699}
700
701###
702### End of file: search.tcl
703
704