# jtextmouse.tcl - support for Text mouse bindings
#
######################################################################
# Copyright 1992-1995 by Jay Sekora.  This file may be freely        #
# distributed, modified or unmodified, for any purpose, provided     #
# that this copyright notice is retained verbatim in all copies and  #
# no attempt is made to obscure the authorship of this file.  If you #
# distribute any modified versions, I ask, but do not require, that  #
# you clearly mark any changes you make as such and that you provide #
# your users with instructions for getting the original sources.     #
######################################################################

# TO DO:
# generalise movement to copying-to-cutbuffer and deletion

######################################################################
# global variables:
#
global J_PREFS env
j:default J_PREFS(bindings) basic
j:default J_PREFS(typeover) 1
#
######################################################################

######################################################################
# front-ends to hide differences between Tk4 and Tk3
######################################################################

proc j:text_select_to { W x y } {
  j:tk3 {
    tk_textSelectTo $W @$x,$y
  }
  j:tk4 {
    tkTextSelectTo $W $x $y
  }
}

######################################################################
# 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:scanpaste_press 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:scanpaste_press { W x y t } {
  global j_teb
  j:tk3 {
    $W scan mark $y
  }
  j:tk4 {
    $W scan mark $x $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:scanpaste_drag 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:scanpaste_drag { 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} {
    j:tk3 {
      $W scan dragto $y
    }
    j:tk4 {
      $W scan dragto $x $y
    }
    set j_teb(scanpaste,pasting) 0
  }
}

# j:tmb:scanpaste_release 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:scanpaste_release { 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@aq.org (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.
#   Also merged in John's <ouster@tcl.eng.sun.com> scrolling mechanism
#   for Tk4
######################################################################

# j:tmb:movesel_press W x y - move and begin a selection
proc j:tmb:movesel_press { W x y t } {
  global j_teb J_PREFS tk_priv tkPriv
  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:
  j:tk3 {
    set tk_priv(selectMode) char
  }
  j:tk4 {
    set tkPriv(selectMode) char
    set tkPriv(mouseMoved) 0
    set tkPriv(pressX) $x
  }
  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:movesel_drag W x y - begin dragging out selection
proc j:tmb:movesel_drag { W x y t } {
  global j_teb tkPriv
  
  j:tk4 {
    set tkPriv(x) $x
    set tkPriv(y) $y
  }
  
  set xdiff [expr {abs($x - $j_teb(dragscroll,x))}]
  set ydiff [expr {abs($y - $j_teb(dragscroll,y))}]
  
  if {$xdiff < 3 && $ydiff < 3} {
    return
  }
  
  j:text_select_to $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"} {
      $W tag add sel sel.first sel.last+1l
      $W yview -pickplace sel.last+1l
    } else {
      if {$j_teb(dragscroll,direction) == "up"} {
        $W tag add sel sel.first sel.last-1l
        $W yview -pickplace sel.first-1l
      } else { return }
    }
    after $j_teb(dragscroll,delay) j:tmb:extend_sel $W $x $y $t
  }
}

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

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

proc j:tb:mouse_bind { W } {
  global j_teb
  
  # mouse bindings (for motion and scrolling selections)
  bind $W <Button-1>		{j:tmb:movesel_press %W %x %y %t}
  bind $W <Control-Button-1>	{j:tmb:move %W %x %y %t}
  bind $W <B1-Motion>		{j:tmb:movesel_drag %W %x %y %t}
  bind $W <ButtonRelease-1>	{j:tmb:movesel_release %W %x %y %t}
  # much slower with following:
  j:tk4 {
    bind $W <B1-Leave> {
      set tkPriv(x) %x
      set tkPriv(y) %y
      tkTextAutoScan %W
      break
    }
    bind $W <B1-Enter> {
      tkCancelRepeat
      break
    }
  }
  
  # mouse bindings (for scanning and pasting)
  bind $W <Button-2>		{j:tmb:scanpaste_press %W %x %y %t}
  bind $W <B2-Motion>		{j:tmb:scanpaste_drag %W %x %y %t}
  bind $W <ButtonRelease-2>	{j:tmb:scanpaste_release %W %x %y %t}
}
