#
# Module for editing Canvas 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, and the item the command occurred in.

# Undoes last command.
proc th_Canvas_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_Canvas_replace $w [lindex $data 2] [lindex $data 0]
}

# Functions that change canvases should call this. Here i is the text item
# changed and name is what gets shown if the user wants to know the last
# undoable command.
proc th_Canvas_register_undoable_cmd {w i {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 itemcget $i -text] $name $i]
  set TH(Undo,Data,$w) [concat [list $new_entry] $TH(Undo,Data,$w)]
  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 entry at the cursor.
proc th_Canvas_insert {w chars} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  $w insert $i insert $chars
  global TH ; set TH(Modified,$w) 1
  th_Canvas_goto $w insert 0
}

# Replaces contents of entry with new_string.
proc th_Canvas_replace {w i new_string} {
  set new_index [expr [$w index $i insert] + [string length $new_string] - \
        [string length [$w itemcget $i -text]]]
  $w dchars $i 0 end
  $w insert $i 0 $new_string
  global TH ; set TH(Modified,$w) 1
  th_Canvas_goto $w $new_index 0
}

# An undoable version of th_Canvas_insert
proc th_Canvas_undoable_insert {w string} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  th_Canvas_register_undoable_cmd $w $i "Insert $string"
  th_Canvas_insert $w $string
}

# Insert, or overwrite a single character. Not undoable.
proc th_Canvas_self_insert {w {c ""}} {
  if {(![regexp . $c])} {return}
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}

  th_Canvas_insert $w $c
  global TH
  if {[catch "set TH(Overwrite,$w)"]} {set TH(Overwrite,$w) 0}
  if $TH(Overwrite,$w) {$w dchars $i insert}
}


# Deletion

# Deletes range of text. Undoable if undo is 1, non-undoable (doable?) otherwise
proc th_Canvas_delete_range {w i start end {undo 1}} {
  if {[regexp $start {[0-9]*}] && [regexp $end {[0-9]*}]} {
    if {$start >= [$w index $i end]} {bell ; return}
    if {$end < 0} {bell ; return}
    if {$start > $end} {bell ; return}
  }
  if {[catch {$w index $i $start} s]} {bell ; return}
  if {[catch {$w index $i $end} e]} {bell ; return}

  if $undo {
    th_Canvas_register_undoable_cmd $w $i "Delete [string range [$w itemcget $i -text] $s $e]"
  } else {  global TH ; set TH(Modified,$w) 1}
  $w dchars $i $s $e
  th_Canvas_goto $w insert 0
}

# Removes selected text from currently focused item in w.
proc th_Canvas_delete_selection {w} {
  if {[catch "$w select item" i]} {bell ; return}
  th_Canvas_delete_range $w $i sel.first sel.last
}


# Killing text

# Kills line from current focussed item in w.
proc th_Canvas_kill_line {w} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  set s [$w itemcget $i -text]
  set index [$w index $i insert]
  set end [th_string_lineend $s $index]
  set killed [string range $s $index $end]
  global TH ;  set TH(Modified,$w) 1

# Add a new binding tag to tell us the next keystrokes.
  set restart_kill 0
  if {[lsearch [set bindtags [bindtags $w]] "kill_keys"] < 0} {
    set restart_kill 1
    bindtags $w "kill_keys $bindtags"
    set TH(Kill,Active) 1
    bind kill_keys <Key> {if {[string length "%A"] ==1} {set TH(Kill,Active) 0}}
    foreach binding {<Control-Key> <Meta-Key> <Meta-Control-Key> <Button> <Enter>} {
      bind kill_keys $binding {set TH(Kill,Active) 0}}
    foreach key $TH(Binding,Kill_Line) {
      bind kill_keys $key {# do nothing, don't clear TH(Kill,Active)}
  }}
  if {$TH(Kill,Active) != 1} {set restart_kill 1}
  set TH(Kill,Active) 1

  if $restart_kill {clipboard clear -displayof $w}
  clipboard append -displayof $w -- $killed
  th_Canvas_delete_range $w $i insert $end
}

# Kills range of text from item i in w.
proc th_Canvas_kill_range {w i start end} {
  set s [$w index $i $start]
  set e [$w index $i $end]
  if {$s > $e} {bell ; return}
  global TH ; set TH(Modified,$w) 1
  clipboard clear -displayof $w
  clipboard append -displayof $w -- [string range [$w itemcget $i -text] $s $e]
  th_Canvas_delete_range $w $i $s $e
}

# Kills region in current focused item in w.
proc th_Canvas_kill_region {w} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  global TH
  if {![catch "$w index $i sel.last"] &&
      ([$w index $i sel.first] <= [$w index $i insert]) &&
	([$w index $i insert] <= [$w index $i sel.last])} {
    th_Canvas_kill_range $w $i sel.first sel.last
  } elseif {![catch "set TH(Mark,$w,$i)"]} {
    if {[$w index $i insert] > $TH(Mark,$w,$i)} {
      th_Canvas_kill_range $w $i $TH(Mark,$w,$i) [expr [$w index $i insert] -1]
    } else {th_Canvas_kill_range $w $i insert [expr $TH(Mark,$w,$i) -1]}
  } else {bell}
}


# String filtering

proc th_Canvas_filter {w filter} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  set s [$w itemcget $i -text]
  if {[catch "$w index $i sel.last"]} {
    set start [$w index $i insert]
    set end [expr [string wordend $s $start] - 1]
    set selected 0
  } else {set start [$w index $i sel.first]
    set end [$w index $i sel.last]
    set selected 1
  }
  set replacement [th_string_filter $s $start $end $filter]
  th_Canvas_register_undoable_cmd $w $i "Change [string range $s $start $end]"
  th_Canvas_replace $w $i $replacement
  if $selected {
    $w select from $i $start
    $w select to $i $end
  } else {
    th_Canvas_goto $w [expr [$w index $i $end] + 1] 0}
}


# Transposition

proc th_Canvas_transpose_chars {w} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  set s [$w itemcget $i -text]

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

  set c1 [string index $s [expr "$index-1"]]
  set c2 [string index $s $index]

  set replacement [th_string_transpose_chars $s $index]
  th_Canvas_register_undoable_cmd $w $i "Transpose $c1 $c2"
  th_Canvas_replace $w $i $replacement
  th_Canvas_goto $w [expr [$w index $i insert] + 1] 0
}

proc th_Canvas_transpose_words {w} {
  if {[catch "$w index [set i [$w focus]] insert"]} {bell ; return}
  set in [$w index $i insert]
  if {($in == 0) || ($in >= [$w index $i end])} {bell ; return}

  set s [$w itemcget $i -text]
  set end3 [expr [string wordend $s $in] - 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 $in]
  th_Canvas_register_undoable_cmd $w $i "Transpose $c1$c2$c3"
  th_Canvas_replace $w $i $replacement
  th_Canvas_goto $w [string wordend [$w itemcget $i -text] [$w index $i insert]] 0
}
