3 # This file defines the default bindings for Tk text widgets and provides
4 # procedures that help in implementing the bindings.
6 # RCS: @(#) $Id: text.tcl,v 1.41.4.2 2009/10/25 13:50:48 dkf Exp $
8 # Copyright (c) 1992-1994 The Regents of the University of California.
9 # Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 # Copyright (c) 1998 by Scriptics Corporation.
12 # See the file "license.terms" for information on usage and redistribution
13 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 #-------------------------------------------------------------------------
17 # Elements of ::tk::Priv that are used in this file:
19 # afterId - If non-null, it means that auto-scanning is underway
20 # and it gives the "after" id for the next auto-scan
21 # command to be executed.
22 # char - Character position on the line; kept in order
23 # to allow moving up or down past short lines while
24 # still remembering the desired position.
25 # mouseMoved - Non-zero means the mouse has moved a significant
26 # amount since the button went down (so, for example,
27 # start dragging out a selection).
28 # prevPos - Used when moving up or down lines via the keyboard.
29 # Keeps track of the previous insert position, so
30 # we can distinguish a series of ups and downs, all
31 # in a row, from a new up or down.
32 # selectMode - The style of selection currently underway:
33 # char, word, or line.
34 # x, y - Last known mouse coordinates for scanning
37 #-------------------------------------------------------------------------
39 #-------------------------------------------------------------------------
40 # The code below creates the default class bindings for text widgets.
41 #-------------------------------------------------------------------------
43 # Standard Motif bindings:
46 tk::TextButton1 %W
%x
%y
47 %W tag remove sel
0.0 end
49 bind Text
<B1-Motion
> {
52 tk::TextSelectTo %W
%x
%y
54 bind Text
<Double-1
> {
55 set tk::Priv(selectMode
) word
56 tk::TextSelectTo %W
%x
%y
57 catch {%W mark
set insert sel.first
}
59 bind Text
<Triple-1
> {
60 set tk::Priv(selectMode
) line
61 tk::TextSelectTo %W
%x
%y
62 catch {%W mark
set insert sel.first
}
65 tk::TextResetAnchor %W
@%x
,%y
66 set tk::Priv(selectMode
) char
67 tk::TextSelectTo %W
%x
%y
69 bind Text
<Double-Shift-1
> {
70 set tk::Priv(selectMode
) word
71 tk::TextSelectTo %W
%x
%y
1
73 bind Text
<Triple-Shift-1
> {
74 set tk::Priv(selectMode
) line
75 tk::TextSelectTo %W
%x
%y
77 bind Text
<B1-Leave
> {
82 bind Text
<B1-Enter
> {
85 bind Text
<ButtonRelease-1
> {
88 bind Text
<Control-1
> {
89 %W mark
set insert
@%x
,%y
92 tk::TextSetCursor %W insert-1displayindices
95 tk::TextSetCursor %W insert
+1displayindices
98 tk::TextSetCursor %W
[tk::TextUpDownLine %W
-1]
101 tk::TextSetCursor %W
[tk::TextUpDownLine %W
1]
103 bind Text
<Shift-Left
> {
104 tk::TextKeySelect %W
[%W index
{insert
- 1displayindices
}]
106 bind Text
<Shift-Right
> {
107 tk::TextKeySelect %W
[%W index
{insert
+ 1displayindices
}]
109 bind Text
<Shift-Up
> {
110 tk::TextKeySelect %W
[tk::TextUpDownLine %W
-1]
112 bind Text
<Shift-Down
> {
113 tk::TextKeySelect %W
[tk::TextUpDownLine %W
1]
115 bind Text
<Control-Left
> {
116 tk::TextSetCursor %W
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord]
118 bind Text
<Control-Right
> {
119 tk::TextSetCursor %W
[tk::TextNextWord %W insert
]
121 bind Text
<Control-Up
> {
122 tk::TextSetCursor %W
[tk::TextPrevPara %W insert
]
124 bind Text
<Control-Down
> {
125 tk::TextSetCursor %W
[tk::TextNextPara %W insert
]
127 bind Text
<Shift-Control-Left
> {
128 tk::TextKeySelect %W
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord]
130 bind Text
<Shift-Control-Right
> {
131 tk::TextKeySelect %W
[tk::TextNextWord %W insert
]
133 bind Text
<Shift-Control-Up
> {
134 tk::TextKeySelect %W
[tk::TextPrevPara %W insert
]
136 bind Text
<Shift-Control-Down
> {
137 tk::TextKeySelect %W
[tk::TextNextPara %W insert
]
140 tk::TextSetCursor %W
[tk::TextScrollPages %W
-1]
142 bind Text
<Shift-Prior
> {
143 tk::TextKeySelect %W
[tk::TextScrollPages %W
-1]
146 tk::TextSetCursor %W
[tk::TextScrollPages %W
1]
148 bind Text
<Shift-Next
> {
149 tk::TextKeySelect %W
[tk::TextScrollPages %W
1]
151 bind Text
<Control-Prior
> {
152 %W xview scroll
-1 page
154 bind Text
<Control-Next
> {
155 %W xview scroll
1 page
159 tk::TextSetCursor %W
{insert display linestart
}
161 bind Text
<Shift-Home
> {
162 tk::TextKeySelect %W
{insert display linestart
}
165 tk::TextSetCursor %W
{insert display lineend
}
167 bind Text
<Shift-End
> {
168 tk::TextKeySelect %W
{insert display lineend
}
170 bind Text
<Control-Home
> {
171 tk::TextSetCursor %W
1.0
173 bind Text
<Control-Shift-Home
> {
174 tk::TextKeySelect %W
1.0
176 bind Text
<Control-End
> {
177 tk::TextSetCursor %W
{end
- 1 indices
}
179 bind Text
<Control-Shift-End
> {
180 tk::TextKeySelect %W
{end
- 1 indices
}
184 if {[%W cget
-state] eq
"normal"} {
190 bind Text
<Shift-Tab
> {
191 # Needed only to keep <Tab> binding from triggering; doesn't
192 # have to actually do anything.
195 bind Text
<Control-Tab
> {
196 focus [tk_focusNext %W
]
198 bind Text
<Control-Shift-Tab
> {
199 focus [tk_focusPrev %W
]
201 bind Text
<Control-i
> {
206 if {[%W cget
-autoseparators]} {
211 if {[%W tag nextrange sel
1.0 end
] ne
""} {
212 %W delete sel.first sel.last
214 if {[%W compare end
!= insert
+1c
]} {
220 bind Text
<BackSpace
> {
221 if {[%W tag nextrange sel
1.0 end
] ne
""} {
222 %W delete sel.first sel.last
224 if {[%W compare insert
!= 1.0]} {
231 bind Text
<Control-space
> {
232 %W mark
set [tk::TextAnchor %W
] insert
235 %W mark
set [tk::TextAnchor %W
] insert
237 bind Text
<Control-Shift-space
> {
238 set tk::Priv(selectMode
) char
239 tk::TextKeyExtend %W insert
241 bind Text
<Shift-Select
> {
242 set tk::Priv(selectMode
) char
243 tk::TextKeyExtend %W insert
245 bind Text
<Control-slash
> {
246 %W tag add sel
1.0 end
248 bind Text
<Control-backslash
> {
249 %W tag remove sel
1.0 end
257 bind Text
<<Paste
>> {
260 bind Text
<<Clear
>> {
261 catch {%W delete sel.first sel.last
}
263 bind Text
<<PasteSelection
>> {
264 if {$tk_strictMotif ||
![info exists
tk::Priv(mouseMoved
)]
265 ||
!$tk::Priv(mouseMoved
)} {
266 tk::TextPasteSelection %W
%x
%y
270 catch {tk::TextInsert %W
[::tk::GetSelection %W PRIMARY
]}
272 bind Text
<KeyPress
> {
276 # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
277 # Otherwise, if a widget binding for one of these is defined, the
278 # <KeyPress> class binding will also fire and insert the character,
279 # which is wrong. Ditto for <Escape>.
281 bind Text
<Alt-KeyPress
> {# nothing }
282 bind Text
<Meta-KeyPress
> {# nothing}
283 bind Text
<Control-KeyPress
> {# nothing}
284 bind Text
<Escape
> {# nothing}
285 bind Text
<KP_Enter
> {# nothing}
286 if {[tk windowingsystem
] eq
"aqua"} {
287 bind Text
<Command-KeyPress
> {# nothing}
290 # Additional emacs-like bindings:
292 bind Text
<Control-a
> {
293 if {!$tk_strictMotif} {
294 tk::TextSetCursor %W
{insert display linestart
}
297 bind Text
<Control-b
> {
298 if {!$tk_strictMotif} {
299 tk::TextSetCursor %W insert-1displayindices
302 bind Text
<Control-d
> {
303 if {!$tk_strictMotif && [%W compare end
!= insert
+1c
]} {
307 bind Text
<Control-e
> {
308 if {!$tk_strictMotif} {
309 tk::TextSetCursor %W
{insert display lineend
}
312 bind Text
<Control-f
> {
313 if {!$tk_strictMotif} {
314 tk::TextSetCursor %W insert
+1displayindices
317 bind Text
<Control-k
> {
318 if {!$tk_strictMotif && [%W compare end
!= insert
+1c
]} {
319 if {[%W compare insert
== {insert lineend
}]} {
322 %W delete insert
{insert lineend
}
326 bind Text
<Control-n
> {
327 if {!$tk_strictMotif} {
328 tk::TextSetCursor %W
[tk::TextUpDownLine %W
1]
331 bind Text
<Control-o
> {
332 if {!$tk_strictMotif} {
334 %W mark
set insert insert-1c
337 bind Text
<Control-p
> {
338 if {!$tk_strictMotif} {
339 tk::TextSetCursor %W
[tk::TextUpDownLine %W
-1]
342 bind Text
<Control-t
> {
343 if {!$tk_strictMotif} {
349 catch { %W edit undo
}
353 catch { %W edit redo
}
357 if {!$tk_strictMotif} {
358 tk::TextSetCursor %W
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord]
362 if {!$tk_strictMotif && [%W compare end
!= insert
+1c
]} {
363 %W delete insert
[tk::TextNextWord %W insert
]
367 if {!$tk_strictMotif} {
368 tk::TextSetCursor %W
[tk::TextNextWord %W insert
]
371 bind Text
<Meta-less
> {
372 if {!$tk_strictMotif} {
373 tk::TextSetCursor %W
1.0
376 bind Text
<Meta-greater
> {
377 if {!$tk_strictMotif} {
378 tk::TextSetCursor %W end-1c
381 bind Text
<Meta-BackSpace
> {
382 if {!$tk_strictMotif} {
383 %W delete
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord] insert
386 bind Text
<Meta-Delete
> {
387 if {!$tk_strictMotif} {
388 %W delete
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord] insert
392 # Macintosh only bindings:
394 if {[tk windowingsystem
] eq
"aqua"} {
395 bind Text
<Option-Left
> {
396 tk::TextSetCursor %W
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord]
398 bind Text
<Option-Right
> {
399 tk::TextSetCursor %W
[tk::TextNextWord %W insert
]
401 bind Text
<Option-Up
> {
402 tk::TextSetCursor %W
[tk::TextPrevPara %W insert
]
404 bind Text
<Option-Down
> {
405 tk::TextSetCursor %W
[tk::TextNextPara %W insert
]
407 bind Text
<Shift-Option-Left
> {
408 tk::TextKeySelect %W
[tk::TextPrevPos %W insert
tcl_startOfPreviousWord]
410 bind Text
<Shift-Option-Right
> {
411 tk::TextKeySelect %W
[tk::TextNextWord %W insert
]
413 bind Text
<Shift-Option-Up
> {
414 tk::TextKeySelect %W
[tk::TextPrevPara %W insert
]
416 bind Text
<Shift-Option-Down
> {
417 tk::TextKeySelect %W
[tk::TextNextPara %W insert
]
419 bind Text
<Control-v
> {
420 tk::TextScrollPages %W
1
423 # End of Mac only bindings
426 # A few additional bindings of my own.
428 bind Text
<Control-h
> {
429 if {!$tk_strictMotif && [%W compare insert
!= 1.0]} {
435 if {!$tk_strictMotif} {
436 tk::TextScanMark %W
%x
%y
439 bind Text
<B2-Motion
> {
440 if {!$tk_strictMotif} {
441 tk::TextScanDrag %W
%x
%y
444 set ::tk::Priv(prevPos
) {}
446 # The MouseWheel will typically only fire on Windows and MacOS X.
447 # However, someone could use the "event generate" command to produce one
448 # on other platforms. We must be careful not to round -ve values of %D
451 if {[tk windowingsystem
] eq
"aqua"} {
452 bind Text
<MouseWheel
> {
453 %W yview scroll
[expr {-15 * (%D
)}] pixels
455 bind Text
<Option-MouseWheel
> {
456 %W yview scroll
[expr {-150 * (%D
)}] pixels
458 bind Text
<Shift-MouseWheel
> {
459 %W xview scroll
[expr {-15 * (%D
)}] pixels
461 bind Text
<Shift-Option-MouseWheel
> {
462 %W xview scroll
[expr {-150 * (%D
)}] pixels
465 # We must make sure that positive and negative movements are rounded
466 # equally to integers, avoiding the problem that
470 # The following code ensure equal +/- behaviour.
471 bind Text
<MouseWheel
> {
473 %W yview scroll
[expr {-%D
/3}] pixels
475 %W yview scroll
[expr {(2-%D
)/3}] pixels
480 if {"x11" eq
[tk windowingsystem
]} {
481 # Support for mousewheels on Linux/Unix commonly comes through mapping
482 # the wheel to the extended buttons. If you have a mousewheel, find
483 # Linux configuration info at:
484 # http://www.inria.fr/koala/colas/mouse-wheel-scroll/
486 if {!$tk_strictMotif} {
487 %W yview scroll
-50 pixels
491 if {!$tk_strictMotif} {
492 %W yview scroll
50 pixels
497 # ::tk::TextClosestGap --
498 # Given x and y coordinates, this procedure finds the closest boundary
499 # between characters to the given coordinates and returns the index
500 # of the character just after the boundary.
503 # w - The text window.
504 # x - X-coordinate within the window.
505 # y - Y-coordinate within the window.
507 proc ::tk::TextClosestGap {w x y
} {
508 set pos
[$w index
@$x,$y]
509 set bbox
[$w bbox
$pos]
513 if {($x - [lindex $bbox 0]) < ([lindex $bbox 2]/2)} {
516 $w index
"$pos + 1 char"
519 # ::tk::TextButton1 --
520 # This procedure is invoked to handle button-1 presses in text
521 # widgets. It moves the insertion cursor, sets the selection anchor,
522 # and claims the input focus.
525 # w - The text window in which the button was pressed.
526 # x - The x-coordinate of the button press.
527 # y - The x-coordinate of the button press.
529 proc ::tk::TextButton1 {w x y
} {
532 set Priv
(selectMode
) char
533 set Priv
(mouseMoved
) 0
535 set anchorname
[tk::TextAnchor $w]
536 $w mark
set insert
[TextClosestGap
$w $x $y]
537 $w mark
set $anchorname insert
538 # Set the anchor mark's gravity depending on the click position
539 # relative to the gap
540 set bbox
[$w bbox
[$w index
$anchorname]]
541 if {$x > [lindex $bbox 0]} {
542 $w mark gravity
$anchorname right
544 $w mark gravity
$anchorname left
546 # Allow focus in any case on Windows, because that will let the
547 # selection be displayed even for state disabled text widgets.
548 if {$::tcl_platform(platform
) eq
"windows" \
549 ||
[$w cget
-state] eq
"normal"} {
552 if {[$w cget
-autoseparators]} {
557 # ::tk::TextSelectTo --
558 # This procedure is invoked to extend the selection, typically when
559 # dragging it with the mouse. Depending on the selection mode (character,
560 # word, line) it selects in different-sized units. This procedure
561 # ignores mouse motions initially until the mouse has moved from
562 # one character to another or until there have been multiple clicks.
564 # Note that the 'anchor' is implemented programmatically using
565 # a text widget mark, and uses a name that will be unique for each
566 # text widget (even when there are multiple peers). Currently the
567 # anchor is considered private to Tk, hence the name 'tk::anchor$w'.
570 # w - The text window in which the button was pressed.
571 # x - Mouse x position.
572 # y - Mouse y position.
574 set ::tk::Priv(textanchoruid
) 0
576 proc ::tk::TextAnchor {w
} {
578 if {![info exists Priv
(textanchor
,$w)]} {
579 set Priv
(textanchor
,$w) tk::anchor[incr Priv
(textanchoruid
)]
581 return $Priv(textanchor
,$w)
584 proc ::tk::TextSelectTo {w x y
{extend
0}} {
588 set anchorname
[tk::TextAnchor $w]
589 set cur
[TextClosestGap
$w $x $y]
590 if {[catch {$w index
$anchorname}]} {
591 $w mark
set $anchorname $cur
593 set anchor
[$w index
$anchorname]
594 if {[$w compare
$cur != $anchor] ||
(abs
($Priv(pressX
) - $x) >= 3)} {
595 set Priv
(mouseMoved
) 1
597 switch -- $Priv(selectMode
) {
599 if {[$w compare
$cur < $anchorname]} {
603 set first
$anchorname
608 # Set initial range based only on the anchor (1 char min width)
609 if {[$w mark gravity
$anchorname] eq
"right"} {
610 set first
$anchorname
611 set last
"$anchorname + 1c"
613 set first
"$anchorname - 1c"
616 # Extend range (if necessary) based on the current point
617 if {[$w compare
$cur < $first]} {
619 } elseif
{[$w compare
$cur > $last]} {
623 # Now find word boundaries
624 set first
[TextPrevPos
$w "$first + 1c" tcl_wordBreakBefore]
625 set last
[TextNextPos
$w "$last - 1c" tcl_wordBreakAfter]
628 # Set initial range based only on the anchor
629 set first
"$anchorname linestart"
630 set last
"$anchorname lineend"
632 # Extend range (if necessary) based on the current point
633 if {[$w compare
$cur < $first]} {
634 set first
"$cur linestart"
635 } elseif
{[$w compare
$cur > $last]} {
636 set last
"$cur lineend"
638 set first
[$w index
$first]
639 set last
[$w index
"$last + 1c"]
642 if {$Priv(mouseMoved
) ||
($Priv(selectMode
) ne
"char")} {
643 $w tag remove sel
0.0 end
644 $w mark
set insert
$cur
645 $w tag add sel
$first $last
646 $w tag remove sel
$last end
651 # ::tk::TextKeyExtend --
652 # This procedure handles extending the selection from the keyboard,
653 # where the point to extend to is really the boundary between two
654 # characters rather than a particular character.
657 # w - The text window.
658 # index - The point to which the selection is to be extended.
660 proc ::tk::TextKeyExtend {w index
} {
662 set anchorname
[tk::TextAnchor $w]
663 set cur
[$w index
$index]
664 if {[catch {$w index
$anchorname}]} {
665 $w mark
set $anchorname $cur
667 set anchor
[$w index
$anchorname]
668 if {[$w compare
$cur < $anchorname]} {
672 set first
$anchorname
675 $w tag remove sel
0.0 $first
676 $w tag add sel
$first $last
677 $w tag remove sel
$last end
680 # ::tk::TextPasteSelection --
681 # This procedure sets the insertion cursor to the mouse position,
682 # inserts the selection, and sets the focus to the window.
685 # w - The text window.
686 # x, y - Position of the mouse.
688 proc ::tk::TextPasteSelection {w x y
} {
689 $w mark
set insert
[TextClosestGap
$w $x $y]
690 if {![catch {::tk::GetSelection $w PRIMARY
} sel
]} {
691 set oldSeparator
[$w cget
-autoseparators]
693 $w configure
-autoseparators 0
696 $w insert insert
$sel
699 $w configure
-autoseparators 1
702 if {[$w cget
-state] eq
"normal"} {
707 # ::tk::TextAutoScan --
708 # This procedure is invoked when the mouse leaves a text window
709 # with button 1 down. It scrolls the window up, down, left, or right,
710 # depending on where the mouse is (this information was saved in
711 # ::tk::Priv(x) and ::tk::Priv(y)), and reschedules itself as an "after"
712 # command so that the window continues to scroll until the mouse
713 # moves back into the window or the mouse button is released.
716 # w - The text window.
718 proc ::tk::TextAutoScan {w
} {
720 if {![winfo exists
$w]} {
723 if {$Priv(y
) >= [winfo height
$w]} {
724 $w yview scroll
[expr {1 + $Priv(y
) - [winfo height
$w]}] pixels
725 } elseif
{$Priv(y
) < 0} {
726 $w yview scroll
[expr {-1 + $Priv(y
)}] pixels
727 } elseif
{$Priv(x
) >= [winfo width
$w]} {
728 $w xview scroll
2 units
729 } elseif
{$Priv(x
) < 0} {
730 $w xview scroll
-2 units
734 TextSelectTo
$w $Priv(x
) $Priv(y
)
735 set Priv
(afterId
) [after 50 [list tk::TextAutoScan $w]]
738 # ::tk::TextSetCursor
739 # Move the insertion cursor to a given position in a text. Also
740 # clears the selection, if there is one in the text, and makes sure
741 # that the insertion cursor is visible. Also, don't let the insertion
742 # cursor appear on the dummy last line of the text.
745 # w - The text window.
746 # pos - The desired new position for the cursor in the window.
748 proc ::tk::TextSetCursor {w pos
} {
749 if {[$w compare
$pos == end
]} {
750 set pos
{end
- 1 chars
}
752 $w mark
set insert
$pos
753 $w tag remove sel
1.0 end
755 if {[$w cget
-autoseparators]} {
760 # ::tk::TextKeySelect
761 # This procedure is invoked when stroking out selections using the
762 # keyboard. It moves the cursor to a new position, then extends
763 # the selection to that position.
766 # w - The text window.
767 # new - A new position for the insertion cursor (the cursor hasn't
768 # actually been moved to this position yet).
770 proc ::tk::TextKeySelect {w new
} {
771 set anchorname
[tk::TextAnchor $w]
772 if {[$w tag nextrange sel
1.0 end
] eq
""} {
773 if {[$w compare
$new < insert
]} {
774 $w tag add sel
$new insert
776 $w tag add sel insert
$new
778 $w mark
set $anchorname insert
780 if {[$w compare
$new < $anchorname]} {
784 set first
$anchorname
787 $w tag remove sel
1.0 $first
788 $w tag add sel
$first $last
789 $w tag remove sel
$last end
791 $w mark
set insert
$new
796 # ::tk::TextResetAnchor --
797 # Set the selection anchor to whichever end is farthest from the
798 # index argument. One special trick: if the selection has two or
799 # fewer characters, just leave the anchor where it is. In this
800 # case it doesn't matter which point gets chosen for the anchor,
801 # and for the things like Shift-Left and Shift-Right this produces
802 # better behavior when the cursor moves back and forth across the
806 # w - The text widget.
807 # index - Position at which mouse button was pressed, which determines
808 # which end of selection should be used as anchor point.
810 proc ::tk::TextResetAnchor {w index
} {
811 if {[$w tag ranges sel
] eq
""} {
812 # Don't move the anchor if there is no selection now; this
813 # makes the widget behave "correctly" when the user clicks
814 # once, then shift-clicks somewhere -- ie, the area between
815 # the two clicks will be selected. [Bug: 5929].
818 set anchorname
[tk::TextAnchor $w]
819 set a
[$w index
$index]
820 set b
[$w index sel.first
]
821 set c
[$w index sel.last
]
822 if {[$w compare
$a < $b]} {
823 $w mark
set $anchorname sel.last
826 if {[$w compare
$a > $c]} {
827 $w mark
set $anchorname sel.first
830 scan $a "%d.%d" lineA chA
831 scan $b "%d.%d" lineB chB
832 scan $c "%d.%d" lineC chC
833 if {$lineB < $lineC+2} {
834 set total
[string length
[$w get
$b $c]]
838 if {[string length
[$w get
$b $a]] < ($total/2)} {
839 $w mark
set $anchorname sel.last
841 $w mark
set $anchorname sel.first
845 if {($lineA-$lineB) < ($lineC-$lineA)} {
846 $w mark
set $anchorname sel.last
848 $w mark
set $anchorname sel.first
852 # ::tk::TextInsert --
853 # Insert a string into a text at the point of the insertion cursor.
854 # If there is a selection in the text, and it covers the point of the
855 # insertion cursor, then delete the selection before inserting.
858 # w - The text window in which to insert the string
859 # s - The string to insert (usually just a single character)
861 proc ::tk::TextInsert {w s
} {
862 if {$s eq
"" ||
[$w cget
-state] eq
"disabled"} {
866 if {[llength [set range
[$w tag ranges sel
]]]} {
867 if {[$w compare
[lindex $range 0] <= insert
] \
868 && [$w compare
[lindex $range end
] >= insert
]} {
869 set oldSeparator
[$w cget
-autoseparators]
871 $w configure
-autoseparators 0
875 $w delete
[lindex $range 0] [lindex $range end
]
880 if {$compound && $oldSeparator} {
882 $w configure
-autoseparators 1
886 # ::tk::TextUpDownLine --
887 # Returns the index of the character one display line above or below the
888 # insertion cursor. There are two tricky things here. First, we want to
889 # maintain the original x position across repeated operations, even though
890 # some lines that will get passed through don't have enough characters to
891 # cover the original column. Second, don't try to scroll past the
892 # beginning or end of the text.
895 # w - The text window in which the cursor is to move.
896 # n - The number of display lines to move: -1 for up one line,
897 # +1 for down one line.
899 proc ::tk::TextUpDownLine {w n
} {
902 set i
[$w index insert
]
903 if {$Priv(prevPos
) ne
$i} {
904 set Priv
(textPosOrig
) $i
906 set lines
[$w count
-displaylines $Priv(textPosOrig
) $i]
908 "$Priv(textPosOrig) + [expr {$lines + $n}] displaylines"]
909 if {[$w compare
$new == end
] \
910 ||
[$w compare
$new == "insert display linestart"]} {
913 set Priv
(prevPos
) $new
917 # ::tk::TextPrevPara --
918 # Returns the index of the beginning of the paragraph just before a given
919 # position in the text (the beginning of a paragraph is the first non-blank
920 # character after a blank line).
923 # w - The text window in which the cursor is to move.
924 # pos - Position at which to start search.
926 proc ::tk::TextPrevPara {w pos
} {
927 set pos
[$w index
"$pos linestart"]
929 if {([$w get
"$pos - 1 line"] eq
"\n" && ([$w get
$pos] ne
"\n")) \
931 if {[regexp -indices -- {^
[ \t]+(.
)} \
932 [$w get
$pos "$pos lineend"] -> index
]} {
933 set pos
[$w index
"$pos + [lindex $index 0] chars"]
935 if {[$w compare
$pos != insert
] ||
[lindex [split $pos .
] 0]==1} {
939 set pos
[$w index
"$pos - 1 line"]
943 # ::tk::TextNextPara --
944 # Returns the index of the beginning of the paragraph just after a given
945 # position in the text (the beginning of a paragraph is the first non-blank
946 # character after a blank line).
949 # w - The text window in which the cursor is to move.
950 # start - Position at which to start search.
952 proc ::tk::TextNextPara {w start
} {
953 set pos
[$w index
"$start linestart + 1 line"]
954 while {[$w get
$pos] ne
"\n"} {
955 if {[$w compare
$pos == end
]} {
956 return [$w index
"end - 1c"]
958 set pos
[$w index
"$pos + 1 line"]
960 while {[$w get
$pos] eq
"\n"} {
961 set pos
[$w index
"$pos + 1 line"]
962 if {[$w compare
$pos == end
]} {
963 return [$w index
"end - 1c"]
966 if {[regexp -indices -- {^
[ \t]+(.
)} \
967 [$w get
$pos "$pos lineend"] -> index
]} {
968 return [$w index
"$pos + [lindex $index 0] chars"]
973 # ::tk::TextScrollPages --
974 # This is a utility procedure used in bindings for moving up and down
975 # pages and possibly extending the selection along the way. It scrolls
976 # the view in the widget by the number of pages, and it returns the
977 # index of the character that is at the same position in the new view
978 # as the insertion cursor used to be in the old view.
981 # w - The text window in which the cursor is to move.
982 # count - Number of pages forward to scroll; may be negative
983 # to scroll backwards.
985 proc ::tk::TextScrollPages {w count
} {
986 set bbox
[$w bbox insert
]
987 $w yview scroll
$count pages
989 return [$w index
@[expr {[winfo height
$w]/2}],0]
991 return [$w index
@[lindex $bbox 0],[lindex $bbox 1]]
994 # ::tk::TextTranspose --
995 # This procedure implements the "transpose" function for text widgets.
996 # It tranposes the characters on either side of the insertion cursor,
997 # unless the cursor is at the end of the line. In this case it
998 # transposes the two characters to the left of the cursor. In either
999 # case, the cursor ends up to the right of the transposed characters.
1002 # w - Text window in which to transpose.
1004 proc ::tk::TextTranspose w
{
1006 if {[$w compare
$pos != "$pos lineend"]} {
1007 set pos
[$w index
"$pos + 1 char"]
1009 set new
[$w get
"$pos - 1 char"][$w get
"$pos - 2 char"]
1010 if {[$w compare
"$pos - 1 char" == 1.0]} {
1013 # ensure this is seen as an atomic op to undo
1014 set autosep
[$w cget
-autoseparators]
1016 $w configure
-autoseparators 0
1019 $w delete
"$pos - 2 char" $pos
1020 $w insert insert
$new
1024 $w configure
-autoseparators $autosep
1029 # This procedure copies the selection from a text widget into the
1033 # w - Name of a text widget.
1035 proc ::tk_textCopy w
{
1036 if {![catch {set data
[$w get sel.first sel.last
]}]} {
1037 clipboard clear
-displayof $w
1038 clipboard append -displayof $w $data
1043 # This procedure copies the selection from a text widget into the
1044 # clipboard, then deletes the selection (if it exists in the given
1048 # w - Name of a text widget.
1050 proc ::tk_textCut w
{
1051 if {![catch {set data
[$w get sel.first sel.last
]}]} {
1052 clipboard clear
-displayof $w
1053 clipboard append -displayof $w $data
1054 $w delete sel.first sel.last
1059 # This procedure pastes the contents of the clipboard to the insertion
1060 # point in a text widget.
1063 # w - Name of a text widget.
1065 proc ::tk_textPaste w
{
1067 if {![catch {::tk::GetSelection $w CLIPBOARD
} sel
]} {
1068 set oldSeparator
[$w cget
-autoseparators]
1069 if {$oldSeparator} {
1070 $w configure
-autoseparators 0
1073 if {[tk windowingsystem
] ne
"x11"} {
1074 catch { $w delete sel.first sel.last
}
1076 $w insert insert
$sel
1077 if {$oldSeparator} {
1079 $w configure
-autoseparators 1
1084 # ::tk::TextNextWord --
1085 # Returns the index of the next word position after a given position in the
1086 # text. The next word is platform dependent and may be either the next
1087 # end-of-word position or the next start-of-word position after the next
1088 # end-of-word position.
1091 # w - The text window in which the cursor is to move.
1092 # start - Position at which to start search.
1094 if {$tcl_platform(platform
) eq
"windows"} {
1095 proc ::tk::TextNextWord {w start
} {
1096 TextNextPos
$w [TextNextPos
$w $start tcl_endOfWord] \
1100 proc ::tk::TextNextWord {w start
} {
1101 TextNextPos
$w $start tcl_endOfWord
1105 # ::tk::TextNextPos --
1106 # Returns the index of the next position after the given starting
1107 # position in the text as computed by a specified function.
1110 # w - The text window in which the cursor is to move.
1111 # start - Position at which to start search.
1112 # op - Function to use to find next position.
1114 proc ::tk::TextNextPos {w start op
} {
1117 while {[$w compare
$cur < end
]} {
1118 set text $text[$w get
-displaychars $cur "$cur lineend + 1c"]
1119 set pos
[$op $text 0]
1121 return [$w index
"$start + $pos display chars"]
1123 set cur
[$w index
"$cur lineend +1c"]
1128 # ::tk::TextPrevPos --
1129 # Returns the index of the previous position before the given starting
1130 # position in the text as computed by a specified function.
1133 # w - The text window in which the cursor is to move.
1134 # start - Position at which to start search.
1135 # op - Function to use to find next position.
1137 proc ::tk::TextPrevPos {w start op
} {
1140 while {[$w compare
$cur > 0.0]} {
1141 set text [$w get
-displaychars "$cur linestart - 1c" $cur]$text
1142 set pos
[$op $text end
]
1144 return [$w index
"$cur linestart - 1c + $pos display chars"]
1146 set cur
[$w index
"$cur linestart - 1c"]
1151 # ::tk::TextScanMark --
1153 # Marks the start of a possible scan drag operation
1156 # w - The text window from which the text to get
1157 # x - x location on screen
1158 # y - y location on screen
1160 proc ::tk::TextScanMark {w x y
} {
1165 set Priv
(mouseMoved
) 0
1168 # ::tk::TextScanDrag --
1170 # Marks the start of a possible scan drag operation
1173 # w - The text window from which the text to get
1174 # x - x location on screen
1175 # y - y location on screen
1177 proc ::tk::TextScanDrag {w x y
} {
1179 # Make sure these exist, as some weird situations can trigger the
1180 # motion binding without the initial press. [Bug #220269]
1181 if {![info exists Priv
(x
)]} {
1184 if {![info exists Priv
(y
)]} {
1187 if {($x != $Priv(x
)) ||
($y != $Priv(y
))} {
1188 set Priv
(mouseMoved
) 1
1190 if {[info exists Priv
(mouseMoved
)] && $Priv(mouseMoved
)} {
1191 $w scan dragto
$x $y