File Coverage

blib/lib/Tk/TextANSIColor.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             package Tk::TextANSIColor;
2              
3 1     1   818 use strict;
  1         1  
  1         48  
4              
5             require Tk;
6             require Tk::Text;
7             require Tk::Font;
8              
9 1     1   860 use Term::ANSIColor;
  1         8316  
  1         99  
10              
11 1     1   10 use vars qw/ $VERSION /;
  1         2  
  1         57  
12             $VERSION = '0.15';
13              
14             # Inherit from Tk::Text
15 1     1   5 use base qw(Tk::Text);
  1         2  
  1         763  
16              
17             # Construct the new widget
18             Construct Tk::Widget 'TextANSIColor';
19              
20             # Setup some local (my'ed) variables that contain the
21             # control codes. Set up hashes which have keys of the control
22             # codes and values containing the actual color (the TAG).
23             # Currently retrieve these from Term::ANSIColor module.
24              
25             my (%fgcolors, %bgcolors);
26             my $clear = color('clear'); # Code to reset control codes
27              
28             my $code_bold = color('bold');
29             my $code_uline= color('underline');
30             my @colors = qw/black red green yellow blue magenta cyan white/;
31             for (@colors) {
32             my $fg = color($_);
33             my $bg = color("on_$_");
34              
35             $fgcolors{$fg} = "ANSIfg$_";
36             $bgcolors{$bg} = "ANSIbg$_";
37             }
38              
39              
40             # Initialise class.
41             # This effectively means create a whole load of Tag shortcuts
42             # that can be used on this widget
43              
44             sub InitObject {
45             my ($widget, $args) = @_;
46              
47             # Initialise base class
48             $widget->SUPER::InitObject($args);
49              
50             # Setup tags
51             # colors
52             for (@colors) {
53             $widget->tagConfigure("ANSIfg$_", -foreground => $_);
54             $widget->tagConfigure("ANSIbg$_", -background => $_);
55             }
56             # Underline
57             $widget->tagConfigure("ANSIul", -underline => 1);
58             $widget->tagConfigure("ANSIbd",
59             -font => $widget->Font(weight => "bold") );
60              
61             # return $widget;
62             }
63              
64              
65              
66             # Sub-classed insert method
67             # We replace ANSI color codes with Text tags
68              
69             sub insert {
70              
71             my $self= shift; # The widget reference
72             my $pos = shift; # The position to insert
73             my @userstuff = @_; # Strings and tags
74              
75             # This is the array containing text and tags pairs
76             # We pass this to SUPER::insert
77             # as (POS, string, [tags], string, [tags]....)
78             # insert_array contains string,[tags] pairs
79             my @insert_array = ();
80              
81             # Need to loop over @userstuff
82             # extracting out the text string and any user supplied tags.
83             # note that multiple sets of text strings and tags can be supplied
84             # as arguments to the insert() method, and we have to process
85             # each set in turn.
86             # Use an old-fashioned for since we have to extract two items at
87             # a time
88              
89             for (my $i=0; $i <= $#userstuff; $i += 2) {
90              
91             my $text = $userstuff[$i];
92             my $utags = $userstuff[$i+1];
93              
94             # Store the usertags in an array, expanding the
95             # array ref if required
96             my @taglist = ();
97             if (ref($utags) eq 'ARRAY') {
98             @taglist = @{$utags};
99             } else {
100             @taglist = ($utags);
101             }
102              
103             # Split the string on control codes
104             # returning the codes as well as the strings between
105             # the codes
106             # Note that this pattern also checks for the case when
107             # multiple escape codes are embedded together separated
108             # by semi-colons.
109             my @split = split /(\e\[(?:\d{1,2};?)+m)/, $text;
110              
111             # Array containing the tags to use with the insertion
112             # Note that this routine *always* assumes the colors are reset
113             # after the last insertion. ie it does not allow the colors to be
114             # remembered between calls to insert().
115             my @ansitags = ();
116              
117             # Current text string
118             my $cur_text = undef;
119              
120             # Now loop over the split strings
121             for my $part (@split) {
122              
123             # If we have a plain string, just store it
124             if ($part !~ /^\e/) {
125             $cur_text = $part;
126             } else {
127             # We have an escape sequence
128             # Need to store the current string with required tags
129             # Include the ansi tags and the user-supplied tag list
130             push(@insert_array, $cur_text, [@taglist, @ansitags])
131             if defined $cur_text;
132              
133             # There is no longer a 'current string'
134             $cur_text = undef;
135              
136             # The escape sequence can have semi-colon separated bits
137             # in it. Need to strip off the \e[ and the m. Split on
138             # semi-colon and then reconstruct before comparing
139             # We know it matches \e[....m so use substr
140              
141             # Only bother if we have a semi-colon
142              
143             my @escs = ($part);
144             if ($part =~ /;/) {
145             my $strip = substr($part, 2, length($part) - 3);
146              
147             # Split on ; (overwriting @escs)
148             @escs = split(/;/,$strip);
149              
150             # Now attach the correct escape sequence
151             foreach (@escs) { $_ = "\e[${_}m" }
152             }
153              
154             # Loop over all the escape sequences
155             for my $esc (@escs) {
156              
157             # Check what type of escape
158             if ($esc eq $clear) {
159             # Clear all escape sequences
160             @ansitags = ();
161             } elsif (exists $fgcolors{$esc}) {
162             # A foreground color has been specified
163             push(@ansitags, $fgcolors{$esc});
164             } elsif (exists $bgcolors{$esc}) {
165             # A background color
166             push(@ansitags, $bgcolors{$esc});
167             } elsif ($esc eq $code_bold) {
168             # Boldify
169             push(@ansitags, "ANSIbd");
170             } elsif ($esc eq $code_uline) {
171             # underline
172             push(@ansitags, "ANSIul");
173             } else {
174             print "Unrecognised control code - ignoring\n";
175             foreach (split //, $esc) {
176             print ord($_) . ": $_\n";
177             }
178             }
179            
180             }
181             }
182             }
183              
184             # If we still have a current string, push that onto the array
185             push(@insert_array, $cur_text, [@taglist, @ansitags])
186             if defined $cur_text;
187              
188             }
189              
190             # Finally, insert the string
191             $self->SUPER::insert($pos, @insert_array)
192             if $#insert_array > 0;
193              
194             }
195              
196             sub getansi {
197             my $self= shift; # The widget reference
198             my @args = @_;
199              
200             # Indicate whether we are in an ANSI tag
201             my $tagflag = 0;
202              
203             # Initialise the results string
204             my $res = '';
205              
206             # Get detailed contents (including tags)
207             my @xdump = $self->dump(@args);
208              
209             # Loop over the dumped array, incrementing in steps of 3
210             for (my $i=0;$i<=$#xdump;$i+=3) {
211              
212             # This is a tag. Check to see whether it is for an ANSI
213             # control code.
214             if ($xdump[$i] eq 'tagon') {
215              
216             if ($xdump[$i+1] =~ /^ANSIfg(\w+)/) {
217              
218             $res .= color($1);
219             $tagflag = 1;
220            
221             } elsif ($xdump[$i+1] =~ /^ANSIbg(\w+)/) {
222              
223             $res .= color("on_$1");
224             $tagflag = 1;
225            
226             } elsif ($xdump[$i+1] =~ /^ANSIbd/) {
227              
228             $res .= color('bold');
229             $tagflag = 1;
230            
231             } elsif ($xdump[$i+1] =~ /^ANSIul/) {
232              
233             $res .= color('underline');
234             $tagflag = 1;
235              
236             }
237              
238             $res .= $xdump[$i+4] if ($xdump[$i+3] eq 'text');
239             }
240              
241             if ($tagflag && $xdump[$i] eq 'tagoff') {
242              
243             $res .= color('reset');
244             $tagflag = 0;
245              
246             } elsif ($i > 3 && $xdump[$i] eq 'text' && $xdump[$i-3] ne 'tagon') {
247              
248             $res .= $xdump[$i+1];
249              
250             }
251              
252             }
253              
254             return $res;
255             }
256              
257             1;
258              
259             __END__