File Coverage

blib/lib/Socialtext/Wikrad/PageViewer.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Socialtext::Wikrad::PageViewer;
2 1     1   1118 use strict;
  1         2  
  1         27  
3 1     1   4 use warnings;
  1         2  
  1         20  
4 1     1   412 use Curses::UI::Common;
  0            
  0            
5             use base 'Curses::UI::TextEditor';
6             use Curses;
7             use Socialtext::Wikrad qw/$App/;
8              
9             sub new {
10             my $class = shift;
11             my $self = $class->SUPER::new(
12             -vscrollbar => 1,
13             -wrapping => 1,
14             @_,
15             );
16              
17             return $self;
18             }
19              
20             sub next_link {
21             my $self = shift;
22             my $pos = $self->{-pos};
23             my $text = $self->get;
24             my $after_text = substr($text, $pos, -1);
25             if ($after_text =~ m/\[(.)/) {
26             my $link_pos = $pos + $-[1];
27             $self->{-pos} = $link_pos;
28             }
29             }
30              
31             sub prev_link {
32             my $self = shift;
33             my $pos = $self->{-pos};
34             my $text = $self->get;
35             my $before_text = reverse substr($text, 0, $pos);
36             if ($before_text =~ m/\](.)/) {
37             my $link_pos = $pos - $-[1] - 1;
38             $self->{-pos} = $link_pos;
39             }
40             }
41              
42             sub viewer_enter {
43             my $self = shift;
44             my $pos = $self->{-pos};
45             my $text = $self->get;
46             my $before_pos = substr($text, 0, $pos);
47              
48             my @link_types = (
49             [ '{link:? (\S+) \[' => '\]' ],
50             [ '\[' => '\]' ],
51             );
52             my $link_text;
53             my $new_wksp;
54             for my $link (@link_types) {
55             my ($pre, $post) = @$link;
56             if ($before_pos =~ m/$pre([^$post]*)$/) {
57             $link_text = $1;
58             if (defined $2) {
59             $link_text = $2;
60             $new_wksp = $1;
61             }
62             my $after_pos = substr($text, $pos, -1);
63             if ($after_pos =~ m/([^$post]*)$post/) {
64             $link_text .= $1;
65             }
66             else {
67             $link_text = undef;
68             $new_wksp = undef;
69             }
70             }
71             last if $link_text;
72             }
73              
74             $App->set_page($link_text, $new_wksp) if $link_text;
75             }
76              
77             sub readonly($;)
78             {
79             my $this = shift;
80             my $readonly = shift;
81              
82             # setup key bindings with readonly set to true
83             # so we can't edit this puppy
84             $this->SUPER::readonly(1);
85             $this->{-readonly} = $readonly;
86             return $this;
87             }
88              
89             sub draw_text(;$)
90             {
91             my $this = shift;
92             my $no_doupdate = shift || 0;
93             return $this if $Curses::UI::screen_too_small;
94              
95             # Return immediately if this object is hidden.
96             return $this if $this->hidden;
97              
98             # Draw the text.
99             for my $id (0 .. $this->canvasheight - 1)
100             {
101             # Let there be color
102             my $co = $Curses::UI::color_object;
103             if ($Curses::UI::color_support) {
104             my $pair = $co->get_color_pair(
105             $this->{-fg},
106             $this->{-bg});
107              
108             $this->{-canvasscr}->attron(COLOR_PAIR($pair));
109             }
110              
111             if (defined $this->{-search_highlight}
112             and $this->{-search_highlight} == ($id+$this->{-yscrpos})) {
113             $this->{-canvasscr}->attron(A_REVERSE) if (not $this->{-reverse});
114             $this->{-canvasscr}->attroff(A_REVERSE) if ($this->{-reverse});
115             } else {
116             $this->{-canvasscr}->attroff(A_REVERSE) if (not $this->{-reverse});
117             $this->{-canvasscr}->attron(A_REVERSE) if ($this->{-reverse});
118             }
119              
120             my $l = $this->{-scr_lines}->[$id + $this->{-yscrpos}];
121             if (defined $l)
122             {
123             # Get the part of the line that is in view.
124             my $inscreen = '';
125             my $fromxscr = '';
126             if ($this->{-xscrpos} < length($l))
127             {
128             $fromxscr = substr($l, $this->{-xscrpos}, length($l));
129             $inscreen = ($this->text_wrap(
130             $fromxscr,
131             $this->canvaswidth,
132             NO_WORDWRAP))->[0];
133             }
134              
135             # Clear line.
136             $this->{-canvasscr}->addstr(
137             $id, 0,
138             " "x$this->canvaswidth
139             );
140              
141             # Strip newline
142             $inscreen =~ s/\n//;
143             my @segments = (
144             { text => $inscreen },
145             );
146             my $replace_segment = sub {
147             my ($i, $pre, $new, $attr, $post) = @_;
148             my $old_segment = $segments[$i];
149             my $old_attr = $old_segment->{attr};
150             my @new_segments;
151             $attr = [$attr] unless ref($attr) eq 'ARRAY';
152             push @new_segments, {
153             attr => $old_attr,
154             text => $pre,
155             } if $pre;
156             push @new_segments, {
157             text => $new,
158             attr => $attr,
159             };
160             push @new_segments, {
161             text => $post,
162             attr => $old_attr,
163             } if $post;
164              
165             splice(@segments, $i, 1, @new_segments);
166             };
167              
168             my $make_color = sub {
169             return COLOR_PAIR($co->get_color_pair(shift, $this->{-bg}));
170             };
171             my $full_line = sub {
172             my ($starting, $colour) = @_;
173             return {
174             regex => qr/^($starting.+)/,
175             cb => sub {
176             my ($i, @matches) = @_;
177             $replace_segment->($i, '', $matches[0],
178             $make_color->($colour), '');
179             },
180             };
181             };
182             my $inline = sub {
183             my ($char, $attr) = @_;
184             my $backchar = reverse $char;
185             return {
186             regex => qr/^(.*?\s)?(\Q$char\E\S.+\S\Q$backchar\E\s?)(.*)/,
187             cb => sub {
188             my ($i, @matches) = @_;
189             $replace_segment->($i, @matches[0, 1], $attr, $matches[2]);
190             },
191             };
192             };
193             my @wiki_syntax = (
194             $full_line->('\^+ ', 'magenta'), # heading
195             $full_line->('\*+ ', 'green'), # list
196             $inline->('*', A_BOLD),
197             $inline->('_', A_UNDERLINE),
198             $inline->('-', A_STANDOUT),
199             $inline->('-----', [A_STANDOUT, $make_color->('yellow')]),
200             { # link
201             regex => qr/(.*?)(\[[^\]]+\])(.*)/,
202             cb => sub {
203             my ($i, @matches) = @_;
204             return unless $matches[0] or $matches[1];
205             $replace_segment->($i, @matches[0, 1],
206             $make_color->('blue'), $matches[2]);
207             },
208             },
209             );
210             for my $w (@wiki_syntax) {
211             my $i = 0;
212             while($i < @segments) {
213             my $s = $segments[$i];
214             my $text = $s->{text};
215             if ($text =~ $w->{regex}) {
216             $w->{cb}->($i, $1, $2, $3);
217             }
218             $i++;
219             }
220             }
221              
222             # Display the string
223             my $len = 0;
224             for my $s (@segments) {
225             my $a = $s->{attr} || [];
226             $this->{-canvasscr}->attron($_) for @$a;
227             $this->{-canvasscr}->addstr($id, $len, $s->{text});
228             $this->{-canvasscr}->attroff($_) for @$a;
229             $len += length($s->{text});
230             }
231             } else {
232             last;
233             }
234             }
235              
236             # Move the cursor.
237             # Take care of TAB's
238             if ($this->{-readonly})
239             {
240             $this->{-canvasscr}->move(
241             $this->canvasheight-1,
242             $this->canvaswidth-1
243             );
244             } else {
245             my $l = $this->{-scr_lines}->[$this->{-ypos}];
246             my $precursor = substr(
247             $l,
248             $this->{-xscrpos},
249             $this->{-xpos} - $this->{-xscrpos}
250             );
251              
252             my $realxpos = scrlength($precursor);
253             $this->{-canvasscr}->move(
254             $this->{-ypos} - $this->{-yscrpos},
255             $realxpos
256             );
257             }
258            
259             $this->{-canvasscr}->attroff(A_UNDERLINE) if $this->{-showlines};
260             $this->{-canvasscr}->attroff(A_REVERSE) if $this->{-reverse};
261             $this->{-canvasscr}->noutrefresh();
262             doupdate() unless $no_doupdate;
263             return $this;
264             }
265              
266             1;