File Coverage

blib/lib/Tk/Text/SuperText.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Tk::Text::SuperText;
2              
3 2     2   35000 use Exporter ();
  2         4  
  2         44  
4 2     2   334 use Tk qw(800 Ev);
  0            
  0            
5             use Tk::Text;
6             use Tk::Derived;
7              
8              
9             #+20010117 JWT TextANSIColor support
10             my $ansicolor = 0;
11             eval 'use Term::ANSIColor; 1' or $ansicolor = -1;
12             #+
13              
14             use Carp;
15             use strict;
16             use vars qw($VERSION @ISA @EXPORT);
17              
18             @EXPORT = qw(
19             mouseSetInsert mouseSelect mouseSelectWord mouseSelectLine mouseSelectAdd mouseSelectChar
20             mouseSelectAddWord mouseSelectAddLine mouseSelectAutoScan mouseSelectAutoScanStop
21             mouseMoveInsert mouseRectSelection mouseMovePageTo mouseMovePage mousePasteSelection
22             moveLeft selectLeft selectRectLeft moveLeftWord selectLeftWord
23             moveRight selectRight selectRectRight moveRightWord selectRightWord moveUp selectUp
24             selectRectUp moveUpParagraph selectUpParagraph moveDown selectDown selectRectDown
25             moveDownParagraph selectDownParagraph moveLineStart selectToLineStart moveTextStart
26             selectToTextStart moveLineEnd selectToLineEnd moveTextEnd selectToTextEnd movePageUp
27             selectToPageUp movePageLeft movePageDown selectToPageDown movePageRight
28             setSelectionMark selectToMark selectAll selectionShiftLeft selectionShiftLeftTab
29             selectionShiftRight selectionShiftRightTab ins enter autoIndentEnter
30             noAutoIndentEnter del backSpace deleteToWordStart deleteToWordEnd deleteToLineStart
31             deleteToLineEnd deleteWord deleteLine insertControlCode focusNext focusPrev
32             flashMatchingChar removeMatch findMatchingChar jumpToMatchingChar escape tab
33             leftTab copy cut paste inlinePaste undo redo destroy keyPress menuSelect noOP
34             );
35              
36             $VERSION = '0.11';
37             @ISA = qw(Tk::Derived Tk::Text Exporter);
38              
39             use base qw(Tk::Text);
40              
41             Construct Tk::Widget 'SuperText';
42              
43             my (%fgcolors, %bgcolors, $clear, $code_bold, $code_uline, @colors);
44              
45             #+20010117 JWT TextANSIColor support
46             unless ($ansicolor == -1)
47             {
48             $clear = color('clear'); # Code to reset control codes
49             $code_bold = color('bold');
50             $code_uline= color('underline');
51             @colors = qw/black red green yellow blue magenta cyan white/;
52             for (@colors)
53             {
54             my $fg = color($_);
55             my $bg = color("on_$_");
56            
57             $fgcolors{$fg} = "ANSIfg$_";
58             $bgcolors{$bg} = "ANSIbg$_";
59             }
60             }
61             #+
62              
63             # returns an hash with the default events and key binds
64             sub DefaultEvents {
65             my (%events);
66            
67             %events = (
68             'MouseSetInsert' => ['<1>'],
69             'MouseSelect' => [''],
70             'MouseSelectWord' => [''],
71             'MouseSelectLine' => [''],
72             'MouseSelectChar' => [''], #ADDED 1999/07 by JWT TO CAUSE RIGHT BUTTON TO EXTEND SELECT!
73             'MouseSelectAdd' => [''],
74             'MouseSelectAddWord' => [''],
75             'MouseSelectAddLine' => [''],
76             'MouseSelectAutoScan' => [''],
77             'MouseSelectAutoScanStop' => ['',''],
78             'MouseMoveInsert' => [''],
79             'MouseRectSelection' => [''],
80             'MouseMovePageTo' => ['<2>'],
81             'MouseMovePage' => [''],
82             'MousePasteSelection' => [''],
83            
84             'MoveLeft' => [''],
85             'SelectLeft' => [''],
86             'SelectRectLeft' => [''],
87             'MoveLeftWord' => [''],
88             'SelectLeftWord' => [''],
89             'MoveRight' => [''],
90             'SelectRight' => [''],
91             'SelectRectRight' => [''],
92             'MoveRightWord' => [''],
93             'SelectRightWord' => [''],
94             'MoveUp' => [''],
95             'SelectUp' => [''],
96             'SelectRectUp' => [''],
97             'MoveUpParagraph' => [''],
98             'SelectUpParagraph' => [''],
99             'MoveDown' => [''],
100             'SelectDown' => [''],
101             'SelectRectDown' => [''],
102             'MoveDownParagraph' => [''],
103             'SelectDownParagraph' => [''],
104             'MoveLineStart' => [''],
105             'SelectToLineStart' => [''],
106             'MoveTextStart' => [''],
107             'SelectToTextStart' => [''],
108             'MoveLineEnd' => [''],
109             'SelectToLineEnd' => [''],
110             'MoveTextEnd' => [''],
111             'SelectToTextEnd' => [''],
112             'MovePageUp' => [''],
113             'SelectToPageUp' => [''],
114             'MovePageLeft' => [''],
115             'MovePageDown' => [''],
116             'SelectToPageDown' => [''],
117             'MovePageRight' => [''],
118             'SetSelectionMark' => ['','
119             'SelectToMark' => ['',''],
120             #=20010117 JWT selection extensions
121             # 'SelectAll' => [''],
122             'SelectAll' => ['','',''],
123             #=
124             'SelectionShiftLeft' => [''],
125             'SelectionShiftLeftTab' => [''],
126             'SelectionShiftRight' => [''],
127             'SelectionShiftRightTab' => [''],
128            
129             'Ins' => [''],
130             'Enter' => [''],
131             'AutoIndentEnter' => [''],
132             'NoAutoIndentEnter' => [''],
133             'Del' => [''],
134             #-1999/07/11 alexiob@dlevel.com - Fixed win32 BackSpace bug thanks to Jim Turner
135             # 'BackSpace' => [''],
136             'DeleteToWordStart' => [''],
137             'DeleteToWordEnd' => [''],
138             'DeleteToLineStart' => [''],
139             'DeleteToLineEnd' => [''],
140             'DeleteWord' => [''],
141             'DeleteLine' => [''],
142            
143             'InsertControlCode' => [''],
144            
145             'FocusNext' => [''],
146             'FocusPrev' => [''],
147            
148             'FlashMatchingChar' => [''],
149             'RemoveMatch' => [''],
150             'FindMatchingChar' => [''],
151             'JumpToMatchingChar' => [''],
152             #+20010117 JWT fix
153             'JumpToMatchingChar' => [''],
154             #+
155             'Escape' => [''],
156             'Tab' => [''],
157             'LeftTab' => [''],
158             'Copy' => [''],
159             'Cut' => [''],
160             'Paste' => [''],
161             'InlinePaste' => [''],
162             'Undo' => [''],
163             'Redo' => [''],
164            
165             'Destroy' => [''],
166              
167             'KeyPress' => [''],
168             'MenuSelect' => [''],
169            
170             'NoOP' => ['']
171             );
172            
173             return \%events;
174             } # /DefaultEvents
175              
176             sub ClassInit
177             {
178             my ($class,$w) = @_;
179            
180             $class->SUPER::ClassInit($w);
181            
182             # reset default Tk::Text binds
183             $class->RemoveTextBinds($w);
184            
185             return $class;
186             }
187              
188             sub Populate
189             {
190             #+20010117 JWT TextANSIColor support
191             my ($w,$args) = @_;
192            
193             $w->{ansicolor} = 0;
194             $w->{ansicolor} = delete ($args->{-ansicolor}) if (defined($args->{-ansicolor}));
195             #+
196              
197             $w->SUPER::Populate($args);
198              
199             # and set configuration parameters defaults
200             $w->ConfigSpecs(
201             '-indentmode' => ['PASSIVE','indentMode','IndentMode','auto'],
202             #+20010117 JWT TextANSIColor support
203             '-ansicolor' => ['PASSIVE','ansicolor','ansicolor',undef],
204             #+
205             '-undodepth' => ['PASSIVE','undoDepth','UndoDepth',undef],
206             '-redodepth' => ['PASSIVE','redoDepth','RedoDepth',undef],
207             '-showmatching' => ['PASSIVE','showMatching','ShowMatching',1],
208             '-matchhighlighttime' => ['PASSIVE','matchHighlightTime','MatchHighlightTime',1400],
209             '-matchforeground' => ['METHOD','matchForeground','MatchForeground','white'],
210             '-matchbackground' => ['METHOD','matchBackground','MatchBackground','blue'],
211             '-matchingcouples' => ['METHOD','matchingCouples','MatchingCouples',"//[]{}()<>\\\\''``\"\""],
212             '-insertmode' => ['METHOD','insertMode','InsertMode','insert'],
213             '-foreground' => ['SELF','foreground','Foreground',$w->cget('-foreground')],
214             );
215             # set default key binds and events
216             $w->bindDefault;
217             # set undo block flag
218             $w->{UNDOBLOCK}=0;
219              
220             #+20010117 JWT TextANSIColor support
221             if ($w->{ansicolor})
222             {
223             # Setup tags
224             # colors
225             for (@colors)
226             {
227             $w->tagConfigure("ANSIfg$_", -foreground => $_);
228             $w->tagConfigure("ANSIbg$_", -background => $_);
229             }
230             # Underline
231             $w->tagConfigure("ANSIul", -underline => 1);
232             $w->tagConfigure("ANSIbd", -font => [-weight => "bold" ]);
233             }
234             #+
235             }
236              
237             # callbacks for options management
238              
239             sub matchforeground
240             {
241             my ($w,$val) = @_;
242            
243             if(!defined $val) {return $w->tagConfigure('match','-foreground');}
244             $w->tagConfigure('match','-foreground' => $val);
245             }
246              
247             sub matchbackground
248             {
249             my ($w,$val) = @_;
250            
251             if(!defined $val) {return $w->tagConfigure('match','-background');}
252             $w->tagConfigure('match','-background' => $val);
253             }
254              
255             sub matchingcouples
256             {
257             my ($w,$val) = @_;
258             my ($i,$dir);
259            
260              
261             if(!defined $val) {return $w->{MATCHINGCOUPLES_STRING};}
262             $w->{MATCHINGCOUPLES_STRING}=$val;
263              
264             $w->{MATCHINGCOUPLES}={} unless exists $w->{MATCHINGCOUPLES};
265             for($i=0;$i
266             $dir=($i % 2 ? -1 : 1);
267             if($dir == -1 && (substr($val,$i,1) eq substr($val,$i+$dir,1))) {next;}
268             $w->{MATCHINGCOUPLES}->{substr($val,$i,1)}=[substr($val,$i+$dir,1),$dir];
269             }
270             }
271              
272             sub insertmode
273             {
274             my ($w,$val) = @_;
275            
276             if(!defined $val) {return $w->{INSERTMODE};}
277             $w->{INSERTMODE}=$val;
278             }
279              
280             # insertion and deletion functions intereptors
281              
282             sub insert
283             {
284             my ($w,$index,$str,@tags) = @_;
285             my $s = $w->index($index);
286             my $i;
287              
288             # for line start hack
289             $w->{LINESTART}=0;
290            
291             $w->markSet('undopos' => $s);
292             # insert ascii code
293             if((exists $w->{ASCIICODE}) && $w->{ASCIICODE} == 1) {
294             if(($str ge ' ') && ($str le '?')) {$i=-0x20;}
295             else {$i=0x7f-0x40;}
296             $str=sprintf('%c',ord($str) + $i);
297             $w->{ASCIICODE} = 0;
298             }
299             # manage overwrite mode,NOT optimal for undo,but... hey who uses overwrite mode???
300             if($w->{INSERTMODE} eq 'overwrite') {
301             $w->_BeginUndoBlock;
302             if($w->compare($s,'<',$w->index("$s lineend"))) {$w->delete($s);}
303             }
304              
305             #-20010117 JWT TextANSIColor support
306             # $w->SUPER::insert($s,$str,@tags);
307             #-
308             #+20010117 JWT TextANSIColor support
309             if ($w->{ansicolor})
310             {
311             #$w->SUPER::insert($s,$str,@tags); #JWT:01042001: REPL. W/NEXT LINES FOR TEXTANSICOLOR!
312             my (@userstuff) = ($str,@tags);
313             my ($pos) = $s;
314            
315             # This is the array containing text and tags pairs
316             # We pass this to SUPER::insert
317             # as (POS, string, [tags], string, [tags]....)
318             # insert_array contains string,[tags] pairs
319             my @insert_array = ();
320            
321             # Need to loop over @userstuff
322             # extracting out the text string and any user supplied tags.
323             # note that multiple sets of text strings and tags can be supplied
324             # as arguments to the insert() method, and we have to process
325             # each set in turn.
326             # Use an old-fashioned for since we have to extract two items at
327             # a time
328            
329             for (my $i=0; $i <= $#userstuff; $i += 2)
330             {
331            
332             my $text = $userstuff[$i];
333             my $utags = $userstuff[$i+1];
334            
335             # Store the usertags in an array, expanding the
336             # array ref if required
337             my @taglist = ();
338             if (ref($utags) eq 'ARRAY')
339             {
340             @taglist = @{$utags};
341             }
342             else
343             {
344             @taglist = ($utags);
345             }
346            
347             # Split the string on control codes
348             # returning the codes as well as the strings between
349             # the codes
350             # Note that this pattern also checks for the case when
351             # multiple escape codes are embedded together separated
352             # by semi-colons.
353             my @split = split /(\e\[(?:\d{1,2};?)+m)/, $text;
354             # Array containing the tags to use with the insertion
355             # Note that this routine *always* assumes the colors are reset
356             # after the last insertion. ie it does not allow the colors to be
357             # remembered between calls to insert().
358             my @ansitags = ();
359            
360             # Current text string
361             my $cur_text = undef;
362            
363             # Now loop over the split strings
364             for my $part (@split)
365             {
366            
367             # If we have a plain string, just store it
368             if ($part !~ /^\e/)
369             {
370             $cur_text = $part;
371             }
372             else
373             {
374             # We have an escape sequence
375             # Need to store the current string with required tags
376             # Include the ansi tags and the user-supplied tag list
377             push(@insert_array, $cur_text, [@taglist, @ansitags])
378             if defined $cur_text;
379            
380             # There is no longer a 'current string'
381             $cur_text = undef;
382            
383             # The escape sequence can have semi-colon separated bits
384             # in it. Need to strip off the \e[ and the m. Split on
385             # semi-colon and then reconstruct before comparing
386             # We know it matches \e[....m so use substr
387            
388             # Only bother if we have a semi-colon
389            
390             my @escs = ($part);
391             if ($part =~ /;/)
392             {
393             my $strip = substr($part, 2, length($part) - 3);
394            
395             # Split on ; (overwriting @escs)
396             @escs = split(/;/,$strip);
397            
398             # Now attach the correct escape sequence
399             foreach (@escs) { $_ = "\e[${_}m" }
400             }
401            
402             # Loop over all the escape sequences
403             for my $esc (@escs)
404             {
405            
406             # Check what type of escape
407             if ($esc eq $clear)
408             {
409             # Clear all escape sequences
410             @ansitags = ();
411             }
412             elsif (exists $fgcolors{$esc})
413             {
414             # A foreground color has been specified
415             push(@ansitags, $fgcolors{$esc});
416             }
417             elsif (exists $bgcolors{$esc})
418             {
419             # A background color
420             push(@ansitags, $bgcolors{$esc});
421             }
422             elsif ($esc eq $code_bold)
423             {
424             # Boldify
425             push(@ansitags, "ANSIbd");
426             }
427             elsif ($esc eq $code_uline)
428             {
429             # underline
430             push(@ansitags, "ANSIul");
431             }
432             else
433             {
434             print "Unrecognised control code - ignoring\n";
435             foreach (split //, $esc)
436             {
437             print ord($_) . ": $_\n";
438             }
439             }
440             }
441             }
442             }
443             # If we still have a current string, push that onto the array
444             push(@insert_array, $cur_text, [@taglist, @ansitags])
445             if defined $cur_text;
446             }
447             # Finally, insert the string
448             $w->SUPER::insert($pos, @insert_array)
449             if $#insert_array > 0;
450             }
451             else
452             {
453             $w->SUPER::insert($s,$str,@tags); #JWT:01042001: REPL. W/NEXT LINES FOR TEXTANSICOLOR!
454             }
455             #+
456              
457             # match coupled chars
458             if((!defined $w->tag('ranges','sel')) && $w->cget('-showmatching') == 1) {
459             if(exists $w->{MATCHINGCOUPLES}->{$str}) {
460             # calculate visible zone and search only in this one
461             my ($l,$c) = split('\.',$w->index('end'));
462             my ($slimit,$elimit) = $w->yview;
463            
464             $slimit=int($l*$slimit)+1;
465             $slimit="$slimit.0";
466             $elimit=int($l*$elimit);
467             $elimit="$elimit.0";
468             my $i=$w->_FindMatchingChar($str,$s,$slimit,$elimit);
469             if(defined $i) {
470             my $sel = Tk::catch {$w->tag('nextrange','match','1.0','end');};
471             if(defined $sel) {$w->tag('remove','match','match.first');}
472             $w->tag('add','match',$i,$w->index("$i + 1 c"));
473             my $t=$w->cget('-matchhighlighttime');
474             if($t != 0) {$w->after($t,[\&removeMatch,$w,$i]);}
475             }
476             }
477             }
478              
479             # combine 'trivial ' inserts into clumps
480             if((length($str) == 1) && ($str ne "\n")) {
481             my $t = $w->_TopUndo;
482             if($t && $t->[0] =~ /delete$/ && $w->compare($t->[2],'==',$s)) {
483             $t->[2] = $w->index('undopos');
484             return;
485             }
486             }
487             $w->_AddUndo('delete',$s,$w->index('undopos'));
488             # for undo blocks
489             if($w->{INSERTMODE} eq 'overwrite') {
490             $w->_EndUndoBlock;
491             }
492             }
493              
494             sub delete
495             {
496             my $w = shift;
497             my $str = $w->get(@_);
498             my $s = $w->index(shift);
499            
500             $w->{LINESTART}=0;
501             $w->SUPER::delete($s,@_);
502             $w->_AddUndo('insert',$s,$str);
503             }
504              
505              
506             # used for removing match tag after some time
507             # here so Tk::After doesn't complain
508             sub removeMatch
509             {
510             my ($w,$i) = @_;
511            
512             if(defined $i) {$w->tag('remove','match',$i);}
513             else {$w->tag('remove','match','1.0','end');}
514             }
515              
516              
517             #+20010117 JWT TextANSIColor support
518             #sub get
519             #{
520             # my $self= shift; # The widget reference
521             # return $self->SUPER::get(@_);
522             #}
523              
524             sub getansi
525             {
526             my $self= shift; # The widget reference
527             my (@args) = @_;
528             return $self->get(@args) unless ($self->{ansicolor});
529              
530             my $i;
531             my (@xdump);
532             my $tagflag = 0;
533             my $res = '';
534              
535             @xdump = $self->dump(@args);
536             for ($i=0;$i<=$#xdump;$i+=3)
537             {
538             if ($xdump[$i] eq 'tagon')
539             {
540             if ($xdump[$i+1] =~ /^ANSIfg(\w+)/)
541             {
542             $res .= color($1);
543             $tagflag = 1;
544             }
545             elsif ($xdump[$i+1] =~ /^ANSIbg(\w+)/)
546             {
547             $res .= color("on_$1");
548             $tagflag = 1;
549             }
550             elsif ($xdump[$i+1] =~ /^ANSIbd/)
551             {
552             $res .= color('bold');
553             $tagflag = 1;
554             }
555             elsif ($xdump[$i+1] =~ /^ANSIul/)
556             {
557             $res .= color('underline');
558             $tagflag = 1;
559             }
560             #$res .= $xdump[$i+4] if ($xdump[$i+3] eq 'text');
561             }
562             if ($tagflag && $xdump[$i] eq 'tagoff')
563             {
564             $res .= color('reset');
565             $tagflag = 0;
566             }
567             if ($xdump[$i] eq 'text')
568             {
569             $res .= $xdump[$i+1];
570             }
571             };
572             return $res;
573             }
574             #+
575              
576             # clipboard methods that must be overriden for rectangular selections
577              
578             sub deleteSelected
579             {
580             my $w = shift;
581            
582             if(!defined $Tk::selectionType || ($Tk::selectionType eq 'normal')) {
583             $w->SUPER::deleteSelected;
584             } elsif ($Tk::selectionType eq 'rect') {
585             my ($sl,$sc) = split('\.',$w->index('sel.first'));
586             my ($el,$ec) = split('\.',$w->index('sel.last'));
587             my ($i,$x);
588            
589             # delete only text in the rectangular selection range
590             $w->_BeginUndoBlock;
591             for($i=$sl;$i<=$el;$i++) {
592             my ($l,$c) = split('\.',$w->index("$i.end"));
593             # check if selection is too right (??) for this line
594             if($sc > $c) {next;}
595             # and clip selection
596             if($ec <= $c) {$x=$ec;}
597             else { $x=$c;}
598            
599             $w->delete($w->index("$i.$sc"),$w->index("$i.$x"));
600             }
601             $w->_EndUndoBlock;
602             }
603             }
604              
605             sub getSelected
606             {
607             my $w = shift;
608            
609             if(!defined $Tk::selectionType || ($Tk::selectionType eq 'normal')) {
610             return $w->SUPER::getSelected;
611             } elsif ($Tk::selectionType eq 'rect') {
612             my ($sl,$sc) = split('\.',$w->index('sel.first'));
613             my ($el,$ec) = split('\.',$w->index('sel.last'));
614             my ($i,$x);
615             my ($sel,$str);
616            
617             $sel="";
618            
619             # walk throught all the selected lines and add a sel tag
620             for($i=$sl;$i<=$el;$i++) {
621             my ($l,$c) = split('\.',$w->index("$i.end"));
622             # check if selection is too much to the right
623             if($sc > $c) {next;}
624             # or clif if too wide
625             if($ec <= $c) {$x=$ec;}
626             else { $x=$c;}
627             $str=$w->get($w->index("$i.$sc"),$w->index("$i.$x"));
628             # add a new line if not the last line
629             if(substr($str,-1,1) ne "\n") {
630             $str=$str."\n";
631             }
632             $sel=$sel.$str;
633             }
634             return $sel;
635             }
636             }
637              
638             # redefine SetCursor for parentheses highlight
639             sub SetCursor
640             {
641             my $w = shift;
642             my $str;
643            
644             $w->SUPER::SetCursor(@_);
645            
646             if((!defined $w->tag('ranges','sel')) && $w->cget('-showmatching') == 1) {
647             if(exists $w->{MATCHINGCOUPLES}->{$str=$w->get('insert','insert + 1c')}) {
648             # calculate visible zone and search only in this one
649             my ($l,$c) = split('\.',$w->index('end'));
650             my ($slimit,$elimit) = $w->yview;
651            
652             $slimit=int($l*$slimit)+1;
653             $slimit="$slimit.0";
654             $elimit=int($l*$elimit);
655             $elimit="$elimit.0";
656             my $i=$w->_FindMatchingChar($str,'insert',$slimit,$elimit);
657             if(defined $i) {
658             my $sel = Tk::catch {$w->tag('nextrange','match','1.0','end');};
659             if(defined $sel) {$w->tag('remove','match','match.first');}
660             $w->tag('add','match',$i,$w->index("$i + 1c"));
661             my $t=$w->cget('-matchhighlighttime');
662             if($t != 0) {$w->after($t,[\&removeMatch,$w,$i]);}
663             }
664             }
665             }
666             }
667              
668             # redefine Button1for parentheses highlight
669             sub Button1
670             {
671             my $w = shift;
672             my $str;
673            
674             $w->SUPER::Button1(@_);
675            
676             if((!defined $w->tag('ranges','sel')) && $w->cget('-showmatching') == 1) {
677             if(exists $w->{MATCHINGCOUPLES}->{$str=$w->get('insert','insert + 1c')}) {
678             # calculate visible zone and search only in this one
679             my ($l,$c) = split('\.',$w->index('end'));
680             my ($slimit,$elimit) = $w->yview;
681            
682             $slimit=int($l*$slimit)+1;
683             $slimit="$slimit.0";
684             $elimit=int($l*$elimit);
685             $elimit="$elimit.0";
686             my $i=$w->_FindMatchingChar($str,'insert',$slimit,$elimit);
687             if(defined $i) {
688             my $sel = Tk::catch {$w->tag('nextrange','match','1.0','end');};
689             if(defined $sel) {$w->tag('remove','match','match.first');}
690             $w->tag('add','match',$i,$w->index("$i + 1c"));
691             my $t=$w->cget('-matchhighlighttime');
692             if($t != 0) {$w->after($t,[\&removeMatch,$w,$i]);}
693             }
694             }
695             }
696             }
697              
698             # remove default Tk::Text key binds
699             sub RemoveTextBinds
700             {
701             my ($class,$w) = @_;
702             my (@binds) = $w->bind($class);
703            
704             foreach $b (@binds) {
705             #=1999/07/11 alexiob@dlevel.com - Fixed win32 BackSpace bug thanks to Jim Turner
706             # $w->bind($class,$b,"");
707             $w->bind($class,$b,"") unless ($b =~ /Key-BackSpace/);
708             }
709             }
710              
711             # bind default keys with default events
712             sub bindDefault
713             {
714             my $w = shift;
715             my $events = $w->DefaultEvents;
716            
717             foreach my $e (keys %$events) {
718             $w->eventAdd("<<$e>>",@{$$events{$e}});
719             $w->bind($w,"<<$e>>",lcfirst($e));
720             }
721             #+1999/07/11 alexiob@dlevel.com - Fixed win32 BackSpace bug thanks to Jim Turner
722             $w->bind("", sub {Tk->break;});
723             }
724              
725             # delete all event binds,specified event bind
726             sub bindDelete
727             {
728             my ($w,$event,@triggers) = @_;
729            
730             if(!$event) {
731             # delete all events binds
732             my ($e);
733            
734             foreach $e (%{$w->DefaultEvents}) {
735             $w->eventDelete($e);
736             }
737             return;
738             }
739             $w->eventDelete($event,@triggers);
740             }
741              
742             # Key binding Events subs
743              
744             sub _BeginUndoBlock
745             {
746             my $w = shift;
747              
748             $w->_AddUndo('#_BlockEnd_#');
749             }
750              
751             sub _EndUndoBlock
752             {
753             my $w = shift;
754              
755             $w->_AddUndo('#_BlockBegin_#');
756             }
757              
758             # resets undo and redo buffers
759             sub resetUndo
760             {
761             my $w = shift;
762            
763             delete $w->{UNDO};
764             delete $w->{REDO};
765             }
766              
767             # undo last operation
768             sub undo
769             {
770             my ($w) = @_;
771             my $s;
772             my $op;
773             my @args;
774             my $block = 0;
775            
776             if(exists $w->{UNDO}) {
777             if(@{$w->{UNDO}}) {
778             # undo loop
779             while(1) {
780             # retrive undo command
781             my ($op,@args) = Tk::catch{@{pop(@{$w->{UNDO}})};};
782              
783             if($op eq '#_BlockBegin_#') {
784             $w->_AddRedo('#_BlockEnd_#');
785             $block=1;
786             next;
787             } elsif($op eq '#_BlockEnd_#') {
788             $w->_AddRedo('#_BlockBegin_#');
789             return 1;
790             }
791             # convert for redo
792             if($op =~ /insert$/) {
793             # get current insert position
794             $s = $w->index($args[0]);
795             # mark for getting the with of the insertion
796             $w->markSet('redopos' => $s);
797             } elsif ($op =~ /delete$/) {
798             # save text and position
799             my $str = $w->get(@args);
800             $s = $w->index($args[0]);
801            
802             $w->_AddRedo('insert',$s,$str);
803             }
804             # execute undo command
805             $w->$op(@args);
806             $w->SetCursor($args[0]);
807             # insert redo command
808             if($op =~ /insert$/) {
809             $w->_AddRedo('delete',$s,$w->index('redopos'));
810             }
811             if($block == 0) {return 1;}
812             }
813             }
814             }
815             $w->bell;
816             return 0;
817             }
818              
819             # redo last undone operation
820             sub redo
821             {
822             my ($w) = @_;
823             my $block = 0;
824            
825             if(exists $w->{REDO}) {
826             if(@{$w->{REDO}}) {
827             while(1) {
828             my ($op,@args) = Tk::catch{@{pop(@{$w->{REDO}})};};
829              
830             if($op eq '#_BlockBegin_#') {
831             $w->_AddUndo('#_BlockEnd_#');
832             $block=1;
833             next;
834             } elsif($op eq '#_BlockEnd_#') {
835             $w->_AddUndo('#_BlockBegin_#');
836             return 1;
837             }
838             $op =~ s/^SUPER:://;
839             $w->$op(@args);
840             $w->SetCursor($args[0]);
841             if($block == 0) {return 1;}
842             }
843             }
844             }
845             $w->bell;
846             return 0;
847             }
848              
849             # add an undo command to the undo stack
850             sub _AddUndo
851             {
852             my ($w,$op,@args) = @_;
853             my ($usize,$udepth);
854            
855             $w->{UNDO} = [] unless(exists $w->{UNDO});
856             # check for undo depth limit
857             $usize = @{$w->{UNDO}} + 1;
858             $udepth = $w->cget('-undodepth');
859            
860             if(defined $udepth) {
861             if($udepth == 0) {return;}
862             if($usize >= $udepth) {
863             # free oldest undo sequence
864             $udepth=$usize - $udepth + 1;
865             splice(@{$w->{UNDO}},0,$udepth);
866             }
867             }
868             if($op =~ /^#_/) {push(@{$w->{UNDO}},[$op]);}
869             else {push(@{$w->{UNDO}},['SUPER::'.$op,@args]);}
870             }
871              
872             # return the last added undo command
873             sub _TopUndo
874             {
875             my ($w) = @_;
876            
877             return undef unless (exists $w->{UNDO});
878             return $w->{UNDO}[-1];
879             }
880              
881             # add a new redo command to the redo stack
882             sub _AddRedo
883             {
884             my ($w,$op,@args) = @_;
885             my ($rsize,$rdepth);
886            
887             $w->{REDO} = [] unless(exists $w->{REDO});
888            
889             # check for undo depth limit
890             $rsize = @{$w->{REDO}} + 1;
891             $rdepth = $w->cget('-undodepth');
892            
893             if(defined $rdepth) {
894             if($rdepth == 0) {return;}
895             if($rsize >= $rdepth) {
896             # free oldest undo sequence
897             $rdepth=$rsize - $rdepth + 1;
898             splice(@{$w->{REDO}},0,$rdepth);
899             }
900             }
901             if($op =~ /^#_/) {push(@{$w->{REDO}},[$op]);}
902             else {push(@{$w->{REDO}},['SUPER::'.$op,@args]);}
903             }
904              
905             # manage mouse normal and rectangular selections for char,word or line mode
906             # overrides standard Tk::Text->SelectTo method
907             sub SelectTo
908             {
909             my $w = shift;
910             my $index = shift;
911             $Tk::selectMode = shift if (@_);
912             my $cur = $w->index($index);
913             my $anchor = Tk::catch{$w->index('anchor')};
914              
915             # check for mouse movement
916             if(!defined $anchor) {
917             $w->markSet('anchor',$anchor=$cur);
918             $Tk::mouseMoved=0;
919             } elsif($w->compare($cur,"!=",$anchor)) {
920             $Tk::mouseMoved=1;
921             }
922             $Tk::selectMode='char' unless(defined $Tk::selectMode);
923              
924             my $mode = $Tk::selectMode;
925             my ($first,$last);
926              
927             # get new selection limits
928             if($mode eq 'char') {
929             if($w->compare($cur,"<",'anchor')) {
930             $first=$cur;
931             $last='anchor';
932             } else {
933             $first='anchor';
934             $last=$cur;
935             }
936             } elsif($mode eq 'word') {
937             if($w->compare($cur,"<",'anchor')) {
938             $first = $w->index("$cur wordstart");
939             $last = $w->index("anchor - 1c wordend");
940             } else {
941             $first=$w->index("anchor wordstart");
942             $last=$w->index("$cur wordend");
943             }
944             } elsif($mode eq 'line') {
945             if($w->compare($cur,"<",'anchor')) {
946             $first=$w->index("$cur linestart");
947             $last=$w->index("anchor - 1c lineend + 1c");
948             } else {
949             $first=$w->index("anchor linestart");
950             $last=$w->index("$cur lineend + 1c");
951             }
952             }
953             # update selection
954             if($Tk::mouseMoved || $Tk::selectMode ne 'char') {
955             if((!defined $Tk::selectionType) || ($Tk::selectionType eq 'normal')) {
956             # simple normal selection
957             $w->tag('remove','sel','1.0',$first);
958             $w->tag('add','sel',$first,$last);
959             $w->tag('remove','sel',$last,'end');
960             $w->idletasks;
961             } elsif($Tk::selectionType eq 'rect') {
962             my ($sl,$sc) = split('\.',$w->index($first));
963             my ($el,$ec) = split('\.',$w->index($last));
964             my $i;
965            
966             # swap min,max x,y coords
967             if($sl >= $el) {($sl,$el)=($el,$sl);}
968             if($sc >= $ec) {($sc,$ec)=($ec,$sc);}
969              
970             $w->tag('remove','sel','1.0','end');
971             # add a selection tag to all the selected lines
972             # FIXME: the selection's right limit is the line lenght of the line where mouse is on.BAD!!!
973             for($i=$sl;$i<=$el;$i++) {
974             $w->tag('add','sel',"$i.$sc","$i.$ec");
975             }
976             $w->idletasks;
977             }
978             }
979             }
980              
981             sub mouseSetInsert
982             {
983             my $w = shift;
984             my $ev = $w->XEvent;
985              
986             $w->{LINESTART}=0;
987             $w->Button1($ev->x,$ev->y);
988             }
989              
990             sub mouseSelect
991             {
992             my $w = shift;
993             my $ev = $w->XEvent;
994              
995             $Tk::selectionType='normal';
996             $Tk::x=$ev->x;
997             $Tk::y=$ev->y;
998             $w->SelectTo($ev->xy);
999             }
1000              
1001             sub mouseSelectWord
1002             {
1003             my $w = shift;
1004             my $ev = $w->XEvent;
1005              
1006             $Tk::selectionType='normal';
1007             $w->SelectTo($ev->xy,'word');
1008             Tk::catch {$w->markSet('insert',"sel.first")};
1009             }
1010              
1011             sub mouseSelectLine
1012             {
1013             my $w = shift;
1014             my $ev = $w->XEvent;
1015              
1016             $Tk::selectionType='normal';
1017             $w->SelectTo($ev->xy,'line');
1018             Tk::catch {$w->markSet('insert',"sel.first")};
1019             }
1020              
1021             #+20010117 JWT cause right button to extend select
1022             sub mouseSelectChar
1023             {
1024             my $w = shift;
1025             my $ev = $w->XEvent;
1026              
1027             $Tk::selectionType='normal';
1028             $w->SelectTo($ev->xy,'char');
1029             Tk::catch {$w->markSet('insert',"sel.first")};
1030             }
1031             #+
1032              
1033             sub mouseSelectAdd
1034             {
1035             my $w = shift;
1036             my $ev = $w->XEvent;
1037              
1038             $Tk::selectionType='normal';
1039             $w->ResetAnchor($ev->xy);
1040             $w->SelectTo($ev->xy,'char');
1041             }
1042              
1043             sub mouseSelectAddWord
1044             {
1045             my $w = shift;
1046             my $ev = $w->XEvent;
1047              
1048             $Tk::selectionType='normal';
1049             $w->SelectTo($ev->xy,'word');
1050             }
1051              
1052             sub mouseSelectAddLine
1053             {
1054             my $w = shift;
1055             my $ev = $w->XEvent;
1056              
1057             $Tk::selectionType='normal';
1058             $w->SelectTo($ev->xy,'line');
1059             }
1060              
1061             sub mouseSelectAutoScan
1062             {
1063             my $w = shift;
1064             my $ev = $w->XEvent;
1065              
1066             $Tk::selectionType='normal';
1067             $Tk::x=$ev->x;
1068             $Tk::y=$ev->y;
1069             $w->AutoScan;
1070             }
1071              
1072             sub mouseSelectAutoScanStop
1073             {
1074             my $w = shift;
1075              
1076             $w->CancelRepeat;
1077             }
1078              
1079             sub mouseMoveInsert
1080             {
1081             my $w = shift;
1082             my $ev = $w->XEvent;
1083              
1084             $Tk::selectionType='normal';
1085             $w->markSet('insert',$ev->xy);
1086             }
1087              
1088             sub mouseRectSelection
1089             {
1090             my $w = shift;
1091             my $ev = $w->XEvent;
1092              
1093             $Tk::selectionType='rect';
1094             $Tk::x=$ev->x;
1095             $Tk::y=$ev->y;
1096             $w->SelectTo($ev->xy);
1097             }
1098              
1099             sub mouseMovePageTo
1100             {
1101             my $w = shift;
1102             my $ev = $w->XEvent;
1103              
1104             $w->Button2($ev->x,$ev->y);
1105             }
1106              
1107             sub mouseMovePage
1108             {
1109             my $w = shift;
1110             my $ev = $w->XEvent;
1111              
1112             $w->Motion2($ev->x,$ev->y);
1113             }
1114            
1115             sub mousePasteSelection
1116             {
1117             my $w = shift;
1118             my $ev = $w->XEvent;
1119              
1120             if(!$Tk::mouseMoved) {
1121             Tk::catch { $w->insert($ev->xy,$w->SelectionGet);};
1122             }
1123             }
1124              
1125             sub KeySelect
1126             {
1127             my $w = shift;
1128             my $new = shift;
1129             my ($first,$last);
1130             if(!defined $w->tag('ranges','sel')) {
1131             # No selection yet
1132             $w->markSet('anchor','insert');
1133             if($w->compare($new,"<",'insert')) {
1134             $w->tag('add','sel',$new,'insert');
1135             } else {
1136             $w->tag('add','sel','insert',$new);
1137             }
1138             } else {
1139             # Selection exists
1140             if($w->compare($new,"<",'anchor')) {
1141             $first=$new;
1142             $last='anchor';
1143             } else {
1144             $first='anchor';
1145             $last=$new;
1146             }
1147             if((!defined $Tk::selectionType) || ($Tk::selectionType eq 'normal')) {
1148             $w->tag('remove','sel','1.0',$first);
1149             $w->tag('add','sel',$first,$last);
1150             $w->tag('remove','sel',$last,'end');
1151             } elsif($Tk::selectionType eq 'rect') {
1152             my ($sl,$sc) = split('\.',$w->index($first));
1153             my ($el,$ec) = split('\.',$w->index($last));
1154             my $i;
1155            
1156             # swap min,max x,y coords
1157             if($sl >= $el) {($sl,$el)=($el,$sl);}
1158             if($sc >= $ec) {($sc,$ec)=($ec,$sc);}
1159              
1160             $w->tag('remove','sel','1.0','end');
1161             # add a selection tag to all the selected lines
1162             # FIXME: the selection's right limit is the line lenght of the line where mouse is on.BAD!!!
1163             for($i=$sl;$i<=$el;$i++) {
1164             $w->tag('add','sel',"$i.$sc","$i.$ec");
1165             }
1166             }
1167             }
1168             $w->markSet('insert',$new);
1169             $w->see('insert');
1170             $w->idletasks;
1171             }
1172              
1173             sub moveLeft
1174             {
1175             my $w = shift;
1176              
1177             $w->{LINESTART}=0;
1178             $w->SetCursor($w->index("insert - 1c"));
1179             }
1180              
1181             sub selectLeft
1182             {
1183             my $w = shift;
1184              
1185             $w->{LINESTART}=0;
1186             $Tk::selectionType='normal';
1187             $w->KeySelect($w->index("insert - 1c"));
1188             }
1189              
1190             sub selectRectLeft
1191             {
1192             my $w = shift;
1193              
1194             $w->{LINESTART}=0;
1195             $Tk::selectionType='rect';
1196             $w->KeySelect($w->index("insert - 1c"));
1197             }
1198              
1199             sub moveLeftWord
1200             {
1201             my $w = shift;
1202              
1203             $w->{LINESTART}=0;
1204             $w->SetCursor($w->index("insert - 1c wordstart"));
1205             }
1206              
1207             sub selectLeftWord
1208             {
1209             my $w = shift;
1210              
1211             $w->{LINESTART}=0;
1212             $Tk::selectionType='normal';
1213             $w->KeySelect($w->index("insert - 1c wordstart"));
1214             }
1215              
1216             sub moveRight
1217             {
1218             my $w = shift;
1219              
1220             $w->{LINESTART}=0;
1221             $w->SetCursor($w->index("insert + 1c"));
1222             }
1223              
1224             sub selectRight
1225             {
1226             my $w = shift;
1227              
1228             $w->{LINESTART}=0;
1229             $Tk::selectionType='normal';
1230             $w->KeySelect($w->index("insert + 1c"));
1231             }
1232              
1233             sub selectRectRight
1234             {
1235             my $w = shift;
1236              
1237             $w->{LINESTART}=0;
1238             $Tk::selectionType='rect';
1239             $w->KeySelect($w->index("insert + 1c"));
1240             }
1241              
1242             sub moveRightWord
1243             {
1244             my $w = shift;
1245              
1246             $w->{LINESTART}=0;
1247             $w->SetCursor($w->index("insert + 1c wordend"));
1248             }
1249              
1250             sub selectRightWord
1251             {
1252             my $w = shift;
1253              
1254             $w->{LINESTART}=0;
1255             $Tk::selectionType='normal';
1256             $w->KeySelect($w->index("insert wordend"));
1257             }
1258              
1259             sub moveUp
1260             {
1261             my $w = shift;
1262              
1263             $w->{LINESTART}=0;
1264             $w->SetCursor($w->UpDownLine(-1));
1265             }
1266              
1267             sub selectUp
1268             {
1269             my $w = shift;
1270              
1271             $w->{LINESTART}=0;
1272             $Tk::selectionType='normal';
1273             $w->KeySelect($w->UpDownLine(-1));
1274             }
1275              
1276             sub selectRectUp
1277             {
1278             my $w = shift;
1279              
1280             $w->{LINESTART}=0;
1281             $Tk::selectionType='rect';
1282             $w->KeySelect($w->UpDownLine(-1));
1283             }
1284              
1285             sub moveUpParagraph
1286             {
1287             my $w = shift;
1288              
1289             $w->{LINESTART}=0;
1290             $w->SetCursor($w->PrevPara('insert'));
1291             }
1292              
1293             sub selectUpParagraph
1294             {
1295             my $w = shift;
1296              
1297             $w->{LINESTART}=0;
1298             $Tk::selectionType='normal';
1299             $w->KeySelect($w->PrevPara('insert'));
1300             }
1301              
1302             sub moveDown
1303             {
1304             my $w = shift;
1305              
1306             $w->{LINESTART}=0;
1307             $w->SetCursor($w->UpDownLine(1));
1308             }
1309              
1310             sub selectDown
1311             {
1312             my $w = shift;
1313              
1314             $w->{LINESTART}=0;
1315             $Tk::selectionType='normal';
1316             $w->KeySelect($w->UpDownLine(1));
1317             }
1318              
1319             sub selectRectDown
1320             {
1321             my $w = shift;
1322              
1323             $w->{LINESTART}=0;
1324             $Tk::selectionType='rect';
1325             $w->KeySelect($w->UpDownLine(1));
1326             }
1327              
1328             sub moveDownParagraph
1329             {
1330             my $w = shift;
1331              
1332             $w->{LINESTART}=0;
1333             $w->SetCursor($w->NextPara('insert'));
1334             }
1335              
1336             sub selectDownParagraph
1337             {
1338             my $w = shift;
1339              
1340             $w->{LINESTART}=0;
1341             $Tk::selectionType='normal';
1342             $w->KeySelect($w->NextPara('insert'));
1343             }
1344              
1345             sub moveLineStart
1346             {
1347             my $w = shift;
1348            
1349             if(exists $w->{LINESTART} && $w->{LINESTART} == 1) {
1350             $w->SetCursor('insert linestart');
1351             $w->{LINESTART}=0;
1352             } else {
1353             $w->{LINESTART}=1;
1354             my $str = $w->get('insert linestart','insert lineend');
1355             my $i=0;
1356            
1357             if($str =~ /^(\s+)(\S*)/) {
1358             if($2) {$i=length($1);}
1359             else {$i=0};
1360             }
1361             $w->SetCursor("insert linestart + $i c");
1362             }
1363             }
1364              
1365             sub selectToLineStart
1366             {
1367             my $w = shift;
1368              
1369             $w->{LINESTART}=0;
1370             $Tk::selectionType='normal';
1371             $w->KeySelect('insert linestart');
1372             }
1373              
1374             sub moveTextStart
1375             {
1376             my $w = shift;
1377              
1378             $w->{LINESTART}=0;
1379             $w->SetCursor('1.0');
1380             }
1381              
1382             sub selectToTextStart
1383             {
1384             my $w = shift;
1385              
1386             $w->{LINESTART}=0;
1387             $Tk::selectionType='normal';
1388             $w->KeySelect('1.0');
1389             }
1390              
1391             sub moveLineEnd
1392             {
1393             my $w = shift;
1394              
1395             $w->{LINESTART}=0;
1396             $w->SetCursor('insert lineend');
1397             }
1398              
1399             sub selectToLineEnd
1400             {
1401             my $w = shift;
1402              
1403             $w->{LINESTART}=0;
1404             $Tk::selectionType='normal';
1405             $w->KeySelect('insert lineend');
1406             }
1407              
1408             sub moveTextEnd
1409             {
1410             my $w = shift;
1411              
1412             $w->{LINESTART}=0;
1413             $w->SetCursor('end - 1c');
1414             }
1415              
1416             sub selectToTextEnd
1417             {
1418             my $w = shift;
1419              
1420             $w->{LINESTART}=0;
1421             $Tk::selectionType='normal';
1422             $w->KeySelect('end - 1c');
1423             }
1424              
1425             sub ScrollPages
1426             {
1427             my ($w,$count) = @_;
1428             my ($l,$c) = $w->index('end');
1429             my ($slimit,$elimit) = $w->yview;
1430             # get current page top and bottom line coords
1431             $slimit=int($l*$slimit)+1;
1432             $slimit="$slimit.0";
1433             $elimit=int($l*$elimit);
1434             $elimit="$elimit.0";
1435             # position insert cursor at text begin/end if the text is scrolled to begin/end
1436             if($count < 0 && $w->compare($slimit,'<=','1.0')) {return('1.0');}
1437             elsif($count >= 0 && $w->compare($elimit,'>=','end')) {return($w->index('end'));}
1438             else {return $w->SUPER::ScrollPages($count);}
1439             }
1440            
1441             sub movePageUp
1442             {
1443             my $w = shift;
1444              
1445             $w->{LINESTART}=0;
1446             $w->SetCursor($w->ScrollPages(-1));
1447             }
1448              
1449             sub selectToPageUp
1450             {
1451             my $w = shift;
1452              
1453             $w->{LINESTART}=0;
1454             $Tk::selectionType='normal';
1455             $w->KeySelect($w->ScrollPages(-1));
1456             }
1457              
1458             sub movePageLeft
1459             {
1460             my $w = shift;
1461              
1462             $w->{LINESTART}=0;
1463             $w->xview('scroll',-1,'page');
1464             }
1465              
1466             sub movePageDown
1467             {
1468             my $w = shift;
1469              
1470             $w->{LINESTART}=0;
1471             $w->SetCursor($w->ScrollPages(1));
1472             }
1473              
1474             sub selectToPageDown
1475             {
1476             my $w = shift;
1477              
1478             $w->{LINESTART}=0;
1479             $Tk::selectionType='normal';
1480             $w->KeySelect($w->ScrollPages(1));
1481             }
1482              
1483             sub movePageRight
1484             {
1485             my $w = shift;
1486              
1487             $w->{LINESTART}=0;
1488             $w->xview('scroll',1,'page');
1489             }
1490              
1491             sub setSelectionMark
1492             {
1493             my $w = shift;
1494              
1495             $w->{LINESTART}=0;
1496             $w->markSet('anchor','insert');
1497             }
1498              
1499             sub selectToMark
1500             {
1501             my $w = shift;
1502              
1503             $w->{LINESTART}=0;
1504             $Tk::selectionType='normal';
1505             $w->SelectTo('insert','char');
1506             }
1507              
1508             sub selectAll
1509             {
1510             my $w = shift;
1511              
1512             $w->{LINESTART}=0;
1513             $Tk::selectionType='normal';
1514             $w->tag('add','sel','1.0','end');
1515             }
1516              
1517             sub selectionShiftLeft
1518             {
1519             my $w = shift;
1520            
1521             $w->{LINESTART}=0;
1522             $w->_SelectionShift(" ","left");
1523             }
1524              
1525             sub selectionShiftLeftTab
1526             {
1527             my $w = shift;
1528            
1529             $w->{LINESTART}=0;
1530             $w->_SelectionShift("\t","left");
1531             }
1532              
1533             sub selectionShiftRight
1534             {
1535             my $w = shift;
1536            
1537             $w->{LINESTART}=0;
1538             $w->_SelectionShift(" ","right");
1539             }
1540              
1541             sub selectionShiftRightTab
1542             {
1543             my $w = shift;
1544            
1545             $w->{LINESTART}=0;
1546             $w->_SelectionShift("\t","right");
1547             }
1548              
1549             sub _SelectionShift
1550             {
1551             my ($w,$type,$dir) = @_;
1552            
1553             if((!defined $type) || (!defined $dir)) {return;}
1554             if(!defined $w->tag('ranges','sel')) {return;}
1555            
1556             my ($sline,$scol) = split('\.',$w->index('sel.first'));
1557             my ($eline,$ecol) = split('\.',$w->index('sel.last'));
1558            
1559             my $col;
1560             if($Tk::selectionType eq 'rect') {$col=$scol;}
1561             else {$col=0;}
1562            
1563             if($ecol == 0) {$eline--;}
1564            
1565             my $s;
1566             $w->_BeginUndoBlock;
1567             if($dir eq "left") {
1568             if($scol != 0) {$scol--;}
1569             $w->delete("$sline.$scol");
1570             for(my $i=$sline+1;$i <= $eline;$i++) {
1571             $s="$i.$scol";
1572             if($w->compare($s,'==',$w->index("$s lineend"))) {next;}
1573             $w->delete("$i.$scol");
1574             $w->idletasks;
1575             }
1576             } elsif($dir eq "right") {
1577             $w->insert("$sline.$scol",$type);
1578             for(my $i=$sline+1;$i <= $eline;$i++) {
1579             # $w->insert("$i.$scol",$type);
1580             $s="$i.$scol";
1581             $w->markSet('undopos' => $s);
1582             $w->SUPER::insert($s,$type);
1583             $w->_AddUndo('delete',$s,$w->index('undopos'));
1584             $w->idletasks;
1585             }
1586             }
1587             $w->_EndUndoBlock;
1588             }
1589              
1590             sub ins
1591             {
1592             my $w = shift;
1593              
1594             $w->{LINESTART}=0;
1595             if($w->{INSERTMODE} eq 'insert') {$w->{INSERTMODE}='overwrite';}
1596             elsif($w->{INSERTMODE} eq 'overwrite') {$w->{INSERTMODE}='insert';}
1597             }
1598              
1599             sub enter
1600             {
1601             my $w = shift;
1602              
1603             $w->_BeginUndoBlock;
1604             Tk::catch {$w->Insert("\n")};
1605             if($w->cget('-indentmode') eq 'auto') {
1606             $w->_AutoIndent;
1607             }
1608             $w->_EndUndoBlock;
1609             }
1610              
1611             sub autoIndentEnter
1612             {
1613             my $w = shift;
1614              
1615             $w->_BeginUndoBlock;
1616             Tk::catch {$w->Insert("\n")};
1617             $w->_AutoIndent;
1618             $w->_EndUndoBlock;
1619             }
1620              
1621             sub noAutoIndentEnter
1622             {
1623             my $w = shift;
1624              
1625             Tk::catch {$w->Insert("\n")};
1626             }
1627              
1628             sub _AutoIndent
1629             {
1630             my $w = shift;
1631             my ($line,$col) = split('\.',$w->index('insert'));
1632              
1633             # no autoindent for first line
1634             if($line == 1) {return;}
1635             $line--;
1636             my $s=$w->get("$line.0","$line.end");
1637             if($s =~ /^(\s+)(\S*)/) {$s=$1;}
1638             else {$s='';}
1639             if($2) {
1640             $w->insert('insert linestart',$s);
1641             }
1642             }
1643              
1644             sub del
1645             {
1646             my $w = shift;
1647              
1648             $w->Delete;
1649             }
1650              
1651             # overrides Tk::Text->Delete method
1652             sub Delete
1653             {
1654             my $w = shift;
1655             my $sel = Tk::catch {$w->tag('nextrange','sel','1.0','end');};
1656            
1657             if(defined $sel) {
1658             $w->deleteSelected;
1659             } else {
1660             $w->delete('insert');
1661             $w->see('insert');
1662             }
1663             }
1664              
1665             sub backSpace
1666             {
1667             my $w = shift;
1668              
1669             $w->Backspace;
1670             }
1671              
1672             # overrides Tk::Text->Backspace method
1673             sub Backspace
1674             {
1675             my $w = shift;
1676             my $sel = Tk::catch {$w->tag('nextrange','sel','1.0','end');};
1677            
1678             if(defined $sel) {
1679             $w->deleteSelected;
1680             } elsif($w->compare('insert',"!=",'1.0')) {
1681             $w->delete('insert - 1c');
1682             $w->see('insert');
1683             }
1684             }
1685              
1686             sub deleteToWordStart
1687             {
1688             my $w = shift;
1689            
1690             if($w->compare('insert','==','insert wordstart')) {
1691             $w->delete('insert - 1c');
1692             } else {
1693             $w->delete('insert wordstart','insert');
1694             }
1695             }
1696              
1697             sub deleteToWordEnd
1698             {
1699             my $w = shift;
1700            
1701             if($w->compare('insert','==','insert wordend')) {
1702             $w->delete('insert');
1703             } else {
1704             $w->delete('insert','insert wordend');
1705             }
1706             }
1707              
1708             sub deleteToLineStart
1709             {
1710             my $w = shift;
1711              
1712             if($w->compare('insert','==','1.0')) {return;}
1713             if($w->compare('insert','==','insert linestart')) {
1714             $w->delete('insert - 1c');
1715             } else {
1716             $w->delete('insert linestart','insert');
1717             }
1718             }
1719              
1720             sub deleteToLineEnd
1721             {
1722             my $w = shift;
1723            
1724             if($w->compare('insert','==','insert lineend')) {
1725             $w->delete('insert');
1726             } else {
1727             $w->delete('insert','insert lineend');
1728             }
1729             }
1730              
1731             sub deleteWord
1732             {
1733             my $w = shift;
1734              
1735             $w->delete('insert wordstart','insert wordend');
1736             }
1737              
1738             sub deleteLine
1739             {
1740             my $w = shift;
1741              
1742             $w->delete('insert linestart','insert lineend + 1c');
1743             $w->markSet('insert','insert linestart');
1744             }
1745              
1746             sub insertControlCode
1747             {
1748             my $w = shift;
1749            
1750             $w->{LINESTART}=0;
1751             $w->{ASCIICODE} = 1;
1752             }
1753              
1754             #sub focusNext
1755             #{
1756             # my $w = shift;
1757             #
1758             # $w->focusNext;
1759             #}
1760             #
1761             #sub focusPrev
1762             #{
1763             # my $w = shift;
1764             #
1765             # $w->focusPrev;
1766             #}
1767              
1768             # find a matching char for the given one
1769             sub _FindMatchingChar
1770             {
1771             my ($w,$sc,$pos,$slimit,$elimit) = @_;
1772             my $mc = ${$w->{MATCHINGCOUPLES}->{$sc}}[0]; # char to search
1773            
1774             if(!defined $mc) {return undef;}
1775            
1776             my $dir = ${$w->{MATCHINGCOUPLES}->{$sc}}[1]; # forward or backward search
1777             my $spos=($dir == 1 ? $w->index("$pos + $dir c") : $w->index($pos));
1778             my $d=1;
1779             my ($p,$c);
1780             my $match;
1781              
1782             if($dir == 1) { # forward search
1783             $match="[\\$mc|\\$sc]+";
1784             for($p=$spos;$w->compare($p,'<',$elimit);$p=$w->index("$p + 1c")) {
1785             $p=$w->SUPER::search('-forwards','-regex','--',$match,$p,$elimit);
1786             if(!defined $p) {return undef;}
1787             $c=$w->get($p);
1788             if($c eq $mc) {
1789             $d--;
1790             if($d == 0) {
1791             return $p;
1792             }
1793             } elsif($c eq $sc) {
1794             $d++;
1795             }
1796             Tk::DoOneEvent(Tk::DONT_WAIT);
1797             }
1798             } else { # backward search
1799             $match="[\\$sc|\\$mc]+";
1800             for($p=$spos;$w->compare($p,'>=',$slimit);) {
1801             $p=$w->SUPER::search('-backwards','-regex','--',$match,$p,$slimit);
1802             if(!defined $p) {return undef;}
1803             $c=$w->get($p);
1804             if($c eq $mc) {
1805             $d--;
1806             if($d == 0) {
1807             return $p;
1808             }
1809             } elsif($c eq $sc) {
1810             $d++;
1811             }
1812             if($w->compare($p,'==','1.0')) {return undef;}
1813             Tk::DoOneEvent(Tk::DONT_WAIT);
1814             }
1815             }
1816             return undef;
1817             }
1818              
1819             sub flashMatchingChar
1820             {
1821             my $w = shift;
1822             my $s = $w->index('insert');
1823             my $str = $w->get('insert');
1824            
1825             if(exists $w->{MATCHINGCOUPLES}->{$str}) {
1826             my $i=$w->_FindMatchingChar($str,$s,"1.0","end");
1827             if(defined $i) {
1828             my $sel = Tk::catch {$w->tag('nextrange','match','1.0','end');};
1829             if(defined $sel) {$w->tag('remove','match','match.first');}
1830             $w->tag('add','match',$i,$w->index("$i + 1c"));
1831             my $t=$w->cget('-matchhighlighttime');
1832             if($t != 0) {$w->after($t,[\&removeMatch,$w,$i]);}
1833             return $i;
1834             }
1835             }
1836             return undef;
1837             }
1838              
1839             sub findMatchingChar
1840             {
1841             my $w = shift;
1842             my $i = $w->flashMatchingChar;
1843            
1844             if(defined $i) {$w->see($i);}
1845             }
1846              
1847             sub jumpToMatchingChar
1848             {
1849             my $w = shift;
1850             my $i = $w->flashMatchingChar;
1851            
1852             if(defined $i) {$w->SetCursor($i);}
1853             }
1854              
1855              
1856             sub escape
1857             {
1858             my $w = shift;
1859             $w->tag('remove','sel','1.0','end');
1860             }
1861              
1862             sub tab
1863             {
1864             my $w = shift;
1865              
1866             $w->Insert("\t");
1867             $w->focus;
1868             $w->break;
1869             }
1870              
1871             sub leftTab
1872             {
1873             }
1874              
1875             sub copy
1876             {
1877             my $w = shift;
1878              
1879             Tk::catch{$w->clipboardCopy;};
1880             }
1881              
1882             sub cut
1883             {
1884             my $w = shift;
1885              
1886             Tk::catch{$w->clipboardCut;};
1887             $w->see('insert');
1888             }
1889              
1890             sub paste
1891             {
1892             my $w = shift;
1893              
1894             Tk::catch{$w->clipboardPaste;};
1895             $w->see('insert');
1896             }
1897              
1898             sub inlinePaste
1899             {
1900             my $w = shift;
1901             my ($l,$c) = split('\.',$w->index('insert'));
1902             my $str;
1903             my $f=0;
1904             Tk::catch{$str=$w->clipboardGet;};
1905            
1906             if($str eq "") {return;}
1907             $w->_BeginUndoBlock;
1908             while($str =~ /(.*)\n+/g) {
1909             $w->insert("$l.$c",$1);
1910             if($f == 0) {
1911             my ($el,$ec) = split('\.',$w->index('end'));
1912             if($l == $el) {
1913             $w->insert('end',"\n");
1914             $f=1;
1915             }
1916             } else {$w->insert('end',"\n");}
1917             $l++;
1918             $w->idletasks;
1919             }
1920             $w->_EndUndoBlock;
1921             $w->see('insert');
1922             }
1923              
1924             sub destroy
1925             {
1926             my $w = shift;
1927              
1928             $w->Destroy;
1929             }
1930              
1931             sub keyPress
1932             {
1933             my $w = shift;
1934             my $ev = $w->XEvent;
1935              
1936             $w->Insert($ev->A);
1937             }
1938              
1939             sub menuSelect
1940             {
1941             my $w = shift;
1942             #NOTE: (JWT) ALSO FIXED IN auto/Tk/Text/SuperText/menuSelect.al!!!!!
1943              
1944             #+20010117 JWT don't do these 2 lines in windows
1945             unless ($^O =~ /Win/)
1946             {
1947             my $ev = $w->XEvent;
1948            
1949             $w->TraverseToMenu($ev->K);
1950             }
1951             #+
1952             }
1953              
1954             sub noOP
1955             {
1956             my $w = shift;
1957             $w->NoOp;
1958             }
1959              
1960             1;
1961             __END__