# This file defines the default bindings for Tk text widgets.
# @(#) text.tcl 1.18 94/12/17 16:05:26
# Copyright (c) 1992-1994 The Regents of the University of California.
# Copyright (c) 1994 Sun Microsystems, Inc.
# Copyright (c) 1995-1999 Nick Ing-Simmons
# Copyright (c) 1999 Greg London
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
$VERSION = '3.044'; # $Id: //depot/Tk8/Text/Text.pm#44 $
use Tk qw(Ev $XS_VERSION);
use base qw(Tk::Clipboard Tk::Widget);
Construct Tk
::Widget
'Text';
sub Tk_cmd
{ \
&Tk
::text
}
sub Tk
::Widget
::ScrlText
{ shift->Scrolled('Text' => @_) }
Tk
::Methods
('bbox','compare','debug','delete','dlineinfo','dump',
'get','image','index','insert','mark','scan','search',
'see','tag','window','xview','yview');
use Tk
::Submethods
( 'mark' => [qw(gravity names next previous set unset)],
'scan' => [qw(mark dragto)],
'tag' => [qw(add bind cget configure delete lower
names nextrange prevrange raise ranges remove)],
'window' => [qw(cget configure create names)],
'image' => [qw(cget configure create names)],
'xview' => [qw(moveto scroll)],
'yview' => [qw(moveto scroll)],
# Standard Motif bindings:
$mw->bind($class,'<Meta-B1-Motion>','NoOp');
$mw->bind($class,'<Meta-1>','NoOp');
$mw->bind($class,'<Alt-KeyPress>','NoOp');
$mw->bind($class,'<Escape>','unselectAll');
$mw->bind($class,'<1>',['Button1',Ev
('x'),Ev
('y')]);
$mw->bind($class,'<B1-Motion>','B1_Motion' ) ;
$mw->bind($class,'<B1-Leave>','B1_Leave' ) ;
$mw->bind($class,'<B1-Enter>','CancelRepeat');
$mw->bind($class,'<ButtonRelease-1>','CancelRepeat');
$mw->bind($class,'<Control-1>',['markSet','insert',Ev
('@')]);
$mw->bind($class,'<Double-1>','selectWord' ) ;
$mw->bind($class,'<Triple-1>','selectLine' ) ;
$mw->bind($class,'<Shift-1>','adjustSelect' ) ;
$mw->bind($class,'<Double-Shift-1>',['SelectTo',Ev
('@'),'word']);
$mw->bind($class,'<Triple-Shift-1>',['SelectTo',Ev
('@'),'line']);
$mw->bind($class,'<Left>',['SetCursor',Ev
('index','insert-1c')]);
$mw->bind($class,'<Shift-Left>',['KeySelect',Ev
('index','insert-1c')]);
$mw->bind($class,'<Control-Left>',['SetCursor',Ev
('index','insert-1c wordstart')]);
$mw->bind($class,'<Shift-Control-Left>',['KeySelect',Ev
('index','insert-1c wordstart')]);
$mw->bind($class,'<Right>',['SetCursor',Ev
('index','insert+1c')]);
$mw->bind($class,'<Shift-Right>',['KeySelect',Ev
('index','insert+1c')]);
$mw->bind($class,'<Control-Right>',['SetCursor',Ev
('index','insert+1c wordend')]);
$mw->bind($class,'<Shift-Control-Right>',['KeySelect',Ev
('index','insert wordend')]);
$mw->bind($class,'<Up>',['SetCursor',Ev
('UpDownLine',-1)]);
$mw->bind($class,'<Shift-Up>',['KeySelect',Ev
('UpDownLine',-1)]);
$mw->bind($class,'<Control-Up>',['SetCursor',Ev
('PrevPara','insert')]);
$mw->bind($class,'<Shift-Control-Up>',['KeySelect',Ev
('PrevPara','insert')]);
$mw->bind($class,'<Down>',['SetCursor',Ev
('UpDownLine',1)]);
$mw->bind($class,'<Shift-Down>',['KeySelect',Ev
('UpDownLine',1)]);
$mw->bind($class,'<Control-Down>',['SetCursor',Ev
('NextPara','insert')]);
$mw->bind($class,'<Shift-Control-Down>',['KeySelect',Ev
('NextPara','insert')]);
$mw->bind($class,'<Home>',['SetCursor','insert linestart']);
$mw->bind($class,'<Shift-Home>',['KeySelect','insert linestart']);
$mw->bind($class,'<Control-Home>',['SetCursor','1.0']);
$mw->bind($class,'<Control-Shift-Home>',['KeySelect','1.0']);
$mw->bind($class,'<End>',['SetCursor','insert lineend']);
$mw->bind($class,'<Shift-End>',['KeySelect','insert lineend']);
$mw->bind($class,'<Control-End>',['SetCursor','end-1char']);
$mw->bind($class,'<Control-Shift-End>',['KeySelect','end-1char']);
$mw->bind($class,'<Prior>',['SetCursor',Ev
('ScrollPages',-1)]);
$mw->bind($class,'<Shift-Prior>',['KeySelect',Ev
('ScrollPages',-1)]);
$mw->bind($class,'<Control-Prior>',['xview','scroll',-1,'page']);
$mw->bind($class,'<Next>',['SetCursor',Ev
('ScrollPages',1)]);
$mw->bind($class,'<Shift-Next>',['KeySelect',Ev
('ScrollPages',1)]);
$mw->bind($class,'<Control-Next>',['xview','scroll',1,'page']);
$mw->bind($class,'<Shift-Tab>', 'NoOp'); # Needed only to keep <Tab> binding from triggering; does not have to actually do anything.
$mw->bind($class,'<Control-Tab>','focusNext');
$mw->bind($class,'<Control-Shift-Tab>','focusPrev');
$mw->bind($class,'<Control-space>',['markSet','anchor','insert']);
$mw->bind($class,'<Select>',['markSet','anchor','insert']);
$mw->bind($class,'<Control-Shift-space>',['SelectTo','insert','char']);
$mw->bind($class,'<Shift-Select>',['SelectTo','insert','char']);
$mw->bind($class,'<Control-slash>','selectAll');
$mw->bind($class,'<Control-backslash>','unselectAll');
$mw->bind($class,'<Control-a>', ['SetCursor','insert linestart']);
$mw->bind($class,'<Control-b>', ['SetCursor','insert-1c']);
$mw->bind($class,'<Control-e>', ['SetCursor','insert lineend']);
$mw->bind($class,'<Control-f>', ['SetCursor','insert+1c']);
$mw->bind($class,'<Meta-b>', ['SetCursor','insert-1c wordstart']);
$mw->bind($class,'<Meta-f>', ['SetCursor','insert wordend']);
$mw->bind($class,'<Meta-less>', ['SetCursor','1.0']);
$mw->bind($class,'<Meta-greater>', ['SetCursor','end-1c']);
$mw->bind($class,'<Control-n>', ['SetCursor',Ev
('UpDownLine',1)]);
$mw->bind($class,'<Control-p>', ['SetCursor',Ev
('UpDownLine',-1)]);
$mw->bind($class,'<2>',['Button2',Ev
('x'),Ev
('y')]);
$mw->bind($class,'<B2-Motion>',['Motion2',Ev
('x'),Ev
('y')]);
$mw->bind($class,'<Destroy>','Destroy');
$mw->bind($class, '<3>', ['PostPopupMenu', Ev
('X'), Ev
('Y')] );
$w->tagAdd('sel','1.0','end');
$w->tagRemove('sel','1.0','end');
$w->ResetAnchor($Ev->xy);
$w->SelectTo($Ev->xy,'char')
$w->SelectTo($Ev->xy,'line');
Tk
::catch
{ $w->markSet('insert','sel.first') };
$w->SelectTo($Ev->xy,'word');
Tk
::catch
{ $w->markSet('insert','sel.first') }
$class->SUPER::ClassInit
($mw);
$mw->bind($class,'<Tab>', 'insertTab');
$mw->bind($class,'<Control-i>', ['Insert',"\t"]);
$mw->bind($class,'<Return>', ['Insert',"\n"]);
$mw->bind($class,'<Delete>','Delete');
$mw->bind($class,'<BackSpace>','Backspace');
$mw->bind($class,'<Insert>', \
&ToggleInsertMode
) ;
$mw->bind($class,'<KeyPress>',['InsertKeypress',Ev
('A')]);
$mw->bind($class,'<F1>', 'clipboardColumnCopy');
$mw->bind($class,'<F2>', 'clipboardColumnCut');
$mw->bind($class,'<F3>', 'clipboardColumnPaste');
# Additional emacs-like bindings:
$mw->bind($class,'<Control-d>',['delete','insert']);
$mw->bind($class,'<Control-k>','deleteToEndofLine') ;
$mw->bind($class,'<Control-o>','openLine');
$mw->bind($class,'<Control-t>','Transpose');
$mw->bind($class,'<Meta-d>',['delete','insert','insert wordend']);
$mw->bind($class,'<Meta-BackSpace>',['delete','insert-1c wordstart','insert']);
# A few additional bindings of my own.
$mw->bind($class,'<Control-h>','deleteBefore');
$mw->bind($class,'<ButtonRelease-2>','ButtonRelease2');
if ($w->compare('insert','==','insert lineend'))
$w->delete('insert','insert lineend')
$w->insert('insert',"\n");
$w->markSet('insert','insert-1c')
$Tk::mouseMoved
= 1 if ($x != $Tk::x
|| $y != $Tk::y
);
$w->scan('dragto',$x,$y) if ($Tk::mouseMoved
);
Tk
::catch
{ $w->insert($Ev->xy,$w->SelectionGet) }
Tk
::catch
{ $w->Insert($w->SelectionGet) }
my $sel = Tk
::catch
{ $w->tag('nextrange','sel','1.0','end') };
$w->delete('sel.first','sel.last');
if ($w->compare('insert','!=','1.0'))
my $sel = Tk
::catch
{ $w->tag('nextrange','sel','1.0','end') };
$w->delete('sel.first','sel.last')
# This procedure is invoked to handle button-1 presses in text
# widgets. It moves the insertion cursor, sets the selection anchor,
# and claims the input focus.
# w - The text window in which the button was pressed.
# x - The x-coordinate of the button press.
# y - The x-coordinate of the button press.
$Tk::selectMode
= 'char';
$w->SetCursor('@'.$x.','.$y);
$w->markSet('anchor','insert');
$w->focus() if ($w->cget('-state') eq 'normal');
return unless defined $Tk::mouseMoved
;
# This procedure is invoked to extend the selection, typically when
# dragging it with the mouse. Depending on the selection mode (character,
# word, line) it selects in different-sized units. This procedure
# ignores mouse motions initially until the mouse has moved from
# one character to another or until there have been multiple clicks.
# w - The text window in which the button was pressed.
# index - Index of character at which the mouse button was pressed.
my ($w, $index, $mode)= @_;
$Tk::selectMode
= $mode if defined ($mode);
my $cur = $w->index($index);
my $anchor = Tk
::catch
{ $w->index('anchor') };
$w->markSet('anchor',$anchor = $cur);
elsif ($w->compare($cur,'!=',$anchor))
$Tk::selectMode
= 'char' unless (defined $Tk::selectMode
);
if ($w->compare($cur,'<','anchor'))
if ($w->compare($cur,'<','anchor'))
$first = $w->index("$cur wordstart");
$last = $w->index('anchor - 1c wordend')
$first = $w->index('anchor wordstart');
$last = $w->index("$cur wordend")
if ($w->compare($cur,'<','anchor'))
$first = $w->index("$cur linestart");
$last = $w->index('anchor - 1c lineend + 1c')
$first = $w->index('anchor linestart');
$last = $w->index("$cur lineend + 1c")
if ($Tk::mouseMoved
|| $Tk::selectMode
ne 'char')
$w->tagRemove('sel','1.0',$first);
$w->tagAdd('sel',$first,$last);
$w->tagRemove('sel',$last,'end');
# This procedure is invoked when the mouse leaves a text window
# with button 1 down. It scrolls the window up, down, left, or right,
# depending on where the mouse is (this information was saved in
# tkPriv(x) and tkPriv(y)), and reschedules itself as an 'after'
# command so that the window continues to scroll until the mouse
# moves back into the window or the mouse button is released.
if ($Tk::y
>= $w->height)
$w->yview('scroll',2,'units')
$w->yview('scroll',-2,'units')
elsif ($Tk::x
>= $w->width)
$w->xview('scroll',2,'units')
$w->xview('scroll',-2,'units')
$w->SelectTo('@' . $Tk::x
. ','. $Tk::y
);
$w->RepeatId($w->after(50,['AutoScan',$w]));
# Move the insertion cursor to a given position in a text. Also
# clears the selection, if there is one in the text, and makes sure
# that the insertion cursor is visible.
# pos - The desired new position for the cursor in the window.
$pos = 'end - 1 chars' if $w->compare($pos,'==','end');
$w->markSet('insert',$pos);
# This procedure is invoked when stroking out selections using the
# keyboard. It moves the cursor to a new position, then extends
# the selection to that position.
# new - A new position for the insertion cursor (the cursor has not
# actually been moved to this position yet).
if (!defined $w->tag('ranges','sel'))
$w->markSet('anchor','insert');
if ($w->compare($new,'<','insert'))
$w->tagAdd('sel',$new,'insert')
$w->tagAdd('sel','insert',$new)
if ($w->compare($new,'<','anchor'))
$w->tagRemove('sel','1.0',$first);
$w->tagAdd('sel',$first,$last);
$w->tagRemove('sel',$last,'end')
$w->markSet('insert',$new);
# Set the selection anchor to whichever end is farthest from the
# index argument. One special trick: if the selection has two or
# fewer characters, just leave the anchor where it is. In this
# case it does not matter which point gets chosen for the anchor,
# and for the things like Shift-Left and Shift-Right this produces
# better behavior when the cursor moves back and forth across the
# index - Position at which mouse button was pressed, which determines
# which end of selection should be used as anchor point.
if (!defined $w->tag('ranges','sel'))
$w->markSet('anchor',$index);
my $a = $w->index($index);
my $b = $w->index('sel.first');
my $c = $w->index('sel.last');
if ($w->compare($a,'<',$b))
$w->markSet('anchor','sel.last');
if ($w->compare($a,'>',$c))
$w->markSet('anchor','sel.first');
my ($lineA,$chA) = split(/\./,$a);
my ($lineB,$chB) = split(/\./,$b);
my ($lineC,$chC) = split(/\./,$c);
my $total = length($w->get($b,$c));
if (length($w->get($b,$a)) < $total/2)
$w->markSet('anchor','sel.last')
$w->markSet('anchor','sel.first')
if ($lineA-$lineB < $lineC-$lineA)
$w->markSet('anchor','sel.last')
$w->markSet('anchor','sel.first')
########################################################################
my @markNames_list = $w->markNames;
foreach my $mark (@markNames_list)
{ if ($markname eq $mark) {$mark_exists=1;last;} }
########################################################################
$w->{'OVERSTRIKE_MODE'} =0 unless exists($w->{'OVERSTRIKE_MODE'});
$w->{'OVERSTRIKE_MODE'}=$mode if (@_ > 1);
return $w->{'OVERSTRIKE_MODE'};
########################################################################
# pressed the <Insert> key, just above 'Del' key.
# this toggles between insert mode and overstrike mode.
$w->OverstrikeMode(!$w->OverstrikeMode);
########################################################################
my $current=$w->get('insert');
$w->delete('insert') unless($current eq "\n");
########################################################################
my ($w,$line_number) = @_;
$line_number=~ s/^\s+|\s+$//g;
return if $line_number =~ m/\D/;
my ($last_line,$junk) = split(/\./, $w->index('end'));
if ($line_number > $last_line) {$line_number = $last_line; }
$w->{'LAST_GOTO_LINE'} = $line_number;
$w->markSet('insert', $line_number.'.0');
########################################################################
my $popup = $w->{'GOTO_LINE_NUMBER_POPUP'};
unless (defined($w->{'LAST_GOTO_LINE'}))
my ($line,$col) = split(/\./, $w->index('insert'));
$w->{'LAST_GOTO_LINE'} = $line;
## if anything is selected when bring up the pop-up, put it in entry window.
eval { $selected = $w->SelectionGet(-selection
=> "PRIMARY"); };
if (defined($selected) and length($selected))
unless ($selected =~ /\D/)
$w->{'LAST_GOTO_LINE'} = $selected;
$popup = $w->DialogBox(-buttons
=> [qw
[Ok Cancel
]],-title
=> "Goto Line Number", -popover
=> $w,
-command
=> sub { $w->GotoLineNumber($w->{'LAST_GOTO_LINE'}) if $_[0] eq 'Ok'});
$w->{'GOTO_LINE_NUMBER_POPUP'}=$popup;
$popup->resizable('no','no');
my $frame = $popup->Frame->pack(-fill
=> 'x');
$frame->Label(text
=>'Enter line number: ')->pack(-side
=> 'left');
my $entry = $frame->Entry(-background
=>'white',width
=>25,
-textvariable
=> \
$w->{'LAST_GOTO_LINE'})->pack(-side
=>'left',-fill
=> 'x');
$popup->Advertise(entry
=> $entry);
$popup->Subwidget('entry')->focus;
########################################################################
shift->GetTextTaggedWith('sel');
shift->DeleteTextTaggedWith('sel');
my @ranges = $w->tagRanges($tag);
my $range_total = @ranges;
# if nothing selected, then ignore
if ($range_total == 0) {return $return_text;}
# for every range-pair, get selected text
my $first = shift(@ranges);
my $last = shift(@ranges);
my $text = $w->get($first , $last);
{$return_text = $return_text . $text;}
# if there is more tagged text, separate with an end of line character
{$return_text = $return_text . "\n";}
########################################################################
my @ranges = $w->tagRanges($tag);
my $range_total = @ranges;
# if nothing tagged with that tag, then ignore
if ($range_total == 0) {return;}
# insert marks where selections are located
# marks will move with text even as text is inserted and deleted
# in a previous selection.
for (my $i=0; $i<$range_total; $i++)
{ $w->markSet('mark_tag_'.$i => $ranges[$i]); }
# for every selected mark pair, insert new text and delete old text
for (my $i=0; $i<$range_total; $i=$i+2)
my $first = $w->index('mark_tag_'.$i);
my $last = $w->index('mark_tag_'.($i+1));
my $text = $w->delete($first , $last);
for (my $i=0; $i<$range_total; $i++)
{ $w->markUnset('mark_tag_'.$i); }
########################################################################
my ($w,$mode, $case, $pattern ) = @_;
### 'sel' tags accumulate, need to remove any previous existing
while(defined($end_index))
$start_index = $w->search(
-count
=> \
$match_length,
$start_index = $w->search(
-count
=> \
$match_length,
unless(defined($start_index) && $start_index) {last;}
my ($line,$col) = split(/\./, $start_index);
$col = $col + $match_length;
$end_index = $line.'.'.$col;
$w->tagAdd('sel', $start_index, $end_index);
########################################################################
# get current selected text and search for the next occurrence
eval {$selected = $w->SelectionGet(-selection
=> "PRIMARY"); };
return unless (defined($selected) and length($selected));
$w->FindNext('-forward', '-exact', '-case', $selected);
########################################################################
# get current selected text and search for the previous occurrence
sub FindSelectionPrevious
eval {$selected = $w->SelectionGet(-selection
=> "PRIMARY"); };
return unless (defined($selected) and length($selected));
$w->FindNext('-backward', '-exact', '-case', $selected);
########################################################################
my ($w,$direction, $mode, $case, $pattern ) = @_;
## if searching forward, start search at end of selected block
## if backward, start search from start of selected block.
## dont want search to find currently selected text.
## tag 'sel' may not be defined, use eval loop to trap error
if ($direction eq '-forward')
$w->markSet('insert', 'sel.last');
$w->markSet('current', 'sel.last');
$w->markSet('insert', 'sel.first');
$w->markSet('current', 'sel.first');
my $saved_index=$w->index('insert');
# remove any previous existing tags
$start_index = $w->search(
-count
=> \
$match_length,
$start_index = $w->search(
-count
=> \
$match_length,
unless(defined($start_index)) { return 0; }
if(length($start_index) == 0) { return 0; }
my ($line,$col) = split(/\./, $start_index);
$col = $col + $match_length;
my $end_index = $line.'.'.$col;
$w->tagAdd('sel', $start_index, $end_index);
if ($direction eq '-forward')
$w->markSet('insert', $end_index);
$w->markSet('current', $end_index);
$w->markSet('insert', $start_index);
$w->markSet('current', $start_index);
my $compared_index = $w->index('insert');
if ($compared_index eq $saved_index)
########################################################################
my ($w,$mode, $case, $find, $replace ) = @_;
$w->markSet('insert', '1.0');
while($w->FindNext('-forward', $mode, $case, $find))
$w->ReplaceSelectionsWith($replace);
########################################################################
sub ReplaceSelectionsWith
my @ranges = $w->tagRanges('sel');
my $range_total = @ranges;
# if nothing selected, then ignore
if ($range_total == 0) {return};
# insert marks where selections are located
# marks will move with text even as text is inserted and deleted
# in a previous selection.
for (my $i=0; $i<$range_total; $i++)
{$w->markSet('mark_sel_'.$i => $ranges[$i]); }
# for every selected mark pair, insert new text and delete old text
for (my $i=0; $i<$range_total; $i=$i+2)
$first = $w->index('mark_sel_'.$i);
$last = $w->index('mark_sel_'.($i+1));
##########################################################################
# eventually, want to be able to get selected text,
# support regular expression matching, determine replace_text
# $replace_text = $selected_text=~m/$new_text/ (or whatever would work)
# will have to pass in mode and case flags.
# this would allow a regular expression search and replace to be performed
# example, look for "line (\d+):" and replace with "$1 >" or similar
##########################################################################
$w->insert($last, $new_text);
$w->delete($first, $last);
############################################################
# set the insert cursor to the end of the last insertion mark
$w->markSet('insert',$w->index('mark_sel_'.($range_total-1)));
for (my $i=0; $i<$range_total; $i++)
{ $w->markUnset('mark_sel_'.$i); }
########################################################################
$w->findandreplacepopup(0);
########################################################################
$w->findandreplacepopup(1);
########################################################################
{ $pop->title("Find and/or Replace"); }
my $frame = $pop->Frame->pack(-anchor
=>'nw');
$frame->Label(text
=>"Direction:")
->grid(-row
=> 1, -column
=>1, -padx
=> 20, -sticky
=> 'nw');
my $direction = '-forward';
text
=> '-forward',value
=> '-forward' )
->grid(-row
=> 2, -column
=>1, -padx
=> 20, -sticky
=> 'nw');
text
=> '-backward',value
=> '-backward' )
->grid(-row
=> 3, -column
=>1, -padx
=> 20, -sticky
=> 'nw');
$frame->Label(text
=>"Mode:")
->grid(-row
=> 1, -column
=>2, -padx
=> 20, -sticky
=> 'nw');
variable
=> \
$mode, text
=> '-exact',value
=> '-exact' )
->grid(-row
=> 2, -column
=>2, -padx
=> 20, -sticky
=> 'nw');
variable
=> \
$mode, text
=> '-regexp',value
=> '-regexp' )
->grid(-row
=> 3, -column
=>2, -padx
=> 20, -sticky
=> 'nw');
$frame->Label(text
=>"Case:")
->grid(-row
=> 1, -column
=>3, -padx
=> 20, -sticky
=> 'nw');
variable
=> \
$case, text
=> '-case',value
=> '-case' )
->grid(-row
=> 2, -column
=>3, -padx
=> 20, -sticky
=> 'nw');
variable
=> \
$case, text
=> '-nocase',value
=> '-nocase' )
->grid(-row
=> 3, -column
=>3, -padx
=> 20, -sticky
=> 'nw');
######################################################
my $find_entry = $pop->Entry(width
=>25);
my $button_find = $pop->Button(text
=>'Find',
command
=> sub {$w->FindNext ($direction,$mode,$case,$find_entry->get()),} )
$find_entry -> pack(-anchor
=>'nw', '-expand' => 'yes' , -fill
=> 'x'); # autosizing
###### if any $w text is selected, put it in the find entry
###### could be more than one text block selected, get first selection
my @ranges = $w->tagRanges('sel');
my $first = shift(@ranges);
my $last = shift(@ranges);
my ($first_line, $first_col) = split(/\./,$first);
my ($last_line, $last_col) = split(/\./,$last);
unless($first_line == $last_line)
{$last = $first. ' lineend';}
$find_entry->insert('insert', $w->get($first , $last));
eval {$selected=$w->SelectionGet(-selection
=> "PRIMARY"); };
elsif (defined($selected))
{$find_entry->insert('insert', $selected);}
my ($replace_entry,$button_replace,$button_replace_all);
######################################################
$replace_entry = $pop->Entry(width
=>25);
######################################################
$button_replace = $pop->Button(text
=>'Replace',
command
=> sub {$w->ReplaceSelectionsWith($replace_entry->get());} )
$replace_entry -> pack(-anchor
=>'nw', '-expand' => 'yes' , -fill
=> 'x');
######################################################
$pop->Label(text
=>" ")->pack();
######################################################
$button_replace_all = $pop->Button(text
=>'Replace All',
command
=> sub {$w->FindAndReplaceAll
($mode,$case,$find_entry->get(),$replace_entry->get());} )
my $button_find_all = $pop->Button(text
=>'Find All',
command
=> sub {$w->FindAll($mode,$case,$find_entry->get());} )
my $button_cancel = $pop->Button(text
=>'Cancel',
command
=> sub {$pop->destroy()} )
$pop->resizable('yes','no');
# paste clipboard into current location
Tk
::catch
{ $w->Insert($w->clipboardGet) };
########################################################################
# Insert a string into a text at the point of the insertion cursor.
# If there is a selection in the text, and it covers the point of the
# insertion cursor, then delete the selection before inserting.
# w - The text window in which to insert the string
# string - The string to insert (usually just a single character)
return unless (defined $string && $string ne '');
#figure out if cursor is inside a selection
my @ranges = $w->tagRanges('sel');
my ($first,$last) = splice(@ranges,0,2);
if ($w->compare($first,'<=','insert') && $w->compare($last,'>=','insert'))
$w->ReplaceSelectionsWith($string);
# paste it at the current cursor location
$w->insert('insert',$string);
# Returns the index of the character one line above or below the
# insertion cursor. There are two tricky things here. First,
# we want to maintain the original column across repeated operations,
# even though some lines that will get passed through do not have
# enough characters to cover the original column. Second, do not
# try to scroll past the beginning or end of the text.
# w - The text window in which the cursor is to move.
# n - The number of lines to move: -1 for up one line,
my $i = $w->index('insert');
my ($line,$char) = split(/\./,$i);
if (!defined($Tk::prevPos
) || $Tk::prevPos
ne $i)
my $new = $w->index($line+$n . '.' . $Tk::char
);
if ($w->compare($new,'==','end') || $w->compare($new,'==','insert linestart'))
my $i = $w->index('insert');
my ($line,$char) = split(/\./,$i);
my $string = $w->get($line.'.0', $i);
$string = expand
($string);
$string = $w->get($line.'.0', $line.'.0 lineend');
$string = expand
($string);
$string = substr($string, 0, $char);
$string = unexpand
($string);
my $new = $w->index($line . '.' . $char);
if ($w->compare($new,'==','end') || $w->compare($new,'==','insert linestart'))
# Returns the index of the beginning of the paragraph just before a given
# position in the text (the beginning of a paragraph is the first non-blank
# character after a blank line).
# w - The text window in which the cursor is to move.
# pos - Position at which to start search.
$pos = $w->index("$pos linestart");
if ($w->get("$pos - 1 line") eq "\n" && $w->get($pos) ne "\n" || $pos eq '1.0' )
my $string = $w->get($pos,"$pos lineend");
$pos = $w->index("$pos + $off chars")
if ($w->compare($pos,'!=','insert') || $pos eq '1.0')
$pos = $w->index("$pos - 1 line")
# Returns the index of the beginning of the paragraph just after a given
# position in the text (the beginning of a paragraph is the first non-blank
# character after a blank line).
# w - The text window in which the cursor is to move.
# start - Position at which to start search.
my $pos = $w->index("$start linestart + 1 line");
while ($w->get($pos) ne "\n")
if ($w->compare($pos,'==','end'))
return $w->index('end - 1c');
$pos = $w->index("$pos + 1 line")
while ($w->get($pos) eq "\n" )
$pos = $w->index("$pos + 1 line");
if ($w->compare($pos,'==','end'))
return $w->index('end - 1c');
my $string = $w->get($pos,"$pos lineend");
return $w->index("$pos + $off chars");
# This is a utility procedure used in bindings for moving up and down
# pages and possibly extending the selection along the way. It scrolls
# the view in the widget by the number of pages, and it returns the
# index of the character that is at the same position in the new view
# as the insertion cursor used to be in the old view.
# w - The text window in which the cursor is to move.
# count - Number of pages forward to scroll; may be negative
my @bbox = $w->bbox('insert');
$w->yview('scroll',$count,'pages');
return $w->index('@' . int($w->height/2) . ',' . 0);
my $x = int($bbox[0]+$bbox[2]/2);
my $y = int($bbox[1]+$bbox[3]/2);
return $w->index('@' . $x . ',' . $y);
$w->insert('end',shift) while (@_);
return $w->get('1.0','end');
$pos = $w->index("$pos + 1 char") if ($w->compare($pos,'!=',"$pos lineend"));
return if ($w->compare("$pos - 1 char",'==','1.0'));
my $new = $w->get("$pos - 1 char").$w->get("$pos - 2 char");
$w->delete("$pos - 2 char",$pos);
$w->insert('insert',$new);
Carp
::confess
('No args') unless (ref $w and defined $name);
$w->{_Tags_
} = {} unless (exists $w->{_Tags_
});
unless (exists $w->{_Tags_
}{$name})
$w->{_Tags_
}{$name} = 'Tk::Text::Tag'->new($w,$name);
$w->{_Tags_
}{$name}->configure(@_) if (@_);
return $w->{_Tags_
}{$name};
foreach $name ($w->tagNames(@_))
push(@result,$w->Tag($name));
# Find out whether 'end' is displayed at the moment
# Retrieve the position of the bottom of the window as
# a fraction of the entire contents of the Text widget
my $yview = ($w->yview)[1];
# If $yview is 1.0 this means that 'end' is visible in the window
$update = 1 if $yview == 1.0;
# Loop over all input strings
# Move the window to see the end of the text if required
$w->see('end') if $update;
$w->PRINT(sprintf(shift,@_));
my ($line,$col) = split(/\./,$w->index('insert'));
$w->messageBox(-type
=> 'Ok', -title
=> "What Line Number",
-message
=> "The cursor is on line $line (column is $col)");
return qw
[~File
~Edit
~Search
~View
];
['command'=>'~Find', -command
=> [$w => 'FindPopUp']],
['command'=>'Find ~Next', -command
=> [$w => 'FindSelectionNext']],
['command'=>'Find ~Previous', -command
=> [$w => 'FindSelectionPrevious']],
['command'=>'~Replace', -command
=> [$w => 'FindAndReplacePopUp']]
foreach my $op ($w->clipEvents)
push(@items,['command' => "~$op", -command
=> [ $w => "clipboard$op"]]);
['command'=>'Select All', -command
=> [$w => 'selectAll']],
['command'=>'Unselect All', -command
=> [$w => 'unselectAll']],
tie
$v,'Tk::Configure',$w,'-wrap';
['command'=>'Goto ~Line...', -command
=> [$w => 'GotoLineNumberPopUp']],
['command'=>'~Which Line?', -command
=> [$w => 'WhatLineNumberPopUp']],
['cascade'=> 'Wrap', -tearoff
=> 0, -menuitems
=> [
[radiobutton
=> 'Word', -variable
=> \
$v, -value
=> 'word'],
[radiobutton
=> 'Character', -variable
=> \
$v, -value
=> 'char'],
[radiobutton
=> 'None', -variable
=> \
$v, -value
=> 'none'],
########################################################################
$w->Column_Copy_or_Cut(0);
$w->Column_Copy_or_Cut(1);
########################################################################
my @ranges = $w->tagRanges('sel');
my $range_total = @ranges;
# this only makes sense if there is one selected block
my $selection_start_index = shift(@ranges);
my $selection_end_index = shift(@ranges);
my ($start_line, $start_column) = split(/\./, $selection_start_index);
my ($end_line, $end_column) = split(/\./, $selection_end_index);
# correct indices for tabs
$string = $w->get($start_line.'.0', $start_line.'.0 lineend');
$string = substr($string, 0, $start_column);
$string = expand
($string);
my $tab_start_column = length($string);
$string = $w->get($end_line.'.0', $end_line.'.0 lineend');
$string = substr($string, 0, $end_column);
$string = expand
($string);
my $tab_end_column = length($string);
my $length = $tab_end_column - $tab_start_column;
$selection_start_index = $start_line . '.' . $tab_start_column;
$selection_end_index = $end_line . '.' . $tab_end_column;
my ($clipstring, $startstring, $endstring);
my $padded_string = ' 'x
$tab_end_column;
for(my $line = $start_line; $line <= $end_line; $line++)
$string = $w->get($line.'.0', $line.'.0 lineend');
$string = expand
($string) . $padded_string;
$clipstring = substr($string, $tab_start_column, $length);
#$clipstring = unexpand($clipstring);
$w->clipboardAppend($clipstring."\n");
$startstring = substr($string, 0, $tab_start_column);
$startstring = unexpand
($startstring);
$start_column = length($startstring);
$endstring = substr($string, 0, $tab_end_column );
$endstring = unexpand
($endstring);
$end_column = length($endstring);
$w->delete($line.'.'.$start_column, $line.'.'.$end_column);
########################################################################
my @ranges = $w->tagRanges('sel');
my $range_total = @ranges;
warn " there cannot be any selections during clipboardColumnPaste. \n";
$clipboard_text = $w->SelectionGet(-selection
=> "CLIPBOARD");
return unless (defined($clipboard_text));
return unless (length($clipboard_text));
my $current_index = $w->index('insert');
my ($current_line, $current_column) = split(/\./,$current_index);
$string = $w->get($current_line.'.0', $current_line.'.'.$current_column);
$string = expand
($string);
$current_column = length($string);
my @clipboard_lines = split(/\n/,$clipboard_text);
my ($delete_start_column, $delete_end_column, $insert_column_index);
foreach my $line (@clipboard_lines)
#figure out start and end indexes to delete, compensating for tabs.
$string = $w->get($current_line.'.0', $current_line.'.0 lineend');
$string = expand
($string);
$string = substr($string, 0, $current_column);
$string = unexpand
($string);
$delete_start_column = length($string);
$string = $w->get($current_line.'.0', $current_line.'.0 lineend');
$string = expand
($string);
$string = substr($string, 0, $current_column + length($line));
chomp($string); # dont delete a "\n" on end of line.
$string = unexpand
($string);
$delete_end_column = length($string);
$current_line.'.'.$delete_start_column ,
$current_line.'.'.$delete_end_column
$string = $w->get($current_line.'.0', $current_line.'.0 lineend');
$string = expand
($string);
$string = substr($string, 0, $current_column);
$string = unexpand
($string);
$insert_column_index = length($string);
$w->insert($current_line.'.'.$insert_column_index, unexpand
($line));
carp
((caller(0))[3]." is deprecated") if $^W
;