summaryrefslogtreecommitdiffstats
path: root/library/text.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'library/text.tcl')
-rw-r--r--library/text.tcl317
1 files changed, 185 insertions, 132 deletions
diff --git a/library/text.tcl b/library/text.tcl
index 60eab41..37aa387 100644
--- a/library/text.tcl
+++ b/library/text.tcl
@@ -3,9 +3,9 @@
# This file defines the default bindings for Tk text widgets and provides
# procedures that help in implementing the bindings.
#
-# Copyright (c) 1992-1994 The Regents of the University of California.
-# Copyright (c) 1994-1997 Sun Microsystems, Inc.
-# Copyright (c) 1998 by Scriptics Corporation.
+# Copyright © 1992-1994 The Regents of the University of California.
+# Copyright © 1994-1997 Sun Microsystems, Inc.
+# Copyright © 1998 Scriptics Corporation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -42,7 +42,7 @@
# Standard Motif bindings:
-bind Text <1> {
+bind Text <Button-1> {
tk::TextButton1 %W %x %y
%W tag remove sel 0.0 end
}
@@ -51,26 +51,26 @@ bind Text <B1-Motion> {
set tk::Priv(y) %y
tk::TextSelectTo %W %x %y
}
-bind Text <Double-1> {
+bind Text <Double-Button-1> {
set tk::Priv(selectMode) word
tk::TextSelectTo %W %x %y
catch {%W mark set insert sel.first}
}
-bind Text <Triple-1> {
+bind Text <Triple-Button-1> {
set tk::Priv(selectMode) line
tk::TextSelectTo %W %x %y
catch {%W mark set insert sel.first}
}
-bind Text <Shift-1> {
+bind Text <Shift-Button-1> {
tk::TextResetAnchor %W @%x,%y
set tk::Priv(selectMode) char
tk::TextSelectTo %W %x %y
}
-bind Text <Double-Shift-1> {
+bind Text <Double-Shift-Button-1> {
set tk::Priv(selectMode) word
tk::TextSelectTo %W %x %y 1
}
-bind Text <Triple-Shift-1> {
+bind Text <Triple-Shift-Button-1> {
set tk::Priv(selectMode) line
tk::TextSelectTo %W %x %y
}
@@ -86,7 +86,7 @@ bind Text <ButtonRelease-1> {
tk::CancelRepeat
}
-bind Text <Control-1> {
+bind Text <Control-Button-1> {
%W mark set insert @%x,%y
# An operation that moves the insert mark without making it
# one end of the selection must insert an autoseparator
@@ -95,14 +95,14 @@ bind Text <Control-1> {
}
}
# stop an accidental double click triggering <Double-Button-1>
-bind Text <Double-Control-1> { # nothing }
+bind Text <Double-Control-Button-1> { # nothing }
# stop an accidental movement triggering <B1-Motion>
bind Text <Control-B1-Motion> { # nothing }
bind Text <<PrevChar>> {
- tk::TextSetCursor %W insert-1displayindices
+ tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfCluster]
}
bind Text <<NextChar>> {
- tk::TextSetCursor %W insert+1displayindices
+ tk::TextSetCursor %W [tk::TextNextPos %W insert tk::endOfCluster]
}
bind Text <<PrevLine>> {
tk::TextSetCursor %W [tk::TextUpDownLine %W -1]
@@ -111,10 +111,10 @@ bind Text <<NextLine>> {
tk::TextSetCursor %W [tk::TextUpDownLine %W 1]
}
bind Text <<SelectPrevChar>> {
- tk::TextKeySelect %W [%W index {insert - 1displayindices}]
+ tk::TextKeySelect %W [tk::TextPrevPos %W insert tk::startOfCluster]
}
bind Text <<SelectNextChar>> {
- tk::TextKeySelect %W [%W index {insert + 1displayindices}]
+ tk::TextKeySelect %W [tk::TextNextPos %W insert tk::endOfCluster]
}
bind Text <<SelectPrevLine>> {
tk::TextKeySelect %W [tk::TextUpDownLine %W -1]
@@ -123,7 +123,7 @@ bind Text <<SelectNextLine>> {
tk::TextKeySelect %W [tk::TextUpDownLine %W 1]
}
bind Text <<PrevWord>> {
- tk::TextSetCursor %W [tk::TextPrevPos %W insert tcl_startOfPreviousWord]
+ tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
}
bind Text <<NextWord>> {
tk::TextSetCursor %W [tk::TextNextWord %W insert]
@@ -135,10 +135,10 @@ bind Text <<NextPara>> {
tk::TextSetCursor %W [tk::TextNextPara %W insert]
}
bind Text <<SelectPrevWord>> {
- tk::TextKeySelect %W [tk::TextPrevPos %W insert tcl_startOfPreviousWord]
+ tk::TextKeySelect %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
}
bind Text <<SelectNextWord>> {
- tk::TextKeySelect %W [tk::TextNextWord %W insert]
+ tk::TextKeySelect %W [tk::TextSelectNextWord %W insert]
}
bind Text <<SelectPrevPara>> {
tk::TextKeySelect %W [tk::TextPrevPara %W insert]
@@ -222,7 +222,8 @@ bind Text <Delete> {
%W delete sel.first sel.last
} else {
if {[%W compare end != insert+1c]} {
- %W delete insert
+ %W delete [tk::TextPrevPos %W insert+1c tk::startOfCluster] \
+ [tk::TextNextPos %W insert tk::endOfCluster]
}
%W see insert
}
@@ -232,7 +233,8 @@ bind Text <BackSpace> {
%W delete sel.first sel.last
} else {
if {[%W compare insert != 1.0]} {
- %W delete insert-1c
+ %W delete [tk::TextPrevPos %W insert tk::startOfCluster] \
+ [tk::TextNextPos %W insert-1c tk::endOfCluster]
}
%W see insert
}
@@ -296,7 +298,7 @@ bind Text <Key> {
tk::TextInsert %W %A
}
-# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Ignore all Alt, Meta, Control, Command, and Fn keypresses unless explicitly bound.
# Otherwise, if a widget binding for one of these is defined, the
# <Key> class binding will also fire and insert the character,
# which is wrong. Ditto for <Escape>.
@@ -306,10 +308,8 @@ bind Text <Meta-Key> {# nothing}
bind Text <Control-Key> {# nothing}
bind Text <Escape> {# nothing}
bind Text <KP_Enter> {# nothing}
-if {[tk windowingsystem] eq "aqua"} {
- bind Text <Command-Key> {# nothing}
- bind Text <Mod4-Key> {# nothing}
-}
+bind Text <Command-Key> {# nothing}
+bind Text <Fn-Key> {# nothing}
# Additional emacs-like bindings:
@@ -358,7 +358,7 @@ bind Text <<Redo>> {
bind Text <Meta-b> {
if {!$tk_strictMotif} {
- tk::TextSetCursor %W [tk::TextPrevPos %W insert tcl_startOfPreviousWord]
+ tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
}
}
bind Text <Meta-d> {
@@ -383,12 +383,12 @@ bind Text <Meta-greater> {
}
bind Text <Meta-BackSpace> {
if {!$tk_strictMotif} {
- %W delete [tk::TextPrevPos %W insert tcl_startOfPreviousWord] insert
+ %W delete [tk::TextPrevPos %W insert tk::startOfPreviousWord] insert
}
}
bind Text <Meta-Delete> {
if {!$tk_strictMotif} {
- %W delete [tk::TextPrevPos %W insert tcl_startOfPreviousWord] insert
+ %W delete [tk::TextPrevPos %W insert tk::startOfPreviousWord] insert
}
}
@@ -398,12 +398,7 @@ bind Text <<TkStartIMEMarkedText>> {
dict set ::tk::Priv(IMETextMark) "%W" [%W index insert]
}
bind Text <<TkEndIMEMarkedText>> {
- if { [catch {dict get $::tk::Priv(IMETextMark) "%W"} mark] } {
- bell
- } else {
- %W tag add IMEmarkedtext $mark insert
- %W tag configure IMEmarkedtext -underline on
- }
+ ::tk::TextEndIMEMarkedText %W
}
bind Text <<TkClearIMEMarkedText>> {
%W delete IMEmarkedtext.first IMEmarkedtext.last
@@ -412,6 +407,25 @@ bind Text <<TkAccentBackspace>> {
%W delete insert-1c
}
+# ::tk::TextEndIMEMarkedText --
+#
+# Handles input method text marking in a text widget.
+#
+# Arguments:
+# w - The text widget
+
+proc ::tk::TextEndIMEMarkedText {w} {
+ variable Priv
+ if {[catch {
+ set mark [dict get $Priv(IMETextMark) $w]
+ }]} {
+ bell
+ return
+ }
+ $w tag add IMEmarkedtext $mark insert
+ $w tag configure IMEmarkedtext -underline 1
+}
+
# Macintosh only bindings:
if {[tk windowingsystem] eq "aqua"} {
@@ -430,96 +444,37 @@ bind Text <Control-h> {
%W see insert
}
}
-if {[tk windowingsystem] ne "aqua"} {
- bind Text <2> {
- if {!$tk_strictMotif} {
- tk::TextScanMark %W %x %y
- }
- }
- bind Text <B2-Motion> {
- if {!$tk_strictMotif} {
- tk::TextScanDrag %W %x %y
- }
- }
-} else {
- bind Text <3> {
- if {!$tk_strictMotif} {
- tk::TextScanMark %W %x %y
- }
+bind Text <Button-2> {
+ if {!$tk_strictMotif} {
+ tk::TextScanMark %W %x %y
}
- bind Text <B3-Motion> {
- if {!$tk_strictMotif} {
- tk::TextScanDrag %W %x %y
- }
+}
+bind Text <B2-Motion> {
+ if {!$tk_strictMotif} {
+ tk::TextScanDrag %W %x %y
}
}
set ::tk::Priv(prevPos) {}
-# The MouseWheel will typically only fire on Windows and MacOS X.
-# However, someone could use the "event generate" command to produce one
-# on other platforms. We must be careful not to round -ve values of %D
-# down to zero.
-
-if {[tk windowingsystem] eq "aqua"} {
- bind Text <MouseWheel> {
- %W yview scroll [expr {-15 * (%D)}] pixels
- }
- bind Text <Option-MouseWheel> {
- %W yview scroll [expr {-150 * (%D)}] pixels
- }
- bind Text <Shift-MouseWheel> {
- %W xview scroll [expr {-15 * (%D)}] pixels
- }
- bind Text <Shift-Option-MouseWheel> {
- %W xview scroll [expr {-150 * (%D)}] pixels
- }
-} else {
- # We must make sure that positive and negative movements are rounded
- # equally to integers, avoiding the problem that
- # (int)1/3 = 0,
- # but
- # (int)-1/3 = -1
- # The following code ensure equal +/- behaviour.
- bind Text <MouseWheel> {
- if {%D >= 0} {
- %W yview scroll [expr {-%D/3}] pixels
- } else {
- %W yview scroll [expr {(2-%D)/3}] pixels
- }
- }
- bind Text <Shift-MouseWheel> {
- if {%D >= 0} {
- %W xview scroll [expr {-%D/3}] pixels
- } else {
- %W xview scroll [expr {(2-%D)/3}] pixels
- }
- }
+bind Text <MouseWheel> {
+ tk::MouseWheel %W y [tk::ScaleNum %D] -4.0 pixels
}
-
-if {[tk windowingsystem] eq "x11"} {
- # Support for mousewheels on Linux/Unix commonly comes through mapping
- # the wheel to the extended buttons. If you have a mousewheel, find
- # Linux configuration info at:
- # https://linuxreviews.org/HOWTO_change_the_mouse_speed_in_X
- bind Text <4> {
- if {!$tk_strictMotif} {
- %W yview scroll -50 pixels
- }
- }
- bind Text <5> {
- if {!$tk_strictMotif} {
- %W yview scroll 50 pixels
- }
+bind Text <Option-MouseWheel> {
+ tk::MouseWheel %W y [tk::ScaleNum %D] -1.2 pixels
+}
+bind Text <Shift-MouseWheel> {
+ tk::MouseWheel %W x [tk::ScaleNum %D] -4.0 pixels
+}
+bind Text <Shift-Option-MouseWheel> {
+ tk::MouseWheel %W x [tk::ScaleNum %D] -1.2 pixels
+}
+bind Text <TouchpadScroll> {
+ lassign [tk::PreciseScrollDeltas %D] tk::Priv(deltaX) tk::Priv(deltaY)
+ if {$tk::Priv(deltaX) != 0} {
+ %W xview scroll [tk::ScaleNum [expr {-$tk::Priv(deltaX)}]] pixels
}
- bind Text <Shift-4> {
- if {!$tk_strictMotif} {
- %W xview scroll -50 pixels
- }
- }
- bind Text <Shift-5> {
- if {!$tk_strictMotif} {
- %W xview scroll 50 pixels
- }
+ if {$tk::Priv(deltaY) != 0} {
+ %W yview scroll [tk::ScaleNum [expr {-$tk::Priv(deltaY)}]] pixels
}
}
@@ -648,8 +603,8 @@ proc ::tk::TextSelectTo {w x y {extend 0}} {
}
# Now find word boundaries
- set first [TextPrevPos $w "$first + 1c" tcl_wordBreakBefore]
- set last [TextNextPos $w "$last - 1c" tcl_wordBreakAfter]
+ set first [TextPrevPos $w "$first + 1c" tk::wordBreakBefore]
+ set last [TextNextPos $w "$last - 1c" tk::wordBreakAfter]
}
line {
# Set initial range based only on the anchor
@@ -1131,26 +1086,31 @@ proc ::tk_textPaste w {
}
# ::tk::TextNextWord --
-# Returns the index of the next word position after a given position in the
-# text. The next word is platform dependent and may be either the next
-# end-of-word position or the next start-of-word position after the next
-# end-of-word position.
+# Returns the index of the next start-of-word position after the next
+# end-of-word position after a given position in the text.
#
# Arguments:
# w - The text window in which the cursor is to move.
# start - Position at which to start search.
-if {[tk windowingsystem] eq "win32"} {
- proc ::tk::TextNextWord {w start} {
- TextNextPos $w [TextNextPos $w $start tcl_endOfWord] \
- tcl_startOfNextWord
- }
-} else {
- proc ::tk::TextNextWord {w start} {
- TextNextPos $w $start tcl_endOfWord
- }
+proc ::tk::TextNextWord {w start} {
+ TextNextPos $w [TextNextPos $w $start tk::endOfWord] \
+ tk::startOfNextWord
}
+# ::tk::TextSelectNextWord --
+# Returns the index of the next end-of-word position after a given
+# position in the text.
+#
+# Arguments:
+# w - The text window in which the cursor is to move.
+# start - Position at which to start search.
+
+proc ::tk::TextSelectNextWord {w start} {
+ TextNextPos $w $start tk::endOfWord
+}
+
+
# ::tk::TextNextPos --
# Returns the index of the next position after the given starting
# position in the text as computed by a specified function.
@@ -1240,3 +1200,96 @@ proc ::tk::TextScanDrag {w x y} {
$w scan dragto $x $y
}
}
+# ::tk::TextUndoRedoProcessMarks --
+#
+# This proc is executed after an undo or redo action.
+# It processes the list of undo/redo marks temporarily set in the
+# text widget to positions delimiting where changes happened, and
+# returns a flat list of ranges. The temporary marks are removed
+# from the text widget.
+#
+# Arguments:
+# w - The text widget
+
+proc ::tk::TextUndoRedoProcessMarks {w} {
+ set indices {}
+ set undoMarks {}
+
+ # only consider the temporary marks set by an undo/redo action
+ foreach mark [$w mark names] {
+ if {[string range $mark 0 11] eq "tk::undoMark"} {
+ lappend undoMarks $mark
+ }
+ }
+
+ # transform marks into indices
+ # the number of undo/redo marks is always even, each right mark
+ # completes a left mark to give a range
+ # this is true because:
+ # - undo/redo only deals with insertions and deletions of text
+ # - insertions may move marks but not delete them
+ # - when deleting text, marks located inside the deleted range
+ # are not erased but moved to the start of the deletion range
+ # . this is done in TkBTreeDeleteIndexRange ("This segment
+ # refuses to die...")
+ # . because MarkDeleteProc does nothing else than returning
+ # a value indicating that marks are not deleted by this
+ # deleteProc
+ # . mark deletion rather happen through [.text mark unset xxx]
+ # which was not used _up to this point of the code_ (it
+ # is a bit later just before exiting the present proc)
+ set nUndoMarks [llength $undoMarks]
+ set n [expr {$nUndoMarks / 2}]
+ set undoMarks [lsort -dictionary $undoMarks]
+ if {$n > 0} {
+ set Lmarks [lrange $undoMarks 0 [expr {$n - 1}]]
+ } else {
+ set Lmarks {}
+ }
+ set Rmarks [lrange $undoMarks $n [llength $undoMarks]]
+ foreach Lmark $Lmarks Rmark $Rmarks {
+ lappend indices [$w index $Lmark] [$w index $Rmark]
+ $w mark unset $Lmark $Rmark
+ }
+
+ # process ranges to:
+ # - remove those already fully included in another range
+ # - merge overlapping ranges
+ set ind [lsort -dictionary -stride 2 $indices]
+ set indices {}
+
+ for {set i 0} {$i < $nUndoMarks} {incr i 2} {
+ set il1 [lindex $ind $i]
+ set ir1 [lindex $ind [expr {$i + 1}]]
+ lappend indices $il1 $ir1
+
+ for {set j [expr {$i + 2}]} {$j < $nUndoMarks} {incr j 2} {
+ set il2 [lindex $ind $j]
+ set ir2 [lindex $ind [expr {$j + 1}]]
+
+ if {[$w compare $il2 > $ir1]} {
+ # second range starts after the end of first range
+ # -> further second ranges do not need to be considered
+ # because ranges were sorted by increasing first index
+ set j $nUndoMarks
+ } else {
+ if {[$w compare $ir2 > $ir1]} {
+ # second range overlaps first range
+ # -> merge them into a single range
+ set indices [lreplace $indices end-1 end]
+ lappend indices $il1 $ir2
+ } else {
+ # second range is fully included in first range
+ # -> ignore it
+ }
+ # in both cases above, the second range shall be
+ # trimmed out from the list of ranges
+ set ind [lreplace $ind $j [expr {$j + 1}]]
+ incr j -2
+ incr nUndoMarks -2
+ }
+ }
+ }
+
+ return $indices
+}