#
# Module for browsing around in Text widgets
#


# Puts cursor at $where, where is an acceptable text index, or an integer,
# which means the beginning of that line.
proc th_Text_goto {w where {dont_stay 1}} {
  if {[regexp {[0-9]*} $where howmuch] && 
      ([string length $howmuch] == [string length $where]) &&
      ($howmuch == $where)} {
    set where "$where.0 linestart"
  }
  if {[catch {$w index $where} i]} {bell ; return 0}
  if {[$w compare $i == end]} {set i "end-1c"}
  $w see $i
  if {$dont_stay && [$w compare $i == insert]} {bell ; return 0}
  $w mark set insert $i
  return 1
}


# A rather crude centering routine. Center on the vertical scrollbar, then call
# the 'see' routine to get horizontal visibility. (Any better ideas, anyone?)
proc th_Text_center_cursor {w} {
  scan [$w index insert] "%d.%d" line column
  scan [$w index "@0,0"] "%d.%d" start_line start_column
  scan [$w index "@[winfo width $w],[winfo height $w]"] "%d.%d" end_line end_column
  $w yview [expr $line - (($end_line - $start_line) / 2)]
  $w see insert
}

# Moves up or down, depending on $down. Tries to keep cursor in same x-location
# despite short lines.
proc th_Text_adjacent_line {w down} {
  global TH
  if {[catch "set TH(Column,$w)"]} {set TH(Column,$w) 1}

  if {[set this_x [lindex [$w bbox insert] 0]] == ""} {set this_x 1}
  if {[set next_x [lindex [$w bbox insert+1c] 0]] == ""} {set this_x 1}
  set info [$w dlineinfo insert]

  if {[$w dlineinfo "insert $down lines"] == ""} {
    $w yview scroll $down units
    set new_y [lindex $info 1]
  } else {
    if {$down == "-1"} {set new_y [expr [lindex $info 1] - 1]
    } else {set new_y [expr [lindex $info 1] + [lindex $info 3]]}
  }
  
  if {($this_x > $TH(Column,$w))} {  
# Column counter is out of date, too far to the left.
    set TH(Column,$w) $this_x
  }

  if {($next_x <= $this_x)} {
# Insert is at end of physical line. Use rightmost: column counter or insert.
    if {($this_x < $TH(Column,$w))} {
      set this_x $TH(Column,$w)
  }} elseif {($TH(Column,$w) >= $next_x)} {
# Our column counter is out of date, too far to the right.
    set TH(Column,$w) $this_x
  } else {set this_x $TH(Column,$w)}

  return "@$this_x,$new_y"
}

# Extends selection one line.
proc th_Text_select_next_line {w} {
  if {[catch "$w index sel.last"]} { set start insert
  } elseif {([$w compare insert < sel.first]) ||
      ([$w compare insert > sel.last])} {
    $w tag remove sel sel.first sel.last
    set start insert
  } else {set start sel.first}
  if {($start == "insert")} {  set near $start
  } else { set near sel.last}
  if {([$w get $near] == "\n")} {
    $w tag add sel $start "$near +1 chars"
    } else {$w tag add sel $start "$near lineend"}
  th_Text_goto $w sel.last
}

# Selects a range of text in w
proc th_Text_select_range {w start end} { 
  set s [$w index $start]
  set e [$w index $end]
  set i [$w index insert]
  if {[$w compare $s > $e]} {bell ; return}
  $w tag remove sel 1.0 end
  $w tag add sel $s $e
  if {[$w compare $i == $s]} {$w mark set anchor $e
  } elseif {[$w compare $i == $e]} {$w mark set anchor $s}
}

# Extends selection to $cur.
proc th_Text_select_to {w cur} {
  if [catch {$w index anchor}] {bell ; return}
  set anchor [$w index anchor]
  if [$w compare $cur < anchor] {
    set first $cur ; set last anchor
  } else {set first anchor ; set last $cur}
  $w tag remove sel 1.0 $first
  $w tag add sel $first $last
  $w tag remove sel $last end
}

# Switches cursor and anchor.
proc th_Text_exchange_mark {w} {
  if {[catch {set m [$w index anchor]}]} {bell ; return}
  $w mark set anchor insert
  th_Text_goto $w $m
}
