### reper.tcl:
### Repertoire editor functions for Scid.
### Copyright (C) 2001-2002 Shane Hudson.

# The heirarchical view used to display a repertoire in Scid was
# originally based on the  public domain Tcl/Tk tree widget written by
# D. Richard Hipp (available at: http://www.hwaci.com/sw/tk/tree.html)
# but the Scid code is completely rewritten and significantly improved.

namespace eval ::rep {}
array set ::rep::_data {}
set ::rep::Win 0

# ::rep::create
#   Create a new repertoire widget.  $args become the configuration
#   arguments to the canvas widget that is created.
#
proc ::rep::create {w args} {
  eval canvas $w -bg white -width 1 $args
  bind $w <Destroy> "catch {::rep::clear $w}"
  ::rep::_defaults $w /
  ::rep::_drawWhenIdle $w
  set ::rep::_data($w:selection) {}
  set ::rep::_data($w:selidx) {}
  set ::rep::_data($w:altered) 0
  set ::rep::_data($w:nlines) 0
  set ::rep::_data($w:ngroups) 0
  set ::rep::_data($w:filename) ""
  #set ::rep::_data($w:readonly) 0
}

# ::rep::_defaults
#   Used to initialize a new element of the tree.
#
proc ::rep::_defaults {w v} {
  set ::rep::_data($w:$v:children) {}
  set ::rep::_data($w:$v:shown) 1
  set ::rep::_data($w:$v:tags) {}
  set ::rep::_data($w:$v:comment) {}
  set ::rep::_data($w:$v:text) {}
  set ::rep::_data($w:$v:group) 0
  set ::rep::_data($w:$v:include) 1
}

# ::rep::_findGroup
#   Finds the parent group of a line or group that will be added.
#
proc ::rep::_findGroup {w v parent} {
  set p $parent
  if {$parent == ""} { set p "/" }
  foreach g $::rep::_data($w:$p:children) {
    set len [string length $g]
    set v2 [string range $v 0 [expr {$len - 1} ]]
    
    # If the next character in the line is a "-", it means we have a
    # situation with both the castling moves "O-O" and "O-O-O".
    # To avoid "O-O-O" looking like a child line of "O-O", we need to
    # deliberately ignore this possibility.
    if {[string length $v] > $len  &&  [string index $v $len] == "-"} { continue }
    
    regsub -all " " $v2 "," v2
    if {! [string compare $v2 $g]} {
      set v [string trim [string range $v $len end]]
      return [::rep::_findGroup $w $v $parent/$g]
    }
  }
  set v [string trim $v]
  regsub -all " " $v "," v
  return $parent/$v
}

# ::rep::AddCurrentBoard
#   Adds the line representing the current board position as a
#   group, included line or excluded line.
#
proc ::rep::AddCurrentBoard {w type} {
  switch -exact -- $type {
    group   { set grp 1; set inc 0; set cmd ::rep::newGroup }
    include { set grp 0; set inc 1; set cmd ::rep::newLine  }
    exclude { set grp 0; set inc 0; set cmd ::rep::newLine  }
    default { return }
  }
  # Verify that the current game began at the standard starting position:
  if {[sc_game startBoard]} {
    tk_messageBox -parent $w -type ok -icon info -title "Scid" \
        -message "The current game does not have the standard starting position, so its positions cannot be added to the repertoire."
    return
  }
  set moves [string trim [sc_game moves]]
  set err [catch {$cmd $w $moves -group $grp -include $inc} res]
  if {$err} {
    tk_messageBox -parent $w -type ok -icon info -title "Scid" -message $res
    return
  }
  ::rep::regroup $w
  ::rep::singleClick $w $res
}

# ::rep::newGroup
#   Adds a new group to the repertoire.
#
proc ::rep::newGroup {w v args} {
  if {$v == ""} {
    return -code error "The starting chess position cannot be a group."
  }
  set s [::rep::_findGroup $w $v ""]
  lappend args -group 1
  return [::rep::_newItem $w $s $args]
}

# ::rep::newLine
#   Adds a new group to the repertoire.
#
proc ::rep::newLine {w v args} {
  set s [::rep::_findGroup $w $v ""]
  lappend args -group 0
  return [::rep::_newItem $w "${s} ,LINE" $args]
}

# ::rep::_decode
#   Decodes a group or line path back to standard chess notation,
#   e.g. "/1.e4_c5/2.Nf3_d6" -> "1.e4 c5 2.Nf3 d6"
#
proc ::rep::_decode {m} {
  regsub -all "," $m " " moves
  regsub -all "/" $moves " " moves
  regsub "LINE" $moves "" moves
  set moves [string trim $moves]
  return $moves
}

# ::rep::_newItem
#   Called from newGroup or newLine to add a new element to the repertoire.
#
proc ::rep::_newItem {w v args} {
  regsub -all " " $v "," v
  set dir [file dirname $v]
  set n [file tail $v]
  if {[llength $args] == 1} {
    set args [lindex $args 0]
  }
  set image {}
  set tags {}
  set text {}
  set comment {}
  set group 0
  set include 1
  set shown 1
  
  foreach {op arg} $args {
    switch -exact -- $op {
      -image {set image $arg}
      -tags {set tags $arg}
      -text {set text $arg}
      -comment {set comment $arg}
      -group {set group $arg}
      -include {set include $arg}
      -shown {set shown $arg}
    }
  }
  if {![info exists ::rep::_data($w:$dir:shown)]} {
    return -code error "parent item \"$dir\" is missing"
  }
  set i [lsearch -exact $::rep::_data($w:$dir:children) $n]
  if {$i >= 0} {
    # Group or line already exists.
    if {! $group} {
      set ::rep::_data($w:$v:include) $include
      ::rep::updateStatus $w
    }
    return $v
    #set  type "line"
    #if {$group} { set type "group" }
    #return -code error "The $type \"[::rep::_decode $v]\" already exists in this repertoire."
  }
  if {$group} {
    incr ::rep::_data($w:ngroups)
  } else {
    incr ::rep::_data($w:nlines)
  }
  
  lappend ::rep::_data($w:$dir:children) $n
  set ::rep::_data($w:$dir:children) [lsort $::rep::_data($w:$dir:children)]
  ::rep::_defaults $w $v
  set ::rep::_data($w:$v:comment) $comment
  set ::rep::_data($w:$v:image) $image
  set ::rep::_data($w:$v:tags) $tags
  set ::rep::_data($w:$v:text) $text
  set ::rep::_data($w:$v:group) $group
  set ::rep::_data($w:$v:include) $include
  set ::rep::_data($w:$v:shown) $shown
  set ::rep::_data($w:altered) 1
  ::rep::updateStatus $w
  # ::rep::_drawWhenIdle $w
  return $v
}

# ::rep::updateStatus
#   Updates the status bar message for the repertoire window.
#
proc ::rep::updateStatus {w} {
  set s "  "
  if {$::rep::_data($w:altered)} { append s "XX" } else { append s "--" }
  append s "  "
  set f $::rep::_data($w:filename)
  if {$f == ""} {
    set f "(Untitled)"
  } else {
    set f [file tail $f]
  }
  append s "$f: $::rep::_data($w:nlines) lines"
  append s " in $::rep::_data($w:ngroups) groups"
  set ::rep::_data($w:status) $s
}

# ::rep::updateWinTitle
#   Updates the repertoire window title.
#
proc ::rep::updateWinTitle {w} {
  set f $::rep::_data($w:filename)
  if {$f == ""} {
    set f "(Untitled)"
  } else {
    set f [file tail $f]
  }
  wm title [winfo toplevel $w] "Scid: [tr WindowsRepertoire]: $f"
}

# ::rep::regroup
#   Calls ::rep::_extract to produce a set of commands for rebuilding
#   the repertoire heirarchy, then clears and rebuilds it.
#   Used to reorganise the repertoire whenever a group is added or
#   removed.
proc ::rep::regroup {w} {
  set list [::rep::_extract $w / {}]
  ::rep::clear $w
  foreach i $list {eval $i}
  ::rep::_drawWhenIdle $w
}

# ::rep::newFile
#   Clears the repertoire and reset the filename to be untitled.
#
proc ::rep::newFile {w} {
  ::rep::clear $w
  set ::rep::_data($w:filename) ""
  #set ::rep::_data($w:readonly) 0
  ::rep::updateWinTitle $w
  ::rep::_drawWhenIdle $w
}

# ::rep::clear
#   Clears the repertoire, but keeps its filename.
#
proc ::rep::clear {w} {
  set fname ""
  set fname $::rep::_data($w:filename)
  foreach t [array names ::rep::_data $w:*] {
    unset ::rep::_data($t)
  }
  set ::rep::_data($w:filename) $fname
  set ::rep::_data($w:selection) {}
  set ::rep::_data($w:selidx) {}
  set ::rep::_data($w:altered) 0
  set ::rep::_data($w:nlines) 0
  set ::rep::_data($w:ngroups) 0
  ::rep::_defaults $w /
}

# ::rep::deleteGroup
#   After verification from the user, this deletes the specified
#   group but keeps all subgroups and lines.
#
proc ::rep::deleteGroup {w v} {
  if {! [info exists ::rep::_data($w:$v:group)]} { return }
  if {! $::rep::_data($w:$v:group)} { return }
  set v2 [::rep::_decode $v]
  set msg "The group \"$v2\" contains [::rep::_numChildLines $w $v] lines "
  append msg "in [expr [::rep::_numChildGroups $w $v] - 1] subgroups.\n\n"
  append msg "Are you sure you want to delete this group, keeping all the groups and lines under it?"
  set answer [tk_messageBox -parent $w -title "Scid: Delete group?" \
      -type yesno -icon question -message $msg]
  if {$answer != "yes"} { return }
  set ::rep::_data($w:$v:delete) 1
  ::rep::regroup $w
}

# ::rep::pruneGroup
#   After verification from the user, this deletes the specified
#   group AND all its subgroups and lines.
#
proc ::rep::pruneGroup {w v} {
  if {! [info exists ::rep::_data($w:$v:group)]} { return }
  if {! $::rep::_data($w:$v:group)} { return }
  set v2 [::rep::_decode $v]
  set msg "The group \"$v2\" contains [::rep::_numChildLines $w $v] lines "
  append msg "in [expr [::rep::_numChildGroups $w $v] - 1] subgroups.\n\n"
  append msg "Are you sure you want to delete this group AND ALL the groups and lines under it?"
  set answer [tk_messageBox -parent $w -title "Scid: Delete group?" \
      -type yesno -icon question -message $msg]
  if {$answer != "yes"} { return }
  set ::rep::_data($w:$v:delete) 1
  set ::rep::_data($w:$v:prune) 1
  ::rep::regroup $w
}

# ::rep::deleteGroup
#   After verification from the user, this deletes the specified line.
#
proc ::rep::deleteLine {w v} {
  if {! [info exists ::rep::_data($w:$v:group)]} { return }
  if {$::rep::_data($w:$v:group)} { return }
  set v2 [::rep::_decode $v]
  set msg "Delete: $v2\n\n"
  append msg "Are you sure you want to delete this line?"
  set answer [tk_messageBox -parent $w -title "Scid: Delete line?" \
      -type yesno -icon question -message $msg]
  if {$answer != "yes"} { return }
  set ::rep::_data($w:$v:delete) 1
  ::rep::regroup $w
}

# ::rep::showAll
#   Expands the specified group and all its subgroups.
#
proc ::rep::showAll {w {v ""}} {
  foreach i [array names ::rep::_data $w:$v*:shown] {
    set ::rep::_data($i) 1
  }
  ::rep::_drawWhenIdle $w
}

# ::rep::hideAll
#   Collapses the specified group and all its subgroups.
#
proc ::rep::hideAll {w {v ""}} {
  foreach i [array names ::rep::_data $w:$v*:shown] {
    set ::rep::_data($i) 0
  }
  ::rep::_drawWhenIdle $w
}

# ::rep::setSelection
#   Changes the selection to the specified group or line.
#
proc ::rep::setSelection {w v} {
  set ::rep::_data($w:selection) $v
  ::rep::_drawSelection $w
  ::rep::updateStatus $w
}

# ::rep::getSelection
#   Retrieves the current selection.
#
proc ::rep::getSelection {w} {
  return $::rep::_data($w:selection)
}

# ::rep::_numChildGroups
#   Returns the number of (direct and indirect) subgroups of a group.
#
proc ::rep::_numChildGroups {w v} {
  if {! $::rep::_data($w:$v:group)} { return 0 }
  set count 1
  if {[info exists ::rep::_data($w:$v:children)]} {
    foreach child $::rep::_data($w:$v:children) {
      incr count [::rep::_numChildGroups $w $v/$child]
    }
  }
  return $count
}


# ::rep::_numChildLines
#   Returns the number of (direct and indirect) child lines of a group.
#
proc ::rep::_numChildLines {w v} {
  if {! $::rep::_data($w:$v:group)} { return 1 }
  set count 0
  if {[info exists ::rep::_data($w:$v:children)]} {
    foreach child $::rep::_data($w:$v:children) {
      incr count [::rep::_numChildLines $w $v/$child]
    }
  }
  return $count
}

# ::rep::_draw
#   Draws the repertoire heirarchy.
#
proc ::rep::_draw {w} {
  catch {unset ::rep::_data($w:drawPending)}
  $w delete all
  set ::rep::_data($w:y) 30
  ::rep::_drawLevel $w "/" 10
  $w config -scrollregion [$w bbox all]
  ::rep::_drawSelection $w
  ::rep::updateStatus $w
  $w xview moveto 0.0
}

# ::rep::_drawLevel
#   Draws a single level of the heirarchy, indented by $in pixels.
#
proc ::rep::_drawLevel {w v in} {
  set p $v
  if {$v == "/"} { set p "" }
  set start [expr {$::rep::_data($w:y)-10} ]
  set y 0
  if {[llength $::rep::_data($w:$v:children)] == 0} {
    set y $::rep::_data($w:y)
    incr ::rep::_data($w:y) 17
    $w create line $in $y [expr {$in+10} ] $y -fill gray50
    incr in 12
    $w create text $in $y -font font_Small -anchor w -text "(empty)"
    return
  }
  
  foreach c $::rep::_data($w:$v:children) {
    set y $::rep::_data($w:y)
    incr ::rep::_data($w:y) 17
    $w create line $in $y [expr {$in + 10} ] $y -fill gray50
    set group $::rep::_data($w:$p/$c:group)
    set icon $::rep::_data($w:$p/$c:image)
    set taglist x
    foreach tag $::rep::_data($w:$p/$c:tags) { lappend taglist $tag }
    if {$group} {
      set icon ::rep::_closedgroup
      if {$::rep::_data($w:$p/$c:shown)} { set icon ::rep::_opengroup }
    } else {
      set icon ::rep::_cross
      if {$::rep::_data($w:$p/$c:include)} { set icon ::rep::_tick }
    }
    set x [expr {$in + 12} ]
    
    if {$icon != ""} {
      set tag [$w create image $x $y -image $icon -anchor w -tags $taglist]
      incr x 20
      set ::rep::_data($w:tag:$tag) $p/$c
      if {$group} {
        set s [expr {1 - $::rep::_data($w:$p/$c:shown)} ]
        $w bind $tag <1> "set ::rep::_data($w:$p/$c:shown) $s; ::rep::_draw $w"
      }
      $w bind $tag <$::MB3> "::rep::_popupMenu $w $p/$c %X %Y"
    }
    set moves [::rep::_decode $c]
    if {$moves == ""} { set moves "..." }
    if {$group} {
      append moves " ([::rep::_numChildLines $w $p/$c])"
    }
    set tag [$w create text $x $y -text $moves -font font_Small \
        -anchor w -tags $taglist]
    set ::rep::_data($w:tag:$tag) $p/$c
    set ::rep::_data($w:$p/$c:tag) $tag
    $w bind $tag <$::MB3> "::rep::_popupMenu $w $p/$c %X %Y"
    set comment ""
    if {[string length $::rep::_data($w:$p/$c:text)] > 0} {
      set comment "** "
    }
    if {[string length $::rep::_data($w:$p/$c:comment)] > 0} {
      append comment "$::rep::_data($w:$p/$c:comment)"
    }
    if {$comment != ""} {
      incr x [expr {3 + [lindex [$w bbox $tag] 2] - [lindex [$w bbox $tag] 0]} ]
      set tag [$w create text $x $y -text $comment -font font_Small \
          -fill red3 -anchor w -tags $taglist]
      $w bind $tag <$::MB3> "::rep::_popupMenu $w $p/$c %X %Y"
      set ::rep::_data($w:tag:$tag) $p/$c
    }
    
    if {[llength $::rep::_data($w:$p/$c:children)] > 0} {
      if {$::rep::_data($w:$p/$c:shown)} {
        set tag [$w create image $in $y -image ::rep::_shown]
        $w bind $tag <1> "set ::rep::_data($w:$p/$c:shown) 0; ::rep::_draw $w"
        ::rep::_drawLevel $w $p/$c [expr {$in + 18} ]
      } else {
        set tag [$w create image $in $y -image ::rep::_hidden]
        $w bind $tag <1> "set ::rep::_data($w:$p/$c:shown) 1; ::rep::_draw $w"
      }
      $w bind $tag <$::MB3> "::rep::_popupMenu $w $p/$c %X %Y"
    }
  }
  set tag [$w create line $in $start $in [expr {$y+1} ] -fill gray50 ]
  $w lower $tag
}

# ::rep::showGroup
#   Expand a single group.
#
proc ::rep::showGroup {w v} {
  if {[info exists ::rep::_data($w:$v:shown)]
    &&  $::rep::_data($w:$v:shown) == 0
    &&  [info exists ::rep::_data($w:$v:children)]
    &&  [llength $::rep::_data($w:$v:children)] > 0} {
    set ::rep::_data($w:$v:shown) 1
    ::rep::_draw $w
  }
}

# ::rep::showGroup
#   Collapse a single group.
#
proc ::rep::hideGroup {w v} {
  if {[info exists ::rep::_data($w:$v:shown)]
    &&  $::rep::_data($w:$v:shown) == 1} {
    set ::rep::_data($w:$v:shown) 0
    ::rep::_draw $w
  }
}

# ::rep::toggleLineState
#   Change the state of a line (included vs excluded).
#
proc ::rep::toggleLineState {w v} {
  if {[info exists ::rep::_data($w:$v:group)]
    &&  $::rep::_data($w:$v:group) == 0} {
    set state $::rep::_data($w:$v:include)
    set state [expr {1 - $state} ]
    set ::rep::_data($w:$v:include) $state
    set ::rep::_data($w:altered) 1
    ::rep::_draw $w
  }
}

# ::rep::_drawSelection
#   Highlight the selected goupr or line.
#
proc ::rep::_drawSelection w {
  if {[string length $::rep::_data($w:selidx)]} {
    $w delete $::rep::_data($w:selidx)
  }
  set v $::rep::_data($w:selection)
  if {[string length $v]==0} { return }
  if {![info exists ::rep::_data($w:$v:tag)]} { return }
  set bbox [$w bbox $::rep::_data($w:$v:tag)]
  if {[llength $bbox]==4} {
    set i [eval $w create rectangle $bbox -fill yellow2 -outline {{}}]
    set ::rep::_data($w:selidx) $i
    $w lower $i
  } else {
    set ::rep::_data($w:selidx) {}
  }
}

# ::rep::_drawWhenIdle
#   Schedule a redraw event of the repertoire heirarchy.
#
proc ::rep::_drawWhenIdle w {
  if {![info exists ::rep::_data($w:drawPending)]} {
    set ::rep::_data($w:drawPending) 1
    after idle "::rep::_draw $w"
  }
}

# ::rep::labelAtXY
#   Return the group or line in the repertoire located at the
#   real coordinates ($x,$y).
#
proc ::rep::labelAtXY {w x y} {
  set x [$w canvasx $x]
  set y [$w canvasy $y]
  foreach m [$w find overlapping $x $y $x $y] {
    if {[info exists ::rep::_data($w:tag:$m)]} {
      return $::rep::_data($w:tag:$m)
    }
  }
  return ""
}

# ::rep::OpenCloseWindow
#   Open/close the repertoire editor.
#
proc ::rep::OpenCloseWindow {} {
  set w .repWin
  if {[winfo exists $w]} {
    destroy $w
    return
  }
  ::rep::makeWindow
}

# ::rep::closeWindow
#   Close the repertoire editor.
#
proc ::rep::closeWindow {} {
  set w .repWin
  if {! [winfo exists $w]} { return }
  if {$::rep::_data($w.f.w.rep:altered)} {
    set answer [tk_messageBox -parent .repWin \
        -type yesno -icon question \
        -title "Scid: [tr RepFileClose]" \
        -message $::tr(RepCloseDialog)]
    if {$answer != "yes"} { return }
  }
  destroy $w
  set ::rep::Win 0
}

# ::rep::OpenWithFile
#   Open the repertoire editor and losd the specified file.
#
proc ::rep::OpenWithFile {fname} {
  ::rep::makeWindow
  ::rep::readFile .repWin.f.w.rep $fname
}

# ::rep::ConfigMenus
#   Called to set the window menus to a specified language.
proc ::rep::ConfigMenus {w {lang ""}} {
  if {! [winfo exists $w]} { return }
  if {$lang == ""} { set lang $::language }
  set m $w.menu
  foreach idx {0 1 2 3 4} tag {File Edit View Search Help} {
    configMenuText $m $idx Rep$tag $lang
  }
  foreach idx {0 1 3 4 6} tag {New Open Save SaveAs Close} {
    configMenuText $m.file $idx RepFile$tag $lang
  }
  foreach idx {0 1 2} tag {Group Include Exclude} {
    configMenuText $m.edit $idx RepEdit$tag $lang
  }
  foreach idx {0 1} tag {Expand Collapse} {
    configMenuText $m.view $idx RepView$tag $lang
  }
  foreach idx {0 1} tag {All Displayed} {
    configMenuText $m.search $idx RepSearch$tag $lang
  }
  foreach idx {0 1} tag {Rep Index} {
    configMenuText $m.helpmenu $idx RepHelp$tag $lang
  }
}

# ::rep::makeWindow
#   Create the repertoire editor window.
#
proc ::rep::makeWindow {} {
  set w .repWin
  if {[winfo exists $w]} { return }
  set ::rep::Win 1
  toplevel $w
  
  standardShortcuts $w
  wm protocol $w WM_DELETE_WINDOW ::rep::closeWindow
  
  frame $w.m -bd 1 -relief raised
  pack $w.m -side top -fill x
  menu $w.menu
  $w configure -menu $w.menu
  $w.menu add cascade -label RepFile -menu $w.menu.file
  menu $w.menu.file
  $w.menu.file add command -label RepFileNew -accelerator "Ctrl+N" \
      -command "::rep::newFile $w.f.w.rep"
  bind $w <Control-n> "$w.menu.file invoke 0"
  $w.menu.file add command -label RepFileOpen -accelerator "Ctrl+O" \
      -command "::rep::OpenFile $w.f.w.rep"
  bind $w <Control-o> "$w.menu.file invoke 1"
  $w.menu.file add separator
  $w.menu.file add command -label RepFileSave -accelerator "Ctrl+S" \
      -command "::rep::saveFile $w.f.w.rep"
  bind $w <Control-s> "$w.menu.file invoke 3"
  $w.menu.file add command -label RepFileSaveAs \
      -command "::rep::saveFile $w.f.w.rep new"
  $w.menu.file add separator
  $w.menu.file add command -label RepFileClose -accelerator "Ctrl+Q" \
      -command "destroy $w"
  bind $w <Control-q> "destroy $w"
  
  $w.menu add cascade -label RepEdit -menu $w.menu.edit
  menu $w.menu.edit
  $w.menu.edit add command -label RepEditGroup -accelerator "Ctrl+G" \
      -command "::rep::AddCurrentBoard $w.f.w.rep group"
  bind $w <Control-g> "$w.menu.edit invoke 0"
  $w.menu.edit add command -label RepEditInclude -accelerator "Ctrl+I" \
      -command "::rep::AddCurrentBoard $w.f.w.rep include"
  bind $w <Control-i> "$w.menu.edit invoke 1"
  $w.menu.edit add command -label RepEditExclude -accelerator "Ctrl+X" \
      -command "::rep::AddCurrentBoard $w.f.w.rep exclude"
  bind $w <Control-x> "$w.menu.edit invoke 2"
  
  $w.menu add cascade -label RepView -menu $w.menu.view
  menu $w.menu.view
  $w.menu.view add command -label RepViewExpand \
      -command "::rep::showAll $w.f.w.rep"
  $w.menu.view add command -label RepViewCollapse \
      -command "::rep::hideAll $w.f.w.rep"
  
  $w.menu add cascade -label RepSearch -menu $w.menu.search
  menu $w.menu.search
  $w.menu.search add command -label RepSearchAll \
      -command "::rep::search $w.f.w.rep all"
  $w.menu.search add command -label RepSearchDisplayed \
      -command "::rep::search $w.f.w.rep displayed"
  
  $w.menu add cascade -label RepHelp -menu $w.menu.helpmenu
  menu $w.menu.helpmenu
  $w.menu.helpmenu add command -label RepHelpRep -command {helpWindow Repertoire}
  $w.menu.helpmenu add command -label RepHelpIndex -command {helpWindow Index}
  
  
  # Toolbar:
  set f [frame $w.toolbar -relief raised -border 1]
  pack $f -side top -fill x
  button $f.new -image tb_new -command "::rep::newFile $w.f.w.rep"
  button $f.open -image tb_open -command "::rep::OpenFile $w.f.w.rep"
  button $f.save -image tb_save -command "::rep::saveFile $w.f.w.rep"
  button $f.close -image tb_close -command "destroy $w"
  frame $f.space1 -width 12
  button $f.group -image ::rep::_tb_group \
      -command "::rep::AddCurrentBoard $w.f.w.rep group"
  button $f.include -image ::rep::_tb_include \
      -command "::rep::AddCurrentBoard $w.f.w.rep include"
  button $f.exclude -image ::rep::_tb_exclude \
      -command "::rep::AddCurrentBoard $w.f.w.rep exclude"
  
  foreach i {new open save close group include exclude} {
    $f.$i configure -relief flat -border 1 -highlightthickness 0 -anchor n \
        -takefocus 0
    bind $f.$i <Any-Enter> "+$f.$i configure -relief groove"
    bind $f.$i <Any-Leave> "+$f.$i configure -relief flat"
  }
  foreach {b m} {
    new RepFileNew open RepFileOpen save RepFileSave
    group RepEditGroup include RepEditInclude exclude RepEditExclude
  } {
    set ::helpMessage($f.$b) [tr $m]
  }
  pack $f.new $f.open $f.save $f.close $f.space1 \
      $f.group $f.include $f.exclude \
      -side left -padx 0 -ipadx 0 -pady 0 -ipady 0 -expand yes
  
  label $w.status -relief sunken -width 1 -anchor w -font font_Small
  pack $w.status -side bottom -anchor w -fill x -expand yes
  
  set pane [::utils::pane::Create $w.f w text 600 600 0.5 h] ;# 600 300
  ::utils::pane::SetRange $w.f 0.2 0.8
  #::pane::SetDrag $w.f 0
  pack $w.f -fill both -expand 1
  
  ::rep::create $w.f.w.rep -yscrollcommand "$w.f.w.sb set"
  scrollbar $w.f.w.sb -orient vertical -command "$w.f.w.rep yview"
  pack $w.f.w.sb -side right -fill y
  pack $w.f.w.rep -side left -fill both -expand 1 -padx 5 -pady 5
  text $w.f.text.moves -height 3 -fg darkBlue -bg white -font font_Small -state disabled -cursor top_left_arrow -wrap word
  entry $w.f.text.entry -width 1 -fg black -bg white -font font_Small
  text $w.f.text.note -width 40 -height 10 -fg black -bg white -font font_Small
  pack $w.f.text.moves -side top -fill x
  pack $w.f.text.entry -side top -fill x
  pack $w.f.text.note -side top -expand 1 -fill both
  $w.status configure -textvar ::rep::_data($w.f.w.rep:status)
  
  bind $w <F1> {helpWindow Repertoire}
  bind $w <Down> "$w.f.w.rep yview scroll +1 units"
  bind $w <Up> "$w.f.w.rep yview scroll -1 units"
  bind $w <Prior> "$w.f.w.rep yview scroll -1 pages"
  bind $w <Next> "$w.f.w.rep yview scroll +1 pages"
  bind $w <Home> "$w.f.w.rep yview moveto 0.0"
  bind $w <End> "$w.f.w.rep yview moveto 0.99"
  $w.f.w.rep xview moveto 0.0
  $w.f.w.rep yview moveto 0.0
  
  $w.f.w.rep bind x <1> \
      "::rep::singleClick $w.f.w.rep \[::rep::labelAtXY %W %x %y\]"
  $w.f.w.rep bind x <Double-Button-1> \
      "::rep::doubleClick $w.f.w.rep \[::rep::labelAtXY %W %x %y\]"
  
  bind $w.f.text.moves <1> "if {\[string length \[$w.f.text.moves get 1.0 end\]\] > 1} { importMoveList \[$w.f.text.moves get 1.0 end\] }"
  
  bind $w.f.text.entry <KeyPress> {
    after idle {
      set label [::rep::getSelection .repWin.f.w.rep]
      if {$label != ""} {
        set ::rep::_data(.repWin.f.w.rep:$label:comment) \
            [string trim [.repWin.f.text.entry get]]
        ::rep::_draw .repWin.f.w.rep
      }
    }
  }
  
  bind $w.f.text.note <KeyPress> {
    after idle {
      set label [::rep::getSelection .repWin.f.w.rep]
      if {$label != ""} {
        set ::rep::_data(.repWin.f.w.rep:$label:text) \
            [string trim [.repWin.f.text.note get 1.0 end]]
      }
    }
  }
  
  ::rep::ConfigMenus $w
  ::rep::updateWinTitle $w.f.w.rep
  wm geometry $w =500x400
}

# ::rep::singleClick
#   Updates the move list label, single-line comment entry widget
#   and multi-line comment text widget in the repertoire editor.
#   Called whenever the left mouse button is clicked on a group or
#   line in the repertoire.
#
proc ::rep::singleClick {w label} {
  ::rep::setSelection $w $label
  if {$label == ""} { return }
  set moves [::rep::_decode $label]
  set win [winfo toplevel $w]
  $win.f.text.moves configure -state normal
  $win.f.text.moves delete 1.0 end
  $win.f.text.moves insert end $moves
  $win.f.text.moves configure -state disabled
  set comment ""
  catch {set comment $::rep::_data($w:$label:comment)}
  $win.f.text.entry delete 0 end
  $win.f.text.entry insert end $comment
  set temptext ""
  catch {set temptext $::rep::_data($w:$label:text)}
  set temptext [string trim $temptext]
  $win.f.text.note delete 1.0 end
  $win.f.text.note insert end $temptext
}

# ::rep::doubleClick
#   Called whenever the left mouse button is double-clicked on a group or
#   line in the repertoire.
#
proc ::rep::doubleClick {w label} {
  ::rep::setSelection $w $label
  if {$label == ""} { return }
  set moves [::rep::_decode $label]
  catch {sc_game import $moves}
  updateBoard -pgn
}

# ::rep::_extract
#   Traverses the repertoire heirarchy, producing a list of
#   commands needed to reconstruct the entire repertoire.
#   If a line or group is marked deleted, it is not included in
#   the list. If a group is marked pruned, its children are not
#   included either.
#
proc ::rep::_extract {w v returnList} {
  set p $v
  if {$v == "/"} { set p "" }
  foreach c $::rep::_data($w:$v:children) {
    set text [string trim $::rep::_data($w:$p/$c:text)]
    set comment [string trim $::rep::_data($w:$p/$c:comment)]
    set moves [::rep::_decode $p/$c]
    
    set recurse 0
    if {$::rep::_data($w:$p/$c:group)} {
      if {! [info exists ::rep::_data($w:$p/$c:prune)]} { set recurse 1 }
      set cmd "::rep::newGroup $w [list $moves]"
      append cmd " -shown $::rep::_data($w:$p/$c:shown)"
    } else {
      set cmd "::rep::newLine $w [list $moves]"
      append cmd " -include $::rep::_data($w:$p/$c:include)"
    }
    append cmd " -comment [list $comment] -text [list $text]"
    if {! [info exists ::rep::_data($w:$p/$c:delete)]} {
      lappend returnList $cmd
    }
    
    if {$recurse  &&  [llength $::rep::_data($w:$p/$c:children)] > 0} {
      set returnList [::rep::_extract $w $p/$c $returnList]
    }
  }
  return $returnList
}

# ::rep::_searchLines
#   Traverses the repertoire heirarchy, producing a list of
#   lines to be searched. If type is "all", all lines in the
#   repertoire will appear. Otherwise, type should be "displayed"
#   and only currently displayed lines will appear.
#   Each include-line returned has "1 " prepended to its moves, and
#   each exclude-line returned has "0 " prepended to its moves.
#
proc ::rep::_searchLines {w type v returnList} {
  set p $v
  if {$v == "/"} { set p "" }
  foreach c $::rep::_data($w:$v:children) {
    set text [string trim $::rep::_data($w:$p/$c:text)]
    set comment [string trim $::rep::_data($w:$p/$c:comment)]
    set moves [::rep::_decode $p/$c]
    
    set recurse 0
    if {$::rep::_data($w:$p/$c:group)} {
      if {$type == "all"  ||  $::rep::_data($w:$p/$c:shown)} {
        set recurse 1
      }
    } else {
      lappend returnList "$::rep::_data($w:$p/$c:include) $moves"
    }
    
    if {$recurse  &&  [llength $::rep::_data($w:$p/$c:children)] > 0} {
      set returnList [::rep::_searchLines $w $type $p/$c $returnList]
    }
  }
  return $returnList
}

# ::rep::readFile
#   Reads the specified repertoire file.
#
proc ::rep::readFile {w fname} {
  #set readonly 0
  #if {[catch {open $fname r+} f]} {
  #  set readonly 1
  #} else {
  #  close $f
  #}
  
  if {[catch {open $fname r} f]} {
    return -code error "Unable to open the file: $fname"
  }
  ::rep::clear $w
  set ::rep::_data($w:altered) 1
  #set ::rep::_data($w:readonly) 1
  set text ""
  set count 0
  set groups {}
  while {1} {
    set line [string trim [gets $f]]
    if {[eof $f]} { break }
    incr count
    set sep [string first ";" $line]
    if {$sep < 0} {
      set moves $line
      set comment ""
    } else {
      set moves [string trim [string range $line 0 [expr {$sep - 1} ]]]
      set comment [string trim [string range $line [expr {$sep + 1} ] end]]
    }
    set c [string index $line 0]
    
    switch -exact -- $c {
      "\#" {
        set line [string trim [string range $line 1 end]]
        append text "$line\n"
      }
      "\[" {
        set group [string trim [string range $moves 1 end]]
        set m [join $groups]
        append m " $group"
        if {[catch {::rep::newGroup $w $m -comment $comment -text $text} err]} {
          return -code error "Error: $fname: line $count: $err"
        }
        lappend groups $group
        set text ""
      }
      "\]" {
        set len [llength $groups]
        if {$len == 0} {
          return -code error "Error: $fname: line $count: extra \"\]\" symbol."
        } elseif {$len == 1} {
          set groups {}
        } else {
          set groups [lrange $groups 0 [expr {$len - 2} ]]
        }
      }
      "-" {
        set m [join $groups]
        append m " "
        append m [string trim [string range $moves 1 end]]
        if {[catch {::rep::newLine $w $m -include 0 -comment $comment -text $text} err]} {
          return -code error "Error: $fname: line $count: $err"
        }
        set text ""
      }
      "+" {
        set m [join $groups]
        append m " "
        append m [string trim [string range $moves 1 end]]
        if {[catch {::rep::newLine $w $m -include 1 -comment $comment -text $text} err]} {
          return -code error "Error: $fname: line $count: $err"
        }
        set text ""
      }
      "" -
      "@" {
        # do nothing
      }
      default {
        return -code error "Error in $fname at line $count: unexpected character: \"$c\""
      }
    }
  }
  close $f
  set ::rep::_data($w:altered) 0
  set ::rep::_data($w:filename) $fname
  ::rep::_drawWhenIdle $w
  ::rep::updateWinTitle $w
  return
}

# ::rep::writeFile
#   Writes the repertoire to the specified file.
#
proc ::rep::writeFile {w fname} {
  if {[catch {open $fname w} f]} {
    return -code error "Unable to open the file \"$fname\""
  }
  puts $f "@ Scid opening repertoire file.  Updated: [::utils::date::today]."
  if {[catch {::rep::_writeFileLevel $w $f "/" 0} err]} {
    return -code error "Error writing the file \"$fname\": $err"
  }
  close $f
  set ::rep::_data($w:altered) 0
  set ::rep::_data($w:filename) $fname
  ::rep::updateStatus $w
  ::rep::updateWinTitle $w
  return
}

# ::rep::_writeFileLevel
#   Writes a single level of the repertoire to the open
#   file channel "f", indented "in" spaces.
#
proc ::rep::_writeFileLevel {w f v in} {
  set p $v
  if {$v == "/"} { set p "" }
  if {[llength $::rep::_data($w:$v:children)] == 0} { return }
  
  foreach c $::rep::_data($w:$v:children) {
    if {$in == 0} { puts $f "" }
    set text [split [string trim $::rep::_data($w:$p/$c:text)] "\n"]
    foreach line $text {
      for {set i 0} {$i < $in} {incr i} { puts -nonewline $f " " }
      puts -nonewline $f "\# "
      puts $f [string trim $line]
    }
    
    for {set i 0} {$i < $in} {incr i} { puts -nonewline $f " " }
    if {$::rep::_data($w:$p/$c:group)} {
      puts -nonewline $f "\[ "
    } else {
      if {$::rep::_data($w:$p/$c:include)} {
        puts -nonewline $f "+ "
      } else {
        puts -nonewline $f "- "
      }
    }
    set moves [::rep::_decode $c]
    if {[string length $::rep::_data($w:$p/$c:comment)] > 0} {
      append moves " ; $::rep::_data($w:$p/$c:comment)"
    }
    puts $f $moves
    if {[llength $::rep::_data($w:$p/$c:children)] > 0} {
      ::rep::_writeFileLevel $w $f $p/$c [expr {$in + 4} ]
    }
    if {$::rep::_data($w:$p/$c:group)} {
      for {set i 0} {$i < $in} {incr i} { puts -nonewline $f " " }
      puts $f "\]"
    }
  }
}

# ::rep::OpenFile
#   Prompts the user to select a repertoire file, and reads it.
#
proc ::rep::OpenFile {w} {
  set ftype { {"Scid repertoire files" {".sor"}} }
  set fname [tk_getOpenFile -filetypes $ftype -title "Open a repertoire file"]
  if {$fname == ""} { return }
  ::rep::readFile $w $fname
}

# ::rep::saveFile
#   Prompts the user to select a repertoire file name if the file is
#   untitled or if the user type is "new", and then writes the file.
#
proc ::rep::saveFile {w {type current}} {
  set fname $::rep::_data($w:filename)
  if {$type == "new"  ||  $fname == ""} {
    set ftype { {"Scid repertoire files" {".sor"}} }
    set fname [tk_getSaveFile -filetypes $ftype -defaultextension ".sor" -title "Create a repertoire file"]
  }
  if {$fname == ""} { return }
  if {[file extension $fname] != ".sor" } {
    append fname ".sor"
  }
  ::rep::writeFile $w $fname
}

# ::rep::_popupMenu
#   Creates and presents a right-mouse-button popup menu for a
#   group or line at the real coordinates ($x,$y).
#
proc ::rep::_popupMenu {w v x y} {
  if {! [info exists ::rep::_data($w:$v:group)]} { return }
  catch {destroy $w.popup}
  set group $::rep::_data($w:$v:group)
  menu $w.popup
  $w.popup add command -label "Paste moves as current game" \
      -command "catch {sc_game import \"[::rep::_decode $v]\"};
  updateBoard -pgn"
  $w.popup add separator
  if {$group} {
    $w.popup add command -label "Expand group and all subgroups" \
        -command "::rep::showAll $w $v"
    $w.popup add command -label "Collapse group and all subgroups" \
        -command "::rep::hideAll $w $v"
    $w.popup add separator
    $w.popup add command -label "Delete group, keeping subgroups..." \
        -command "::rep::deleteGroup $w $v"
    $w.popup add command -label "Delete group and subgroups..." \
        -command "::rep::pruneGroup $w $v"
  } else {
    $w.popup add command -label "Toggle included/excluded state" \
        -command "::rep::toggleLineState $w $v"
    $w.popup add separator
    $w.popup add command -label "Delete line..." \
        -command "::rep::deleteLine $w $v"
  }
  tk_popup $w.popup $x $y
}

# ::rep::search
#   Opens the repertoire search window. The parameter "type" should be
#   "all" or "displayed", indicating which lines to use in the search.
#
proc ::rep::search {repwin {type all}} {
  sc_search repertoire clear
  set lines [::rep::_searchLines $repwin $type / {}]
  set numIncluded 0
  set numExcluded 0
  foreach i $lines {
    set include [string index $i 0]
    set moves [string range $i 2 end]
    sc_game push
    if {[catch {eval "sc_move addSan $moves"} result]} {
      sc_game pop
      tk_messageBox -parent $repwin -type ok -icon warning -title "Scid" \
          -message "Error in line \"$moves\": $result"
      sc_search repertoire clear
      return
    }
    sc_search repertoire add $include
    sc_game pop
    if {$include} { incr numIncluded } else { incr numExcluded }
  }
  if {$numIncluded == 0} {
    tk_messageBox -parent $repwin -type ok -icon info -title "Scid" \
        -message "The repertoire you want to search for has no included lines, so it cannot possibly match any games."
    return
  }
  
  set w .searchRep
  toplevel $w
  wm title $w "Scid: $::tr(RepSearch)"
  bind $w <Escape> "$w.b.cancel invoke"
  bind $w <Return> "$w.b.search invoke"
  bind $w <F1> {helpWindow Repertoire Search}
  
  pack [label $w.l -anchor w] -side top -fill x
  set t "$::tr(RepSearch): $numIncluded $::tr(RepIncludedLines), "
  append t "$numExcluded $::tr(RepExcludedLines)"
  $w.l configure -text $t
  
  ::search::addFilterOpFrame $w
  addHorizontalRule $w
  
  canvas $w.progress -height 20 -width 300 -bg white -relief solid -border 1
  $w.progress create rectangle 0 0 0 0 -fill blue -outline blue -tags bar
  $w.progress create text 295 10 -anchor e -font font_Regular -tags time \
      -fill black -text "0:00 / 0:00"
  
  frame $w.b
  pack $w.b -side top -ipady 5 -fill x
  
  set ::_searchRep $repwin
  button $w.b.search -textvar ::tr(Search) -command {
    busyCursor .
    grab .searchRep.b.cancel
    sc_progressBar .searchRep.progress bar 301 21 time
    .searchRep.b.cancel configure -command sc_progressBar
    set err [catch {sc_search repertoire go $::search::filter::operation} result]
    sc_search repertoire clear
    unbusyCursor .
    grab release .searchRep.b.cancel
    grab release .searchRep
    focus .repWin
    destroy .searchRep
    if {$err} {
      tk_messageBox -parent $::_searchRep -type ok -icon info -title "Scid" \
          -message $result
    } else {
      set ::rep::_data($::_searchRep:status) "  $::tr(RepSearch): $result"
    }
    set glstart 1
    ::windows::gamelist::Refresh
    ::windows::stats::Refresh
  }
  button $w.b.cancel -textvar ::tr(Cancel) \
      -command "grab release $w; focus $repwin; destroy $w"
  pack $w.b.cancel $w.b.search -side right -pady 5 -padx 5
  pack $w.progress -side bottom
  wm resizable $w 0 0
  grab $w
}


# Images and bitmaps used in heirarchical repertoire view.
# I found the open and closed folder images used at the sourceforge.net
# website, but i hope to find or create nicer-looking ones...

image create photo ::rep::_closedgroup -data {
  R0lGODdhDwANAIQAAP7+/AICBMa6la6ehJaJda6ihPbu6ubcwNbGtN7V2c7Dp97Opc66m8a1
  lLaniu7q2Xp0cHJuW+7m1LaulKaYfL6zl25mWJ6ObYJ5a1pVTQAAAAAAAAAAAAAAAAAAAAAA
  ACwAAAAADwANAAAFYSAgjmQ5BmhgkoEwuOoaDARdEGkqBsbR/wZE4qBQBQ6CBUO5EDQcAhwg
  8GgynVAHxHhYLgWCbCFilDAbYUFhQiFPu4tK2uGgUCzlBRpaqF8IeG8QBBgUGIQRFhYZMTmO
  OiEAOw==
}

#image create photo ::rep::_oldgroup -data {
#R0lGODdhEAAPAMIAAP////j4+Hh4eLi4uPj4AAAAAAAAAAAAACwAAAAAEAAPAAADPAi63BBB
#SAlrfWIQPYS92SZ2EwUIjiMUJzC+2tpy8CajNX0DdG+zOJ8OyMv9WsYYsMQssQKFqHQ6TVkV
#CQA7
#}

image create photo ::rep::_opengroup -data {
  R0lGODdhDwANAKUAAP7+/O7u7M7OzDo6NN7e3PLy5J6enNbW1K6urH5+fDo2LIZ+bK6qnJaW
  lGpqbFZSRF5eXG5ubG5mVLaunNLOxObm4HpyZGJiXA4ODBoWFJaOfHZuXI6GdObi3OLi5EpG
  RJqOfMrKzNrWzPb27Pr69CIiHH52ZJqSfBoaFFpWT4J6ZN7azCoqLGZeVKqqrLq2pO7q5IaG
  hKKahL6+vFZWVGpmVD4+PJKKdAYGBAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADwANAAAG
  kkCAcBgQCAYDwrBgOAAQhoRCsWA0BAaD4wGJQB6SCYVSsVwemIxmseF0PAUA5ONYgDIhhUY0
  IhESJSYcICcnKAQpKisADSwtIBonGpMZAC4fLyMoJyCdkJMoAB0RGzAZhJKchJUAMS0yKJOy
  HA8lM0M0JhmFnDU2BkNCBimnIDUPN8HBLCgWCgnAykMHODQCAdJBADs=
}

image create photo ::rep::_tick -data {
  R0lGODdhEAAQAKEAAP///wAAAFFR+wAAACwAAAAAEAAQAAACMISPacHtvpQKUSIKsBQiV8V1
  mIaEFWmYJ8Cl7TGynQpH4XtV9/ThuWepZR5ERtBSAAA7
}

image create photo ::rep::_cross -data {
  R0lGODdhEAAQAKEAAP///wAAAPoTQAAAACwAAAAAEAAQAAACL4SPacHtvpQKUSIKsFA7V9EZ
  YIUBoxliJXqSSeseIKzKXVujyJlbse/JPIYMoKQAADs=
}

image create photo ::rep::_tb_group -data {
  R0lGODlhEQARAIQAANnZ2QICBMa6la6ehJaJda6ihPbu6ubcwNbGtN7V2c7Dp97Opc66m8a1
  lLaniqmpqe7q2Xp0cHJuW+7m1LaulKaYfL6zl25mWJ6ObYJ5a1pVTScznicznicznicznicz
  niwAAAAAEQARAAAFdiAgjmRpnigQrEE6BsIQt2kwEHdBsCwZGAeg0IBIHBQ01UGwYDQXgoZD
  sHuIAhDoMzp1RAJWpdMpEHQLEvB18myYBQVKJR0OHJyWt8NRqVzUKmxuUwV8GAR/dQcRBBkV
  GY0SFxcagCo8mCsPYSKbnp+eLqKjIyEAOw==
}

image create photo ::rep::_tb_include -data {
  R0lGODlhEQARAKEAAP///9nZ2QAAAFFR+ywAAAAAEQARAAACOYyPecLtvoCcVIpY8zUizzEA
  W9B5YDiW1WlhFNtyACjBMTmH9t2ddJWq7XifkMbl4T2WDIXzCVUUAAA7
}

image create photo ::rep::_tb_exclude -data {
  R0lGODlhEQARAKEAANnZ2QAAAP////oTQCwAAAAAEQARAAACOoSPecHtvoScVIZY8zVBjvwJ
  G9AJQ+iFY2mG57RS5wtjE11z8kzF6W/B4FpBXabiO+ZIjyZDAY1KFQUAOw==
}

set maskdata "#define solid_width 9\n#define solid_height 9"
append maskdata {
  static unsigned char solid_bits[] = {
    0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01,
    0xff, 0x01, 0xff, 0x01, 0xff, 0x01
  };
}

set data "#define open_width 9\n#define open_height 9"
append data {
  static unsigned char open_bits[] = {
    0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0xff, 0x01
  };
}

image create bitmap ::rep::_shown -data $data -maskdata $maskdata \
    -foreground black -background white

set data "#define closed_width 9\n#define closed_height 9"
append data {
  static unsigned char closed_bits[] = {
    0xff, 0x01, 0x01, 0x01, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01,
    0x11, 0x01, 0x01, 0x01, 0xff, 0x01
  };
}

image create bitmap ::rep::_hidden -data $data -maskdata $maskdata \
    -foreground black -background white

###
### End of file: reper.tcl
###
