#! /bin/sh
# \
exec wish "$0" "$@"
# wrapping it up in an own namespace for sanity's sake.
namespace eval block_select {
package require Tk
package provide block_select 1.0
namespace export block_select
array set selCoords {}
array set block_selecting {}
# selection handler for the text widget. win is the text-widget,
# offset and len are provided by the selection caller. (see man selection)
#
# builds a list of lines in the text widget carrying the sel tag
# and constructs a buffer, ending lines with newlines if necessary.
# return that buffer.
proc sel_text {win off len} {
set start "0.0"
set lines [list]
set buf {}
while {[string length [set start [$win tag nextrange sel $start]]]} {
lappend lines [$win get [lindex $start 0] [lindex $start 1]]
set start [lindex $start 1]
}
foreach l $lines {
append buf $l
if {[string index $l end] != "\n"} then { append buf "\n" }
}
return [string range $buf $off [expr $off + $len]]
}
# control proc to mark the current selection
#
proc mark_text {win x2 y2} {
variable selCoords
selection clear
foreach {x1 y1} $selCoords($win) {}
if {$x1 > $x2} then {set x $x2; set endx $x1} else {set x $x1; set endx $x2}
if {$y1 > $y2} then {set y $y2; set endy $y1} else {set y $y1; set endy $y2}
while {$y <= $endy} {
set bb [$win bbox "@$x,$y"]
$win tag add sel "@$x,$y" "@$endx,$y"
incr y [lindex $bb 3]
}
}
proc block_select {win} {
variable selCoords
variable block_selecting
selection handle -format STRING -type STRING -selection PRIMARY $win [namespace code "sel_text $win "]
# start of selection
bind $win <Control-ButtonPress-1> [namespace code {
set selCoords(%W) [list %x %y]
set block_selecting(%W) 1
break; # stop default binding
}]
# during move
bind $win <Control-Motion> [namespace code {
if {[info exists block_selecting(%W)] && $block_selecting(%W)} then {
if {![info exists selCoords(%W)]} then {
set block_selecting(%W) 0
break
}
mark_text %W %x %y
break; # stop default binding
}
}]
# end selecting
bind $win <Control-ButtonRelease-1> [namespace code {
if {[info exists block_selecting(%W)] && $block_selecting(%W)} then {
mark_text %W %x %y
catch {unset selCoords(%W)}
}
set block-selecting(%W) 0
break; # stop default binding
}]
bind $win <KeyRelease-Control_L> [namespace code {
if {[info exists block_selecting(%W)] && $block_selecting(%W)} then {
set block-selecting(%W) 0
catch {unset selCoords(%W)}
}
}]
bind $win <KeyRelease-Control_R> [namespace code {
if {[info exists block_selecting(%W)] && $block_selecting(%W)} then {
set block-selecting(%W) 0
catch {unset selCoords(%W)}
}
}]
}
} ;# end of namespace
if {[info exists ::argv0] && $::argv0 == [info script]} then {
# Testing
proc build_text {parent} {
text ${parent}.t -exportselection true
pack ${parent}.t -expand yes -fill both
${parent}.t insert 0.0 {
block selecting fun: mult-table a*b
a/b 7 8 9 10 11 12 tabs mtabs
1 7 8 9 10 11 12 one one
2 14 16 18 20 22 24 two two
3 21 24 27 30 33 36 three three
4 28 32 36 40 44 48 four four
5 35 40 45 50 55 60 five five
6 42 48 54 60 66 72 six six
}
return ${parent}.t
}
# for testing:
wm withdraw .
toplevel .test
package require block_select
block_select::block_select [build_text .test]
}MSW / 20. Feb 2003: block-select now works fine with tabs, be it user-defined global tabs, tag tabs, default tabs .... should've used the coordinates right from the beginning.
Remark: This (atm?) only works with exportselection true (the default). Thought I'd mention it :)

