File Coverage

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