# jtextmouse.tcl - support for Text mouse bindings
# 
# Copyright 1992-1994 by Jay Sekora.  All rights reserved, except 
# that this file may be freely redistributed in whole or in part 
# for non-profit, noncommercial use.

# TO DO:
# ^L
# sentence-manipulation stuff
# case change commands, transposition commands
# commands to do with mark?
# word deletion - fix to use buffer
# generalise movement to copying-to-cutbuffer and deletion
# IMPROVE ENTRY BINDINGS
# literal-insert for entry


######################################################################
# global variables:
#
global J_PREFS env
if {! [info exists J_PREFS(bindings)]} {set J_PREFS(bindings) basic}
if {! [info exists J_PREFS(typeover)]} {set J_PREFS(typeover) 1}
#
######################################################################

######################################################################
# routines to let scanning and pasting to double-up on the same button
# based on code by Tom Phelps <phelps@cs.berkeley.edu>
# modified by jay to make it a little easier to paste
######################################################################

# j:tmb:start_scan_or_paste W x y t - start a drag or paste, recording
#   current location and time so we can later decide whether to paste or drag
# BIND TO <ButtonPress-N>
proc j:tmb:start_scan_or_paste { W x y t } {
  global j_teb
  $W scan mark $y
  set j_teb(scanpaste,time) $t
  set j_teb(scanpaste,x) $x
  set j_teb(scanpaste,y) $y
  set j_teb(scanpaste,pasting) 1
}

# j:tmb:continue_scan W x y t - scan the entry, and mark the fact that
#   we're scanning, not pasting.  won't scan if the mouse has moved only
#   one pixel.
# BIND TO <BN-Motion>
proc j:tmb:continue_scan { W x y t } {
  global j_teb
  
  set xdiff [expr {abs($x - $j_teb(scanpaste,x))}]
  set ydiff [expr {abs($y - $j_teb(scanpaste,y))}]
  
  if {$xdiff >= 2 || $ydiff >= 2} {
    $W scan dragto $y
    set j_teb(scanpaste,pasting) 0
  }
}

# j:tmb:end_scan_or_paste W x y t - if we haven't been scanning, and it's
#   been less than 500ms since button-down, paste selection
# BIND TO <ButtonRelease-N>
proc j:tmb:end_scan_or_paste { W x y t } {
  global j_teb
  if {$j_teb(scanpaste,pasting) &&
      [expr {$t-$j_teb(scanpaste,time)}] < 500} {
    # j:tb:paste_selection $W
    catch {
      focus $W
      j:text:insert_string $W [selection get]
    }
  }
}

######################################################################
# routines to allow scrolling during text selection
# from raines@cgibm1.slac.stanford.edu (Paul E. Raines)
# modifications by js@bu.edu (Jay Sekora) to make it a little
#   harder to make a selection, so you don't do it accidentally
#   when you just want to move.
######################################################################

# j:tmb:move_sel W x y - move and begin a selection
proc j:tmb:move_sel { W x y t } {
  global j_teb J_PREFS tk_priv
  set j_teb(dragscroll,txnd) 0
  set j_teb(dragscroll,x) $x
  set j_teb(dragscroll,y) $y
  
  set my_name [lindex [info level 0] 0]
  set j_teb(last_command,$W) $my_name	;# solves Emacs ^K, click, ^K problem
  
  if $J_PREFS(typeover) {
    # clear current selection:
    $W tag remove sel 1.0 end
  }
  # do what normal Tk binding does:
  set tk_priv(selectMode) char
  j:text:move $W @$x,$y
  $W mark set anchor insert
  if {[lindex [$W config -state] 4] == "normal"} {focus $W}
}

# j:tmb:move W x y - move, don't change selection
proc j:tmb:move { W x y t } {
  global j_teb
  set my_name [lindex [info level 0] 0]
  set j_teb(last_command,$W) $my_name	;# solves Emacs ^K, click, ^K problem
  
  j:text:move $W @$x,$y
  $W mark set anchor insert
  if {[lindex [$W config -state] 4] == "normal"} {focus $W}
}

# j:tmb:drag_sel W x y - begin dragging out selection
proc j:tmb:drag_sel { W x y t } {
  global j_teb
  
  set xdiff [expr {abs($x - $j_teb(dragscroll,x))}]
  set ydiff [expr {abs($y - $j_teb(dragscroll,y))}]
  
  if {$xdiff < 3 && $ydiff < 3} {
    return
  }
  
  if {$y > [winfo height $W]} {
    if {!$j_teb(dragscroll,txnd)} {
      after $j_teb(dragscroll,delay) j:tmb:extend_sel $W $x $y $t
    }
    set j_teb(dragscroll,txnd) 1
    set j_teb(dragscroll,direction) down
  } else {
    if {$y < 0} {
      if {!$j_teb(dragscroll,txnd)} {
        after $j_teb(dragscroll,delay) j:tmb:extend_sel $W $x $y $t
      }
      set j_teb(dragscroll,txnd) 1
      set j_teb(dragscroll,direction) up
    } else {
      set j_teb(dragscroll,txnd) 0
      set j_teb(dragscroll,direction) 0
    }
  }

   if {!$j_teb(dragscroll,txnd)} {
  	tk_textSelectTo $W @$x,$y
  }
}

# j:tmb:extend_sel W x y t - drag out a selection, scrolling if necessary
proc j:tmb:extend_sel { W x y t } {
  global j_teb

  if {$j_teb(dragscroll,txnd)} {
    if {$j_teb(dragscroll,direction) == "down"} {
      tk_textSelectTo $W sel.last+1l
      $W yview -pickplace sel.last+1l
    } else {
      if {$j_teb(dragscroll,direction) == "up"} {
        tk_textSelectTo $W sel.first-1l
        $W yview -pickplace sel.first-1l
      } else { return }
    }
    after $j_teb(dragscroll,delay) j:tmb:extend_sel $W $x $y $t
  }
}

# j:tmb:end_sel W - finish a selection
proc j:tmb:end_sel { W args } {
  global j_teb
  set j_teb(dragscroll,txnd) 0
}

######################################################################
# set up text mouse bindings
#   these supplement, rather than replace, the standard Tk bindings.
######################################################################

proc j:tb:mouse_bind { W } {
  global j_teb
  
  j:tk3 {				;# bindings are superfluous in Tk 4
    # mouse bindings (for motion and scrolling selections)
    bind $W <Button-1>		{j:tmb:move_sel %W %x %y %t}
    bind $W <Control-Button-1>	{j:tmb:move %W %x %y %t}
    bind $W <B1-Motion>		{j:tmb:drag_sel %W %x %y %t}
    bind $W <ButtonRelease-1>	{j:tmb:end_sel %W %x %y %t}
    
    # mouse bindings (for scanning and pasting)
    bind $W <Button-2>		{j:tmb:start_scan_or_paste %W %x %y %t}
    bind $W <B2-Motion>		{j:tmb:continue_scan %W %x %y %t}
    bind $W <ButtonRelease-2>	{j:tmb:end_scan_or_paste %W %x %y %t}
  
    # mouse bindings (for scanning and pasting)
    bind $W <Button-3>		{j:tmb:start_scan_or_paste %W %x %y %t}
    bind $W <B3-Motion>		{j:tmb:continue_scan %W %x %y %t}
    bind $W <ButtonRelease-3>	{j:tmb:end_scan_or_paste %W %x %y %t}
  }
}


