#
# Module for editing Entry widgets
#


# Undos are handled thus: Each cmd that can be undone stores an entry into
# TH(Undo,Data,$w). This entry contains the entry's string before the command
# was done, and a human-readable title to prompt the user with what the cmd
# did.

# Undoes last command.
proc th_Entry_undo {w} {
  global TH
  if {[catch "set TH(Undo,Data,$w)"]} {set TH(Undo,Data,$w) ""}
  if {([llength $TH(Undo,Data,$w)] == 0)} {bell ; return}

  set data [lindex $TH(Undo,Data,$w) 0]
  set TH(Undo,Data,$w) [lrange $TH(Undo,Data,$w) 1 end]
  set TH(Modified,$w) 1

  th_Entry_replace $w [lindex $data 0]
}

# Should be called by every undoable function. Name is shown to the user if the
# user wants to know the last undoable command.
proc th_Entry_register_undoable_cmd {w {name ""}} {
  global TH ; set TH(Modified,$w) 1
  if {[catch "set TH(Undo,Data,$w)"]} {set TH(Undo,Data,$w) ""}
  if {[catch "set TH(Undo,Max,[winfo class $w])"]} {set TH(Undo,Max,[winfo class $w]) 0}

  set new_entry [list [$w get] $name]
  set tmp [concat [list $new_entry] $TH(Undo,Data,$w)] ; set TH(Undo,Data,$w) $tmp
  if {($TH(Undo,Max,[winfo class $w]) >= 0) && ($TH(Undo,Max,[winfo class $w]) < [llength $TH(Undo,Data,$w)])} {
    set TH(Undo,Data,$w) [lrange $TH(Undo,Data,$w) 0 [expr "$TH(Undo,Max,[winfo class $w]) - 1"]]
}}


# Basic insertion commands

# Inserts some characters into the string at i
proc th_string_insert {s i chars} {
  return "[string range $s 0 [expr $i - 1]]$chars[string range $s $i end]"
}

# Inserts some characters into the entry at the cursor.
proc th_Entry_insert {w chars} {
  global TH ; set TH(Modified,$w) 1
  $w insert insert $chars
  th_Entry_goto $w insert 0
}

# Replaces contents of entry with new_string.
proc th_Entry_replace {w new_string} {
  global TH ; set TH(Modified,$w) 1
  set new_index [expr [$w index insert] + [string length $new_string] - \
        [string length [$w get]]]
  $w delete 0 end
  $w insert 0 $new_string
  th_Entry_goto $w $new_index 0
}

# like th_Entry_insert, but undoable
proc th_Entry_undoable_insert {w string} {
  th_Entry_register_undoable_cmd $w "Insert $string"
  th_Entry_insert $w $string
}

# Insert or overwrite single char. Not undoable.
proc th_Entry_self_insert {w {c ""}} {
  if {(![regexp . $c])} {return}
  th_Entry_insert $w $c
  global TH
  if {[catch "set TH(Overwrite,$w)"]} {set TH(Overwrite,$w) 0}
  if $TH(Overwrite,$w) {$w delete insert}
}


# Deletion

proc th_string_delete_range {s start end} {
  incr start -1
  incr end
  return "[string index $s 0 $start][string index $s $end end]"
}

proc th_Entry_delete_range {w start end {undo 1}} {
  set s [$w index $start]
  set e [$w index $end]
  if {$s >= $e} {bell ; return}

  if $undo {
    th_Entry_register_undoable_cmd $w "Delete [string range [$w get] $s [expr $e - 1]]"
  } else {global TH ; set TH(Modified,$w) 1}
  $w delete $s $e
  th_Entry_goto $w insert 0
}

proc th_Entry_delete_selection {w} {
  if {[catch "$w index sel.last"]} {bell ; return}
  th_Entry_delete_range $w sel.first sel.last
}


# Killing text

proc th_Entry_kill_range {w start end} {
  set s [$w index $start]
  set e [$w index $end]
  if {$s >= $e} {bell ; return}

  global TH ; set TH(Modified,$w) 1
  clipboard clear -displayof $w
  clipboard append -displayof $w -- [string range [$w get] $s $e]
  th_Entry_delete_range $w $s $e
}

proc th_Entry_kill_region {w} {
  if {![catch "$w index sel.last"] &&
      ([$w index sel.first] <= [$w index insert]) &&
	([$w index insert] <= [$w index sel.last])} {
    th_Entry_kill_range $w sel.first sel.last
  } elseif {![catch {$w index anchor} m]} {
    if {$m <= [$w index insert]} {
      th_Entry_kill_range $w $m insert
    } else {th_Entry_kill_range $w insert $m}
  } else {bell}
}


# String filtering

proc th_Entry_filter {w filter} {
  if {[catch "$w index sel.last"]} {
    set start [$w index insert]
    set end [string wordend [$w get] $start]
    set selected 0
  } else {set start [$w index sel.first]
    set end [$w index sel.last]
    set selected 1
  }
  incr end -1
  set replacement [th_string_filter [$w get] $start $end $filter]
  th_Entry_register_undoable_cmd $w "Change [string range [$w get] $start $end]"
  th_Entry_replace $w $replacement
  incr end
  if {($selected)} {
    $w select from $start
    $w select to $end
  } else {
    th_Entry_goto $w [$w index $end] 0}
}


# Transposition

proc th_Entry_transpose_chars {w} {
  set i [$w index insert]
  if {($i == 0) || ($i >= [$w index end])} {bell ; return}

  set c1 [string index [$w get] [expr "$i-1"]]
  set c2 [string index [$w get] [expr "$i"]]

  set replacement [th_string_transpose_chars [$w get] $i]
  th_Entry_register_undoable_cmd $w "Transpose $c1 $c2"
  th_Entry_replace $w $replacement
  th_Entry_goto $w [expr [$w index insert] + 1] 0
}

proc th_Entry_transpose_words {w} {
  set i [$w index insert]
  if {($i == 0) || ($i >= [$w index end])} {bell ; return}

  set s [$w get]
  set end3 [expr [string wordend $s $i] - 1]
  set begin3 [string wordstart $s $end3]
  set c3 [string range $s $begin3 $end3]
  set begin2 [string wordstart $s [expr $begin3 - 1]]
  set c2 [string range $s $begin2 [expr $begin3 - 1]]
  set begin1 [string wordstart $s [expr $begin2 - 1]]
  set c1 [string range $s $begin1 [expr $begin2 - 1]]

  set replacement [th_string_transpose_words $s $i]
  th_Entry_register_undoable_cmd $w "Transpose $c1$c2$c3"
  th_Entry_replace $w $replacement
  th_Entry_goto $w [string wordend [$w get] [$w index insert]] 0
}
