File Coverage

blib/lib/Curses/UI/TextEditor.pm
Criterion Covered Total %
statement 165 553 29.8
branch 56 262 21.3
condition 15 61 24.5
subroutine 17 57 29.8
pod 13 51 25.4
total 266 984 27.0


line stmt bran cond sub pod time code
1             # ----------------------------------------------------------------------
2             # Curses::UI::TextEditor
3             #
4             # (c) 2001-2002 by Maurice Makaay. All rights reserved.
5             # This file is part of Curses::UI. Curses::UI is free software.
6             # You can redistribute it and/or modify it under the same terms
7             # as perl itself.
8             #
9             # Currently maintained by Marcus Thiesen
10             # e-mail: marcus@cpan.thiesenweb.de
11             # ----------------------------------------------------------------------
12              
13             # TODO: fix dox
14              
15             package Curses::UI::TextEditor;
16              
17 2     2   12 use strict;
  2         4  
  2         70  
18 2     2   9 use Curses;
  2         5  
  2         12192  
19 2     2   21 use Curses::UI::Common;
  2         3  
  2         262  
20 2     2   18 use Curses::UI::Widget;
  2         4  
  2         224  
21 2     2   1477 use Curses::UI::Searchable;
  2         7  
  2         149  
22              
23 2         12936 use vars qw(
24             $VERSION
25             @ISA
26 2     2   10 );
  2         3  
27              
28             $VERSION = '1.5';
29              
30             @ISA = qw(
31             Curses::UI::Widget
32             Curses::UI::Common
33             Curses::UI::Searchable
34             );
35            
36             # Configuration: routine name to subroutine mapping.
37             my %routines = (
38             'loose-focus' => \&loose_focus,
39             'undo' => \&undo,
40             'paste' => \&paste,
41             'delete-till-eol' => \&delete_till_eol,
42             'delete-line' => \&delete_line,
43             'delete-character' => \&delete_character,
44             'add-string' => \&add_string,
45             'clear-line' => \&clear_line,
46             'backspace' => \&backspace,
47             'newline' => \&newline,
48             'toggle-showhardreturns' => \&toggle_showhardreturns,
49             'toggle-showoverflow' => \&toggle_showoverflow,
50             'toggle-wrapping' => \&toggle_wrapping,
51             'cursor-right' => \&cursor_right,
52             'cursor-left' => \&cursor_left,
53             'cursor-up' => \&cursor_up,
54             'cursor-down' => \&cursor_down,
55             'cursor-pageup' => \&cursor_pageup,
56             'cursor-pagedown' => \&cursor_pagedown,
57             'cursor-scrlinestart' => \&cursor_to_scrlinestart,
58             'cursor-scrlineend' => \&cursor_to_scrlineend,
59             'cursor-home' => \&cursor_to_home,
60             'cursor-end' => \&cursor_to_end,
61             'search-forward' => \&search_forward,
62             'search-backward' => \&search_backward,
63             'mouse-button1' => \&mouse_button1,
64             );
65              
66             # Configuration: binding to routine name mapping.
67             my %basebindings = (
68             CUI_TAB() => 'loose-focus',
69             KEY_BTAB() => 'loose-focus',
70             KEY_LEFT() => 'cursor-left',
71             "\cB" => 'cursor-left',
72             KEY_RIGHT() => 'cursor-right',
73             "\cF" => 'cursor-right',
74             KEY_DOWN() => 'cursor-down',
75             "\cN" => 'cursor-down',
76             KEY_UP() => 'cursor-up',
77             "\cP" => 'cursor-up',
78             KEY_PPAGE() => 'cursor-pageup',
79             KEY_NPAGE() => 'cursor-pagedown',
80             KEY_HOME() => 'cursor-home',
81             KEY_END() => 'cursor-end',
82             "\cA" => 'cursor-scrlinestart',
83             "\cE" => 'cursor-scrlineend',
84             "\cW" => 'toggle-wrapping',
85             "\cR" => 'toggle-showhardreturns',
86             "\cT" => 'toggle-showoverflow',
87             );
88              
89             my %viewbindings = (
90             "/" => 'search-forward',
91             "?" => 'search-backward',
92             CUI_SPACE() => 'cursor-pagedown',
93             "-" => 'cursor-pageup',
94             "]" => 'cursor-pagedown',
95             "[" => 'cursor-pageup',
96             );
97              
98             my %editbindings = (
99             '' => 'add-string',
100             "\cZ" => 'undo',
101             KEY_DL() => 'delete-line',
102             "\cY" => 'delete-line',
103             "\cX" => 'delete-line',
104             "\cK" => 'delete-till-eol',
105             KEY_DC() => 'delete-character',
106             "\cV" => 'paste',
107             "\cU" => 'clear-line',
108             KEY_BACKSPACE() => 'backspace',
109             KEY_ENTER() => 'newline',
110             );
111              
112             # Some viewbindings that should not be available in %bindings;
113             $viewbindings{'h'} = 'cursor-left';
114             $viewbindings{'j'} = 'cursor-down';
115             $viewbindings{'k'} = 'cursor-up';
116             $viewbindings{'l'} = 'cursor-right';
117              
118             sub new ()
119             {
120 1     1 1 3 my $class = shift;
121              
122 1         4 my %userargs = @_;
123 1         5 keys_to_lowercase(\%userargs);
124              
125 1         45 my %args = (
126             # Parent info
127             -parent => undef, # the parent object
128              
129             # Position and size
130             -x => 0, # horizontal position (rel. to -window)
131             -y => 0, # vertical position (rel. to -window)
132             -width => undef, # horizontal editsize, undef = stretch
133             -height => undef, # vertical editsize, undef = stretch
134             -singleline => 0, # single line mode or not?
135              
136             # Initial state
137             -text => '', # data
138             -pos => 0, # cursor position
139              
140             # General options
141             -border => undef, # use border?
142             -showlines => undef, # underline lines?
143             -sbborder => undef, # square bracket border?
144             -undolevels => 10, # number of undolevels. 0 = infinite
145             -maxlength => 0, # the maximum length. 0 = infinite
146             -showoverflow => 1, # show overflow characters.
147             -regexp => undef, # regexp to match the text against
148             -toupper => 0, # convert text to uppercase?
149             -tolower => 0, # convert text to lowercase?
150             -homeonblur => 0, # cursor to homepos on blur?
151             -vscrollbar => 0, # show vertical scrollbar
152             -hscrollbar => 0, # show horizontal scrollbar
153             -readonly => 0, # only used as viewer?
154             -reverse => 0, # show in reverse
155              
156             # Single line options
157             -password => undef, # masquerade chars with given char
158              
159             # Multiple line options
160             -showhardreturns => 0, # show hard returns with diamond char?
161             -wrapping => 0, # do wrap?
162             -maxlines => undef, # max lines. undef = infinite
163            
164              
165             # Events
166             -onchange => undef, # onChange event handler
167            
168             # Color
169             -bg => -1,
170             -fg => -1,
171              
172              
173             %userargs,
174            
175             -routines => {%routines}, # binding routines
176             -bindings => {}, # these are set by readonly()
177              
178             # Init values
179             -nocursor => 0,
180             -scr_lines => [],
181             -yscrpos => 0,
182             -xscrpos => 0,
183             -ypos => 0,
184             -xpos => 0,
185             -focus => 0,
186             );
187              
188             # Let -text always be defined.
189 1 50       7 $args{-text} = '' unless defined $args{-text};
190              
191             # If initially wrapping is on, then we do not use
192             # overflow chars.
193 1 50       5 $args{-showoverflow} = 0 if $args{-wrapping};
194              
195             # Single line mode? Compute the needed height and set it.
196 1 50       3 if ($args{-singleline})
197             {
198 0         0 my $height = height_by_windowscrheight(1,%args);
199 0         0 $args{-height} = $height;
200             }
201            
202             # Create the Widget.
203 1         24 my $this = $class->SUPER::new( %args );
204              
205             # Check if we should wrap or not.
206 1 50       5 $this->{-wrapping} = 0 if $this->{-singleline};
207              
208 1         3 $this->{-undotext} = [$this->{-text}];
209 1         3 $this->{-undopos} = [$this->{-pos}];
210 1         2 $this->{-xscrpos} = 0; # X position for cursor on screen
211 1         2 $this->{-yscrpos} = 0; # Y position for cursor on screen
212 1         1 $this->{-xpos} = 0; # X position for cursor in the document
213 1         2 $this->{-ypos} = 0; # Y position for cursor in the document
214              
215             # Restrict the password character to a single character.
216 1 50       3 $this->set_password_char($this->{-password}) if defined $this->{-password};
217              
218             # Single line? Then initial text may only be singleline.
219 1 0 33     4 if ($this->{-singleline} and
      33        
220             defined $this->{-text} and $this->{-text} =~ /\n/)
221             {
222 0         0 my $lines = $this->split_to_lines($this->{-text});
223 0         0 $this->{-text} = $lines->[0];
224             }
225              
226 1         4 $this->readonly($this->{-readonly});
227 1         3 $this->layout_content;
228              
229 1 50       3 if ($Curses::UI::ncurses_mouse) {
230 1         3 $this->set_mouse_binding('mouse-button1', BUTTON1_CLICKED());
231             }
232              
233 1         7 return $this;
234             }
235              
236             sub getrealxpos()
237             {
238 4     4 0 5 my $this = shift;
239              
240 4         11 my $offset = $this->{-xscrpos};
241 4         6 my $length = $this->{-xpos} - $this->{-xscrpos};
242 4 50       11 return 0 if $length <= 0;
243              
244 0         0 my $current_line = $this->{-scr_lines}->[$this->{-ypos}];
245 0         0 my $before_cursor = substr(
246             $current_line,
247             $this->{-xscrpos}, # Screen's x position
248             $this->{-xpos} - $this->{-xscrpos} # Space up to the cursor
249             );
250              
251 0         0 my $realxpos = scrlength($before_cursor);
252              
253 0         0 return $realxpos;
254             }
255              
256             sub layout()
257             {
258 1     1 1 2 my $this = shift;
259 1 50       8 $this->SUPER::layout() or return;
260              
261             # Scroll up if we can and the number of visible lines
262             # is smaller than the number of available lines in the screen.
263 1         20 my $inscreen = ($this->canvasheight
264             - ($this->number_of_lines
265             - $this->{-yscrpos}));
266 1   33     6 while ($this->{-yscrpos} > 0 and
267             $inscreen < $this->canvasheight)
268             {
269 0         0 $this->{-yscrpos}--;
270 0         0 $inscreen = ($this->canvasheight
271             - ($this->number_of_lines
272             - $this->{-yscrpos}));
273             }
274            
275             # Scroll left if we can and the number of visible columns
276             # is smaller than the number of available columns in the screen.
277 1         6 $inscreen = ($this->canvaswidth
278             - ($this->number_of_columns
279             - $this->{-xscrpos}));
280 1   33     6 while ($this->{-xscrpos} > 0 and $inscreen < $this->canvaswidth)
281             {
282 0         0 $this->{-xscrpos}--;
283 0         0 $inscreen = ($this->canvaswidth
284             - ($this->number_of_columns
285             - $this->{-xscrpos}));
286             }
287              
288 1         3 $this->layout_content();
289 1         2 return $this;
290             }
291              
292             sub layout_content()
293             {
294 4     4 1 7 my $this = shift;
295 4 50       9 return $this if $Curses::UI::screen_too_small;
296            
297             # ----------------------------------------------------------------------
298             # Build an array of lines to display and determine the cursor position
299             # ----------------------------------------------------------------------
300              
301 4         32 my $lines_src = $this->split_to_lines($this->{-text});
302 4         8 foreach (@$lines_src) {$_ .= "\n"}
  4         11  
303 4         14 $lines_src->[-1] =~ s/\n$/ /;
304            
305             # No lines available? Then create an array.
306 4 50       9 $lines_src = [""] unless @$lines_src;
307              
308             # No out of bound values for -pos.
309 4 50       12 $this->{-pos} = 0 unless defined $this->{-pos};
310 4 50       23 $this->{-pos} = 0 if $this->{-pos} < 0;
311 4 100       14 $this->{-pos} = length($this->{-text})
312             if $this->{-pos} >= length($this->{-text});
313              
314             # Do line wrapping if needed and store the lines
315             # to display in -scr_lines. Compute the x- and
316             # y-position of the cursor in the text.
317 4         5 my $lines = [];
318 4         7 my ($xpos, $ypos, $trackpos) = (undef, 0, 0);
319 4         7 foreach my $line (@$lines_src)
320             {
321 4         6 my $add = [];
322 4 50       10 if ($this->{-wrapping}) {
323 0         0 $add = $this->text_wrap($line, $this->canvaswidth, WORDWRAP);
324             } else {
325 4         16 $add = [$line];
326             }
327 4         15 push @$lines, @$add;
328            
329 4 50       8 unless (defined $xpos)
330             {
331 4         7 foreach (@$add)
332             {
333 4         6 my $newtrackpos = $trackpos + length($_);
334 4 50       10 if ( $this->{-pos} < $newtrackpos )
335             {
336 4         12 $xpos = length(substr($_, 0, ($this->{-pos}-$trackpos)));
337             }
338 4         5 $trackpos = $newtrackpos;
339 4 50       16 last if defined $xpos;
340 0         0 $ypos++;
341             }
342             }
343             }
344            
345 4         9 $this->{-scr_lines} = $lines;
346 4 50       19 unless ($this->{-readonly})
347             {
348 4         6 $this->{-xpos} = $xpos;
349 4         7 $this->{-ypos} = $ypos;
350             }
351              
352             # ----------------------------------------------------------------------
353             # Handle vertical scrolling of the screen
354             # ----------------------------------------------------------------------
355              
356             # Scroll down if needed.
357 4 50       14 if ( ($this->{-ypos}-$this->{-yscrpos}) >= $this->canvasheight ) {
    50          
358 0         0 $this->{-yscrpos} = $this->{-ypos} - $this->canvasheight + 1;
359             }
360              
361             # Scroll up if needed.
362             elsif ( $this->{-ypos} < $this->{-yscrpos} ) {
363 0         0 $this->{-yscrpos} = $this->{-ypos};
364             }
365              
366             # Check bounds.
367 4 50       9 $this->{-yscrpos} = 0 if $this->{-yscrpos} < 0;
368 4 50       12 $this->{-yscrpos} = @$lines if $this->{-yscrpos} > @$lines;
369              
370              
371             # ----------------------------------------------------------------------
372             # Handle horizontal scrolling of the screen
373             # ----------------------------------------------------------------------
374              
375             # If wrapping is enabled, then check for horizontal scrolling.
376             # Else make the -xscrpos fixed to 0.
377 4 50       10 unless ($this->{-readonly})
378             {
379 4 50       9 unless ($this->{-wrapping})
380             {
381 4         8 my $realxpos = $this->getrealxpos;
382            
383             # If overflows have to be shown, the cursor may not
384             # be set to the first or the last position of the
385             # screen.
386 4 50 33     23 my $wrapborder =
387             (not $this->{-wrapping} and $this->{-showoverflow})
388             ? 1 : 0;
389            
390             # Scroll left if needed.
391 4 50       9 if ($realxpos < $wrapborder) {
392 4   33     11 while ($realxpos < ($wrapborder + int($this->canvaswidth/3))
393             and $this->{-xscrpos} > 0) {
394 0         0 $this->{-xscrpos}--;
395 0         0 $realxpos = $this->getrealxpos;
396             }
397             }
398            
399             # Scroll right if needed.
400 4 50       10 if ($realxpos > ($this->canvaswidth - 1 - $wrapborder)) {
401 0         0 while ($realxpos > 2*int($this->canvaswidth/3) ) {
402 0         0 $this->{-xscrpos}++;
403 0         0 $realxpos = $this->getrealxpos;
404             }
405             }
406             }
407             else
408             {
409 0         0 $this->{-xscrpos} = 0;
410             }
411             }
412              
413             # Check bounds.
414 4 50       9 $this->{-xscrpos} = 0 if $this->{-xscrpos} < 0;
415 4 50       10 $this->{-xscrpos} = $this->{-xpos} if $this->{-xscrpos} > $this->{-xpos};
416              
417             # ----------------------------------------------------------------------
418             # Layout horizontal scrollbar.
419             # ----------------------------------------------------------------------
420              
421 4 50 33     25 if (($this->{-hscrollbar} and not $this->{-wrapping}) or $this->{-readonly})
      33        
422             {
423 0         0 my $longest_line = $this->number_of_columns;
424 0         0 $this->{-hscrolllen} = $longest_line + 1;
425 0         0 $this->{-hscrollpos} = $this->{-xscrpos};
426             } else {
427 4         5 $this->{-hscrolllen} = 0;
428 4         7 $this->{-hscrollpos} = 0;
429             }
430              
431            
432             # ----------------------------------------------------------------------
433             # Layout vertical scrollbar
434             # ----------------------------------------------------------------------
435              
436 4 50 33     31 if ($this->{-vscrollbar} or $this->{-readonly})
437             {
438 0         0 $this->{-vscrolllen} = @{$this->{-scr_lines}};
  0         0  
439 0         0 $this->{-vscrollpos} = $this->{-yscrpos};
440             } else {
441 4         6 $this->{-vscrolllen} = 0;
442 4         7 $this->{-vscrollpos} = 0;
443             }
444              
445 4         7 return $this;
446             }
447              
448             sub draw_text(;$)
449             {
450 1     1 0 2 my $this = shift;
451 1   50     3 my $no_doupdate = shift || 0;
452 1 50       3 return $this if $Curses::UI::screen_too_small;
453              
454             # Return immediately if this object is hidden.
455 1 50       3 return $this if $this->hidden;
456              
457             # Turn on underlines and fill the screen with lines
458             # if neccessary.
459 1 50 33     7 if ($this->{-showlines} or $this->{-reverse})
460             {
461 0 0       0 $this->{-canvasscr}->attron(A_UNDERLINE) if ($this->{-showlines});;
462 0 0       0 $this->{-canvasscr}->attron(A_REVERSE) if ($this->{-reverse});
463 0         0 for my $y (0..$this->canvasheight-1) {
464 0         0 $this->{-canvasscr}->addstr($y, 0, " "x($this->canvaswidth));
465             }
466             }
467              
468             # Draw the text.
469 1         3 for my $id (0 .. $this->canvasheight - 1)
470             {
471             # Let there be color
472 2 50       17 if ($Curses::UI::color_support) {
473 0         0 my $co = $Curses::UI::color_object;
474 0         0 my $pair = $co->get_color_pair(
475             $this->{-fg},
476             $this->{-bg});
477              
478 0         0 $this->{-canvasscr}->attron(COLOR_PAIR($pair));
479              
480             }
481              
482 2 50 33     8 if (defined $this->{-search_highlight}
483             and $this->{-search_highlight} == ($id+$this->{-yscrpos})) {
484 0 0       0 $this->{-canvasscr}->attron(A_REVERSE) if (not $this->{-reverse});
485 0 0       0 $this->{-canvasscr}->attroff(A_REVERSE) if ($this->{-reverse});
486             } else {
487 2 50       19 $this->{-canvasscr}->attroff(A_REVERSE) if (not $this->{-reverse});
488 2 50       399 $this->{-canvasscr}->attron(A_REVERSE) if ($this->{-reverse});
489             }
490              
491 2         6 my $l = $this->{-scr_lines}->[$id + $this->{-yscrpos}];
492 2 100       6 if (defined $l)
493             {
494             # Get the part of the line that is in view.
495 1         2 my $inscreen = '';
496 1         2 my $fromxscr = '';
497 1 50       3 if ($this->{-xscrpos} < length($l))
498             {
499 1         3 $fromxscr = substr($l, $this->{-xscrpos}, length($l));
500 1         5 $inscreen = ($this->text_wrap(
501             $fromxscr,
502             $this->canvaswidth,
503             NO_WORDWRAP))->[0];
504             }
505              
506             # Masquerading of password fields.
507 1 50 33     6 if ($this->{-singleline} and defined $this->{-password})
508             {
509             # Don't masq the endspace which we
510             # added ourselves.
511 0         0 $inscreen =~ s/\s$//;
512            
513             # Substitute characters.
514 0         0 $inscreen =~ s/[^\n]/$this->{-password}/g;
515             }
516              
517             # Clear line.
518 1         5 $this->{-canvasscr}->addstr(
519             $id, 0,
520             " "x$this->canvaswidth
521             );
522              
523             # Strip newline and replace by diamond character
524             # if the showhardreturns option is on.
525 1 50       187 if ($inscreen =~ /\n/)
526             {
527 0         0 $inscreen =~ s/\n//;
528 0         0 $this->{-canvasscr}->addstr($id, 0, $inscreen);
529 0 0       0 if ($this->{-showhardreturns})
530             {
531 0 0       0 if ($this->root->compat)
532             {
533 0         0 $this->{-canvasscr}->addch($id, scrlength($inscreen),'@');
534             } else {
535 0         0 $this->{-canvasscr}->attron(A_ALTCHARSET);
536 0         0 $this->{-canvasscr}->addch($id, scrlength($inscreen),'`');
537 0         0 $this->{-canvasscr}->attroff(A_ALTCHARSET);
538             }
539             }
540             } else {
541 1         5 $this->{-canvasscr}->addstr($id, 0, $inscreen);
542             }
543            
544             # Draw overflow characters.
545 1 50 33     35 if (not $this->{-wrapping} and $this->{-showoverflow})
546             {
547 1 50       4 $this->{-canvasscr}->addch($id, $this->canvaswidth-1, '$')
548             if $this->canvaswidth < scrlength($fromxscr);
549 1 50       161 $this->{-canvasscr}->addch($id, 0, '$')
550             if $this->{-xscrpos} > 0;
551             }
552              
553             } else {
554 1         2 last;
555             }
556             }
557              
558             # Move the cursor.
559             # Take care of TAB's
560 1 50       4 if ($this->{-readonly})
561             {
562 0         0 $this->{-canvasscr}->move(
563             $this->canvasheight-1,
564             $this->canvaswidth-1
565             );
566             } else {
567 1         5 my $l = $this->{-scr_lines}->[$this->{-ypos}];
568 1         5 my $precursor = substr(
569             $l,
570             $this->{-xscrpos},
571             $this->{-xpos} - $this->{-xscrpos}
572             );
573              
574 1         2 my $realxpos = scrlength($precursor);
575 1         9 $this->{-canvasscr}->move(
576             $this->{-ypos} - $this->{-yscrpos},
577             $realxpos
578             );
579             }
580            
581 1 50       162 $this->{-canvasscr}->attroff(A_UNDERLINE) if $this->{-showlines};
582 1 50       4 $this->{-canvasscr}->attroff(A_REVERSE) if $this->{-reverse};
583 1         5 $this->{-canvasscr}->noutrefresh();
584 1 50       4 doupdate() unless $no_doupdate;
585 1         1 return $this;
586             }
587              
588             sub draw(;$)
589             {
590 1     1 1 2 my $this = shift;
591 1   50     3 my $no_doupdate = shift || 0;
592              
593 1 50       7 $this->SUPER::draw(1) or return $this;
594              
595 1         2 $this->layout_content;
596 1         3 $this->draw_text(1);
597 1 50       3 doupdate() unless $no_doupdate;
598              
599 1         2 return $this;
600             }
601              
602             sub event_onblur()
603             {
604 0     0 0 0 my $this = shift;
605            
606 0         0 $this->SUPER::event_onblur;
607              
608             # Set the cursor position to the startposition
609             # if -homeonblur is set.
610 0 0       0 if ($this->{-homeonblur}) {
611 0         0 $this->cursor_to_home;
612 0         0 $this->layout_content;
613             }
614              
615 0         0 return $this;
616             }
617              
618             sub event_keypress ($;)
619             {
620 0     0 0 0 my $this = shift;
621 0         0 my $key = shift;
622              
623             # Reset the field that tracks if undoinfo has already
624             # been saved or not.
625 0         0 $this->resetsetundo();
626              
627             # Pasting more than one char/line is possible. As long
628             # as you do it at once (no other actions in between are
629             # allowed).
630 0 0 0     0 if (defined $this->{-prevkey} and $this->{-prevkey} ne $key) {
631 0         0 $this->do_new_pastebuffer(1);
632             } else {
633 0         0 $this->do_new_pastebuffer(0);
634             }
635              
636             # Backup, in case illegal input is done.
637 0         0 my %backup = %{$this};
  0         0  
638              
639             # Process bindings.
640 0         0 my $ret = $this->process_bindings($key);
641            
642             # Did the widget loose focus, due to the keypress?
643 0 0       0 return $this unless $this->{-focus};
644              
645             # To upper or to lower?
646 0 0       0 if ($this->{-toupper}) {
    0          
647 0         0 $this->{-text} = uc $this->{-text};
648             } elsif ($this->{-tolower}) {
649 0         0 $this->{-text} = lc $this->{-text};
650             }
651              
652             # Check for illegal input.
653 0         0 my $is_illegal = 0;
654 0 0       0 if ($this->{-maxlength}) {
655 0 0       0 $is_illegal = 1 if length($this->{-text}) > $this->{-maxlength};
656             }
657 0 0 0     0 if (not $is_illegal and defined $this->{-maxlines}) {
658 0         0 my $lines = $this->split_to_lines($this->{-text});
659 0 0       0 $is_illegal = 1 if @$lines > $this->{-maxlines};
660             }
661 0 0 0     0 if (not $is_illegal and defined $this->{-regexp}) {
662 0         0 my $e = '$is_illegal = (not $this->{-text} =~ ' . $this->{-regexp} . ')';
663 0         0 eval $e;
664             }
665            
666 0 0       0 if ($is_illegal) # Illegal input? Then restore and bail out.
667             {
668 0         0 while (my ($k,$v) = each %backup) {
669 0         0 $this->{$k} = $v;
670             }
671 0         0 $this->dobeep();
672             } else { # Legal input? Redraw the text.
673 0         0 $this->run_event('-onchange');
674 0         0 $this->draw(1);
675             }
676              
677             # Save the current key.
678 0         0 $this->{-prevkey} = $key;
679              
680 0         0 return $ret;
681             }
682              
683             sub add_string($;)
684             {
685 0     0 0 0 my $this = shift;
686 0         0 my $ch = shift;
687              
688 0         0 my @ch = split //, $ch;
689 0         0 $ch = '';
690 0         0 foreach (@ch) {
691 0         0 $ch .= $this->key_to_ascii($_);
692             }
693              
694 0         0 $this->set_undoinfo;
695              
696 0         0 PASTED: for (;;)
697             {
698 0         0 my $binding = $this->{-bindings}->{$ch};
699 0 0       0 $binding = 'add-string' unless defined $binding;
700              
701 0 0       0 if ($ch eq "-1") {
    0          
    0          
702 0         0 last PASTED;
703             } elsif ( $binding eq 'add-string' ) {
704 0         0 substr($this->{-text}, $this->{-pos}, 0) = $ch;
705 0         0 $this->{-pos} += length($ch);
706             } elsif ( $binding eq 'newline' ) {
707 0         0 $this->process_bindings($ch);
708             }
709              
710             # Multiple characters at input? This is probably a
711             # pasted string. Get it and process it. Don't do
712             # special bindings, but only add-string and newline.
713 0         0 $ch = $this->get_key(0);
714             }
715              
716 0         0 $this->layout_content;
717 0         0 $this->set_curxpos;
718 0         0 return $this;
719             }
720              
721             sub toggle_showoverflow()
722             {
723 0     0 1 0 my $this = shift;
724 0         0 $this->{-showoverflow} = ! $this->{-showoverflow};
725 0         0 return $this;
726             }
727              
728             sub toggle_wrapping()
729             {
730 0     0 1 0 my $this = shift;
731 0 0       0 return $this->dobeep if $this->{-singleline};
732 0         0 $this->{-wrapping} = ! $this->{-wrapping};
733 0         0 $this->layout;
734 0         0 return $this;
735             }
736              
737             sub toggle_showhardreturns()
738             {
739 0     0 1 0 my $this = shift;
740 0 0       0 return $this->dobeep if $this->{-singleline};
741 0         0 $this->{-showhardreturns} = ! $this->{-showhardreturns};
742 0         0 return $this;
743             }
744              
745             sub cursor_right()
746             {
747 0     0 0 0 my $this = shift;
748            
749             # Handle cursor_right for read only mode.
750 0 0       0 if ($this->{-readonly})
751             {
752 0 0       0 return $this->dobeep
753             unless defined $this->{-hscrolllen};
754              
755 0 0       0 return $this->dobeep
756             if $this->{-xscrpos}
757             >= $this->{-hscrolllen} - $this->canvaswidth;
758              
759 0         0 $this->{-xscrpos} += 1;
760 0         0 $this->{-hscrollpos} = $this->{-xscrpos};
761 0         0 $this->{-xpos} = $this->{-xscrpos};
762              
763 0         0 return $this;
764             }
765              
766 0 0       0 if ($this->{-pos} == length($this->{-text})) {
767 0         0 $this->dobeep;
768             } else {
769 0         0 $this->{-pos}++;
770             }
771 0         0 $this->layout_content;
772 0         0 $this->set_curxpos;
773 0         0 return $this;
774             }
775              
776             sub cursor_left()
777             {
778 0     0 0 0 my $this = shift;
779            
780             # Handle cursor_left for read only mode.
781 0 0       0 if ($this->{-readonly})
782             {
783 0 0       0 return $this->dobeep if $this->{-xscrpos} <= 0;
784 0         0 $this->{-xscrpos} -= 1;
785 0         0 $this->{-xpos} = $this->{-xscrpos};
786 0         0 return $this;
787             }
788              
789 0 0       0 if ($this->{-pos} <= 0) {
790 0         0 $this->dobeep;
791             } else {
792 0         0 $this->{-pos}--;
793             }
794 0         0 $this->layout_content;
795 0         0 $this->set_curxpos;
796 0         0 return $this;
797             }
798            
799             sub set_curxpos()
800             {
801 0     0 0 0 my $this = shift;
802 0         0 $this->{-curxpos} = $this->{-xpos};
803 0         0 return $this;
804             }
805            
806             sub cursor_up(;$)
807             {
808 0     0 0 0 my $this = shift;
809 0         0 shift; # stub for bindings handling.
810 0   0     0 my $amount = shift || 1;
811            
812 0 0       0 return $this->dobeep if $this->{-singleline};
813            
814             # Handle cursor_up for read only mode.
815 0 0       0 if ($this->{-readonly})
816             {
817 0 0       0 return $this->dobeep if $this->{-yscrpos} <= 0;
818 0         0 $this->{-yscrpos} -= $amount;
819 0 0       0 $this->{-yscrpos} = 0 if $this->{-yscrpos} < 0;
820 0         0 $this->{-ypos} = $this->{-yscrpos};
821 0         0 return $this;
822             }
823              
824 0         0 my $maymove = $this->{-ypos};
825 0 0       0 return $this->dobeep unless $maymove;
826 0 0       0 $amount = $maymove if $amount > $maymove;
827              
828 0         0 my $l = $this->{-scr_lines};
829 0         0 $this->cursor_to_scrlinestart;
830 0         0 $this->{-ypos} -= $amount;
831 0         0 while ($amount)
832             {
833 0         0 my $idx = $this->{-ypos} + $amount - 1;
834 0         0 my $line = $l->[$idx];
835 0         0 my $line_length = length($line);
836 0         0 $this->{-pos} -= $line_length;
837 0         0 $amount--;
838             }
839 0         0 $this->cursor_to_curxpos;
840              
841 0         0 return $this;
842             }
843              
844             sub cursor_pageup()
845             {
846 0     0 0 0 my $this = shift;
847              
848 0 0       0 return $this->dobeep if $this->{-singleline};
849 0         0 $this->cursor_up(undef, $this->canvasheight - 1);
850              
851 0         0 return $this;
852             }
853            
854             sub cursor_down($;)
855             {
856 0     0 0 0 my $this = shift;
857 0         0 shift; # stub for bindings handling.
858 0   0     0 my $amount = shift || 1;
859            
860 0 0       0 return $this->dobeep if $this->{-singleline};
861            
862             # Handle cursor_down for read only mode.
863 0 0       0 if ($this->{-readonly})
864             {
865 0         0 my $max = @{$this->{-scr_lines}} - $this->canvasheight;
  0         0  
866 0 0       0 return $this->dobeep
867             if $this->{-yscrpos} >= $max;
868              
869 0         0 $this->{-yscrpos} += $amount;
870 0 0       0 $this->{-yscrpos} = $max if $this->{-yscrpos} > $max;
871 0         0 $this->{-ypos} = $this->{-yscrpos};
872 0         0 return $this;
873             }
874            
875 0         0 my $l = $this->{-scr_lines};
876 0         0 my $maymove = (@$l-1) - $this->{-ypos};
877 0 0       0 return $this->dobeep unless $maymove;
878 0 0       0 $amount = $maymove if $amount > $maymove;
879            
880 0         0 $this->cursor_to_scrlinestart;
881 0         0 $this->{-ypos} += $amount;
882 0         0 while ($amount)
883             {
884 0         0 my $idx = $this->{-ypos} - $amount;
885 0         0 my $line = $l->[$idx];
886 0         0 my $line_length = length($line);
887 0         0 $this->{-pos} += $line_length;
888 0         0 $amount--;
889             }
890 0         0 $this->cursor_to_curxpos;
891              
892 0         0 return $this;
893             }
894              
895             sub cursor_pagedown()
896             {
897 0     0 0 0 my $this = shift;
898 0 0       0 return $this->dobeep if $this->{-singleline};
899            
900 0         0 $this->cursor_down(undef, $this->canvasheight - 1);
901              
902 0         0 return $this;
903             }
904              
905             sub cursor_to_home()
906             {
907 0     0 0 0 my $this = shift;
908            
909 0 0       0 if ($this->{-readonly})
910             {
911 0         0 $this->{-xscrpos} = $this->{-xpos} = 0;
912 0         0 $this->{-yscrpos} = $this->{-ypos} = 0;
913 0         0 return $this;
914             }
915            
916 0         0 $this->{-pos} = 0;
917 0         0 $this->set_curxpos;
918 0         0 return $this;
919             }
920              
921             sub cursor_to_end()
922             {
923 0     0 0 0 my $this = shift;
924              
925 0 0       0 if ($this->{-readonly})
926             {
927 0         0 $this->{-xscrpos} = $this->{-xpos} = 0;
928 0         0 $this->{-yscrpos} = $this->{-ypos} =
929             $this->{-vscrolllen}-$this->canvasheight;
930 0         0 return $this;
931             }
932            
933 0         0 $this->{-pos} = length($this->{-text});
934 0         0 $this->set_curxpos;
935 0         0 return $this;
936             }
937              
938             sub cursor_to_scrlinestart()
939             {
940 0     0 0 0 my $this = shift;
941             # Key argument is set if called from binding.
942 0         0 my $from_binding = shift;
943            
944 0 0       0 if ($this->{-readonly})
945             {
946 0         0 $this->{-xscrpos} = $this->{-xpos} = 0;
947 0         0 return $this;
948             }
949              
950 0         0 $this->{-pos} -= $this->{-xpos};
951 0         0 $this->{-xpos} = 0;
952 0 0       0 $this->set_curxpos if defined $from_binding;
953 0         0 return $this;
954             }
955            
956             sub cursor_to_scrlineend()
957             {
958 0     0 0 0 my $this = shift;
959 0         0 my $from_binding = shift;
960            
961 0 0       0 if ($this->{-readonly})
962             {
963 0         0 $this->{-xscrpos} = $this->{-xpos} =
964             $this->{-hscrolllen} - $this->canvaswidth ;
965 0         0 return $this;
966             }
967              
968 0         0 my $newpos = $this->{-pos};
969 0         0 my $l = $this->{-scr_lines};
970 0         0 my $len = length($l->[$this->{-ypos}]) - 1;
971 0         0 $newpos += $len - $this->{-xpos};
972 0         0 $this->{-pos} = $newpos;
973 0         0 $this->layout_content;
974 0 0       0 $this->set_curxpos if defined $from_binding;
975 0         0 return $this;
976             }
977              
978             sub cursor_to_linestart()
979             {
980 0     0 0 0 my $this = shift;
981              
982             # Move cursor back, until \n is found. That is
983             # the previous line. Then go one position to the
984             # right to find the start of the line.
985 0         0 my $newpos = $this->{-pos};
986 0         0 for(;;)
987             {
988 0 0       0 last if $newpos <= 0;
989 0         0 $newpos--;
990 0 0       0 last if substr($this->{-text}, $newpos, 1) eq "\n";
991             }
992 0 0       0 $newpos++ unless $newpos == 0;
993 0 0       0 $newpos = length($this->{-text}) if $newpos > length($this->{-text});
994 0         0 $this->{-pos} = $newpos;
995 0         0 $this->layout_content;
996 0         0 return $this;
997             }
998              
999             sub cursor_to_curxpos()
1000             {
1001 0     0 0 0 my $this = shift;
1002 0         0 my $right = $this->{-curxpos};
1003 0 0       0 $right = 0 unless defined $right;
1004 0         0 my $len = length($this->{-scr_lines}->[$this->{-ypos}]) - 1;
1005 0 0       0 if ($right > $len) { $right = $len }
  0         0  
1006 0         0 $this->{-pos} += $right;
1007 0         0 $this->layout_content;
1008 0         0 return $this;
1009             }
1010              
1011             sub clear_line()
1012             {
1013 0     0 0 0 my $this = shift;
1014 0         0 $this->cursor_to_linestart;
1015 0         0 $this->delete_till_eol;
1016 0         0 return $this;
1017             }
1018              
1019             sub delete_line()
1020             {
1021 0     0 0 0 my $this = shift;
1022 0 0       0 return $this->dobeep if $this->{-singleline};
1023              
1024 0         0 my $len = length($this->{-text});
1025 0 0       0 if ($len == 0)
1026             {
1027 0         0 $this->dobeep;
1028 0         0 return $this;
1029             }
1030              
1031             $this->beep_off
1032             ->cursor_to_linestart
1033             ->delete_till_eol
1034             ->cursor_left
1035             ->delete_character
1036             ->cursor_right
1037             ->cursor_to_linestart
1038             ->set_curxpos
1039 0         0 ->beep_on;
1040 0         0 return $this;
1041             }
1042              
1043             sub delete_till_eol()
1044             {
1045 0     0 0 0 my $this = shift;
1046            
1047 0         0 $this->set_undoinfo;
1048            
1049             # Cursor is at newline. No action needed.
1050 0 0       0 return $this if substr($this->{-text}, $this->{-pos}, 1) eq "\n";
1051              
1052             # Find the next newline. Delete the content up to that newline.
1053 0         0 my $pos = $this->{-pos};
1054 0         0 for(;;)
1055             {
1056 0         0 $pos++;
1057 0 0       0 last if $pos >= length($this->{-text});
1058 0 0       0 last if substr($this->{-text}, $pos, 1) eq "\n";
1059             }
1060              
1061 0         0 $this->add_to_pastebuffer(
1062             substr($this->{-text}, $this->{-pos}, $pos - $this->{-pos})
1063             );
1064 0         0 substr($this->{-text}, $this->{-pos}, $pos - $this->{-pos}, '');
1065 0         0 return $this;
1066             }
1067            
1068             sub delete_character()
1069             {
1070 0     0 0 0 my $this = shift;
1071 0         0 shift(); # stub for bindings handling.
1072 0         0 my $is_backward = shift;
1073            
1074 0 0       0 if ($this->{-pos} >= length($this->{-text})) {
1075 0         0 $this->dobeep;
1076             } else {
1077 0         0 $this->set_undoinfo;
1078 0         0 $this->add_to_pastebuffer(
1079             substr($this->{-text}, $this->{-pos}, 1),
1080             $is_backward
1081             );
1082 0         0 substr($this->{-text}, $this->{-pos}, 1, ''),
1083             }
1084 0         0 return $this;
1085             }
1086              
1087             sub backspace()
1088             {
1089 0     0 0 0 my $this = shift;
1090            
1091 0 0       0 if ($this->{-pos} <= 0) {
1092 0         0 $this->dobeep;
1093             } else {
1094 0         0 $this->set_undoinfo;
1095 0         0 $this->{-pos}--;
1096 0         0 $this->delete_character(undef,1);
1097 0         0 $this->layout_content;
1098 0         0 $this->set_curxpos;
1099             }
1100 0         0 return $this;
1101             }
1102              
1103             sub newline()
1104             {
1105 0     0 0 0 my $this = shift;
1106 0 0       0 return $this->dobeep if $this->{-singleline};
1107 0         0 $this->add_string("\n");
1108             }
1109              
1110             sub mouse_button1($$$$;)
1111             {
1112 0     0 0 0 my $this = shift;
1113 0         0 my $event = shift;
1114 0         0 my $x = shift;
1115 0         0 my $y = shift;
1116              
1117 0 0       0 return unless $this->{-focusable};
1118              
1119             # TODO: make this possible for multi line widgets.
1120 0 0       0 if ($this->{-singleline})
1121             {
1122 0         0 $this->{-pos} = $this->{-xscrpos} + $x;
1123 0         0 $this->layout_content;
1124 0         0 $this->set_curxpos;
1125             }
1126 0         0 $this->focus();
1127              
1128 0         0 return $this;
1129             }
1130              
1131 0     0 0 0 sub resetsetundo() { shift()->{-didsetundo} = 0}
1132 0     0 0 0 sub didsetundo() { shift()->{-didsetundo} }
1133              
1134             sub set_undoinfo()
1135             {
1136 0     0 0 0 my $this = shift;
1137              
1138 0 0       0 return $this if $this->didsetundo;
1139              
1140 0         0 push @{$this->{-undotext}}, $this->{-text};
  0         0  
1141 0         0 push @{$this->{-undopos}}, $this->{-pos};
  0         0  
1142              
1143 0         0 my $l = $this->{-undolevels};
1144 0 0 0     0 if ($l and @{$this->{-undotext}} > $l) {
  0         0  
1145 0         0 splice(@{$this->{-undotext}}, 0, @{$this->{-undotext}}-$l, ());
  0         0  
  0         0  
1146 0         0 splice(@{$this->{-undopos}}, 0, @{$this->{-undopos}}-$l, ());
  0         0  
  0         0  
1147             }
1148              
1149 0         0 $this->{-didsetundo} = 1;
1150 0         0 return $this;
1151             }
1152              
1153             sub undo()
1154             {
1155 0     0 0 0 my $this = shift;
1156              
1157 0 0       0 if (@{$this->{-undotext}})
  0         0  
1158             {
1159 0         0 my $text = pop @{$this->{-undotext}};
  0         0  
1160 0         0 my $pos = pop @{$this->{-undopos}};
  0         0  
1161 0         0 $this->{-text} = $text;
1162 0         0 $this->{-pos} = $pos;
1163             }
1164 0         0 return $this;
1165             }
1166              
1167             sub do_new_pastebuffer(;$)
1168             {
1169 0     0 0 0 my $this = shift;
1170 0         0 my $value = shift;
1171 0 0       0 $this->{-do_new_pastebuffer} = $value
1172             if defined $value;
1173 0 0       0 $this->{-pastebuffer} = '' unless defined $this->{-pastebuffer};
1174 0         0 return $this->{-do_new_pastebuffer};
1175             }
1176              
1177             sub clear_pastebuffer()
1178             {
1179 0     0 0 0 my $this = shift;
1180 0         0 $this->{-pastebuffer} = '';
1181 0         0 return $this;
1182             }
1183              
1184             sub add_to_pastebuffer($;)
1185             {
1186 0     0 0 0 my $this = shift;
1187 0         0 my $add = shift;
1188 0   0     0 my $is_backward = shift || 0;
1189              
1190 0 0       0 $this->clear_pastebuffer if $this->do_new_pastebuffer;
1191 0 0       0 if ($is_backward) {
1192 0         0 $this->{-pastebuffer} = $add . $this->{-pastebuffer};
1193             } else {
1194 0         0 $this->{-pastebuffer} .= $add;
1195             }
1196 0         0 $this->do_new_pastebuffer(0);
1197 0         0 return $this;
1198             }
1199              
1200             sub paste()
1201             {
1202 0     0 0 0 my $this = shift;
1203            
1204 0 0       0 if ($this->{-pastebuffer} ne '') {
1205 0         0 $this->add_string($this->{-pastebuffer});
1206             }
1207 0         0 return $this;
1208             }
1209              
1210             sub readonly($;)
1211             {
1212 1     1 0 2 my $this = shift;
1213 1         1 my $readonly = shift;
1214              
1215 1         2 $this->{-readonly} = $readonly;
1216            
1217 1 50       2 if ($readonly)
1218             {
1219 0         0 my %mybindings = (
1220             %basebindings,
1221             %viewbindings
1222             );
1223 0         0 $this->{-bindings} = \%mybindings;
1224 0         0 $this->{-nocursor} = 1;
1225             } else {
1226 1         17 my %mybindings = (
1227             %basebindings,
1228             %editbindings
1229             );
1230 1         3 $this->{-bindings} = \%mybindings;
1231 1         3 $this->{-nocursor} = 0;
1232             }
1233              
1234 1         1 return $this;
1235             }
1236              
1237 1     1 1 7 sub get() {shift()->text}
1238              
1239             sub pos(;$)
1240             {
1241 0     0 0 0 my $this = shift;
1242 0         0 my $pos = shift;
1243 0 0       0 if (defined $pos)
1244             {
1245 0         0 $this->{-pos} = $pos;
1246 0         0 $this->layout_content;
1247 0         0 $this->intellidraw;
1248 0         0 return $this;
1249             }
1250 0         0 return $this->{-pos};
1251             }
1252              
1253             sub text(;$)
1254             {
1255 2     2 1 724 my $this = shift;
1256 2         3 my $text = shift;
1257 2 100       13 if (defined $text)
1258             {
1259 1         3 $this->{-text} = $text;
1260 1         3 $this->layout_content;
1261 1         9 $this->intellidraw;
1262 1         2 return $this;
1263             }
1264 1         7 return $this->{-text};
1265             }
1266              
1267 0     0 1 0 sub onChange(;$) { shift()->set_event('-onchange', shift()) }
1268              
1269             sub set_password_char {
1270 0     0 1 0 my ($this, $char) = @_;
1271 0 0       0 $char = substr($char, 0, 1) if defined $char;
1272 0         0 $this->{-password} = $char;
1273             }
1274              
1275             # ----------------------------------------------------------------------
1276             # Routines for search support
1277             # ----------------------------------------------------------------------
1278              
1279 1     1 1 2 sub number_of_lines() { @{shift()->{-scr_lines}} }
  1         4  
1280             sub number_of_columns()
1281             {
1282 1     1 0 2 my $this = shift;
1283 1         2 my $columns = 0;
1284 1         1 foreach (@{$this->{-scr_lines}}) {
  1         3  
1285 0 0       0 $columns = length($_)
1286             if length($_) > $columns;
1287             }
1288 1         3 return $columns;
1289             }
1290 0     0 1   sub getline_at_ypos($;) { shift()->{-scr_lines}->[shift()] }
1291              
1292             #
1293             # Color
1294             #
1295              
1296             sub set_color_fg {
1297 0     0 0   my $this = shift;
1298 0           $this->{-fg} = shift;
1299 0           $this->intellidraw;
1300             }
1301              
1302             sub set_color_bg {
1303 0     0 0   my $this = shift;
1304 0           $this->{-bg} = shift;
1305 0           $this->intellidraw;
1306             }
1307              
1308              
1309             1;
1310              
1311             =pod
1312              
1313             =head1 NAME
1314              
1315             Curses::UI::TextEditor - Create and manipulate texteditor widgets
1316              
1317             =head1 CLASS HIERARCHY
1318              
1319             Curses::UI::Widget
1320             Curses::UI::Searchable
1321             |
1322             +----Curses::UI::TextEditor
1323              
1324              
1325             =head1 SYNOPSIS
1326              
1327             use Curses::UI;
1328             my $cui = new Curses::UI;
1329             my $win = $cui->add('window_id', 'Window');
1330              
1331             my $editor = $win->add(
1332             'myeditor', 'TextEditor',
1333             -vscrollbar => 1,
1334             -wrapping => 1,
1335             );
1336              
1337             $editor->focus();
1338             my $text = $editor->get();
1339              
1340              
1341             =head1 DESCRIPTION
1342              
1343             Curses::UI::TextEditor is a widget that can be used to create
1344             a couple of different kinds of texteditors. These are:
1345              
1346             =over 4
1347              
1348             =item * B
1349              
1350             This is a multi-line text editor with features like word-wrapping,
1351             maximum textlength and undo.
1352              
1353             =item * B
1354              
1355             The texteditor can be created as a single-line editor.
1356             Most of the features of the default texteditor will remain.
1357             Only the multi-line specific options will not be
1358             available (like moving up and down in the text).
1359              
1360             =item * B
1361              
1362             The texteditor can also be used in read only mode.
1363             In this mode, the texteditor will function as a text
1364             viewer. The user can walk through the text and
1365             search trough it.
1366              
1367             =back
1368              
1369             See exampes/demo-Curses::UI::TextEditor in the distribution
1370             for a short demo of these.
1371              
1372              
1373              
1374             =head1 STANDARD OPTIONS
1375              
1376             B<-parent>, B<-x>, B<-y>, B<-width>, B<-height>,
1377             B<-pad>, B<-padleft>, B<-padright>, B<-padtop>, B<-padbottom>,
1378             B<-ipad>, B<-ipadleft>, B<-ipadright>, B<-ipadtop>, B<-ipadbottom>,
1379             B<-title>, B<-titlefullwidth>, B<-titlereverse>, B<-onfocus>,
1380             B<-onblur>
1381              
1382             For an explanation of these standard options, see
1383             L.
1384              
1385              
1386              
1387              
1388             =head1 WIDGET-SPECIFIC OPTIONS
1389              
1390             =over 4
1391              
1392             =item * B<-text> < TEXT >
1393              
1394             This sets the initial text for the widget to TEXT.
1395              
1396             =item * B<-pos> < CURSOR_POSITION >
1397              
1398             This sets the initial cursor position for the widget
1399             to CURSOR_POSITION. B<-pos> represents the character index within
1400             B<-text>. By default this option is set to 0.
1401              
1402             =item * B<-readonly> < BOOLEAN >
1403              
1404             The texteditor widget will be created as a read only
1405             texteditor (which is also called a textviewer) if
1406             BOOLEAN is true. By default BOOLEAN is false.
1407              
1408             =item * B<-singleline> < BOOLEAN >
1409              
1410             The texteditor widget will be created as a single line
1411             texteditor (which is also called a textentry) if
1412             BOOLEAN is true. By default BOOLEAN is false.
1413              
1414             =item * B<-wrapping> < BOOLEAN >
1415              
1416             If BOOLEAN is true, the texteditor will have text wrapping
1417             enabled. By default BOOLEAN is false.
1418              
1419             =item * B<-showlines> < BOOLEAN >
1420              
1421             If BOOLEAN is set to a true value, each editable line
1422             in the editor will show a line to type on. By default
1423             BOOLEAN is set to false.
1424              
1425             =item * B<-maxlength> < VALUE >
1426              
1427             This sets the maximum allowed length of the text to
1428             VALUE. By default VALUE is set to 0,
1429             which means that the text may be infinitely long.
1430              
1431             =item * B<-maxlines> < VALUE >
1432              
1433             This sets the maximum allowed number of lines for the text
1434             to SCALAR. By default VALUE is set to 0, which means that
1435             the text may contain an infinite number of lines.
1436              
1437             =item * B<-password> < CHARACTER >
1438              
1439             Instead of showing the real text in the widget, every
1440             character of the text will (on the screen) be replaced
1441             by CHARACTER. So creating a standard password field
1442             can be done by setting:
1443              
1444             -password => '*'
1445              
1446             =item * B<-regexp> < REGEXP >
1447              
1448             If characters are added to the texteditor, the new text
1449             will be matched against REGEXP. If the text does not match,
1450             the change will be denied. This can for example be used to
1451             force digit-only input on the texteditor:
1452              
1453             -regexp => '/^\d*$/'
1454              
1455             =item * B<-undolevels> < VALUE >
1456              
1457             This option determines how many undolevels should be kept
1458             in memory for the texteditor widget. By default 10 levels
1459             are kept. If this value is set to 0, the number of levels
1460             is infinite.
1461              
1462             =item * B<-showoverflow> < BOOLEAN >
1463              
1464             If BOOLEAN is true, the text in the texteditor will be
1465             padded by an overflow character ($) if there is text
1466             outside the screen (like 'pico' does). By default
1467             BOOLEAN is true.
1468              
1469             =item * B<-showhardreturns> < BOOLEAN >
1470              
1471             If BOOLEAN is true, hard returns will be made visible
1472             by a diamond character. By default BOOLEAN is false.
1473              
1474             =item * B<-homeonblur> < BOOLEAN >
1475              
1476             If BOOLEAN is set to a true value, the cursor will move
1477             to the start of the text if the widget loses focus.
1478              
1479             =item * B<-toupper> < BOOLEAN >
1480              
1481             If BOOLEAN is true, all entered text will be converted
1482             to uppercase. By default BOOLEAN is false.
1483              
1484             =item * B<-tolower> < BOOLEAN >
1485              
1486             If BOOLEAN is true, all entered text will be converted
1487             to lowercase. By default BOOLEAN is false.
1488              
1489             =item * B<-onchange> < CODEREF >
1490              
1491             This sets the onChange event handler for the texteditor widget.
1492             If the text is changed by typing, the code in CODEREF will
1493             be executed. It will get the widget reference as its argument.
1494              
1495             =item * B<-reverse> < BOOLEAN >
1496              
1497             Makes the text drawn in reverse font.
1498              
1499             =back
1500              
1501              
1502              
1503              
1504             =head1 METHODS
1505              
1506             =over 4
1507              
1508             =item * B ( OPTIONS )
1509              
1510             =item * B ( )
1511              
1512             =item * B ( BOOLEAN )
1513              
1514             =item * B ( )
1515              
1516             =item * B ( CODEREF )
1517              
1518             =item * B ( CODEREF )
1519              
1520             These are standard methods. See L
1521             for an explanation of these.
1522              
1523             =item * B ( [TEXT] )
1524              
1525             If TEXT is defined, this will set the text of the widget to TEXT.
1526             To see the change, the widget needs to be redrawn by the B method.
1527             If TEXT is not defined, this method will return the current contents
1528             of the texteditor.
1529              
1530             =item * B ( )
1531              
1532             This method will call B without any arguments, so it
1533             will return the contents of the texteditor.
1534              
1535             =item * B ( CODEREF )
1536              
1537             This method can be used to set the B<-onchange> event handler
1538             (see above) after initialization of the texteditor.
1539              
1540             =item * B ( $char )
1541              
1542             This method can be used to change the password property. The password
1543             character will be set to $char, or turned off in $char is undef.
1544              
1545             =item * B
1546              
1547             Toggles the -showhardreturns option.
1548              
1549             =item * B
1550              
1551             Toggles the -showoverflow option.
1552              
1553             =item * B
1554              
1555             Toggles the -wrapping option.
1556              
1557             =back
1558              
1559              
1560              
1561              
1562              
1563             =head1 DEFAULT BINDINGS
1564              
1565             There are different sets of bindings for each mode in which
1566             this widget can be used.
1567              
1568              
1569              
1570             =head2 All modes (editor, single line and read only)
1571              
1572              
1573              
1574             =over 4
1575              
1576             =item * >
1577              
1578             Call the 'returreturnn' routine. This will have the widget
1579             loose its focus.
1580              
1581             =item * >, >
1582              
1583             Call the 'cursor-left' routine: move the
1584             cursor one position to the left.
1585              
1586             =item * >, >
1587              
1588             Call the 'cursor-right' routine: move the
1589             cursor one position to the right.
1590              
1591             =item * >, >
1592              
1593             Call the 'cursor-down' routine: move the
1594             cursor one line down.
1595              
1596             =item * >, >
1597              
1598             Call the 'cursor-up' routine: move the
1599             cursor one line up.
1600              
1601             =item * >
1602              
1603             Call the 'cursor-pageup' routine: move the
1604             cursor to the previous page.
1605              
1606             =item * >
1607              
1608             Call the 'cursor-pagedown' routine: move
1609             the cursor to the next page.
1610              
1611             =item * >
1612              
1613             Call the 'cursor-home' routine: go to the
1614             start of the text.
1615              
1616             =item * >
1617              
1618             Call the 'cursor-end' routine: go to the
1619             end of the text.
1620              
1621             =item * >
1622              
1623             Call the 'cursor-scrlinestart' routine: move the
1624             cursor to the start of the current line.
1625              
1626             =item * >
1627              
1628             Call the 'cursor-scrlineend' routine: move the
1629             cursor to the end of the current line.
1630              
1631             =item * >
1632              
1633             Call the 'toggle-wrapping' routine: toggle the
1634             -wrapping option of the texteditor.
1635              
1636             =item * >
1637              
1638             Call the 'toggle-showhardreturns' routine: toggle the
1639             -showhardreturns option of the texteditor.
1640              
1641             =item * >
1642              
1643             Call the 'toggle-showoverflow' routine: toggle the
1644             -showoverflow option of the texteditor.
1645              
1646             =back
1647              
1648              
1649              
1650             =head2 All edit modes (all but read only mode)
1651              
1652              
1653              
1654             =over 4
1655              
1656             =item * >, >
1657              
1658             Call the 'delete-line' routine: Delete the current
1659             line.
1660              
1661             =item * >
1662              
1663             Call the 'delete-till-eol' routine: delete the text
1664             from the current cursor position up to the end of
1665             the current line.
1666              
1667             =item * >
1668              
1669             Call the 'clear-line' routine: clear the
1670             current line and move the cursor to the
1671             start of this line.
1672              
1673             =item * >
1674              
1675             Call the 'delete-character' routine: delete the
1676             character that currently is under the cursor.
1677              
1678             =item * >
1679              
1680             Call the 'backspace' routine: delete the character
1681             this is before the current cursor position.
1682              
1683             =item * >
1684              
1685             Call the 'undo' routine: undo the last change to
1686             the text, up to B<-undolevels> levels.
1687              
1688             =item * >
1689              
1690             Call the 'paste' routine: this will paste the
1691             last deleted text at the current cursor position.
1692              
1693             =item * >
1694              
1695             Call the 'add-string' routine: the character
1696             will be inserted in the text at the current
1697             cursor position.
1698              
1699             =back
1700              
1701              
1702              
1703             =head2 Only for the read only mode
1704              
1705              
1706              
1707             =over 4
1708              
1709             =item * >
1710              
1711             Call the 'cursor-left' routine: move the
1712             cursor one position to the left.
1713              
1714             =item * >
1715              
1716             Call the 'cursor-right' routine: move the
1717             cursor one position to the right.
1718              
1719             =item * b<>
1720              
1721             Call the 'cursor-up' routine: move the
1722             cursor one line up.
1723              
1724             =item * b<>
1725              
1726             Call the 'cursor-down' routine: move the
1727             cursor one line down.
1728              
1729             =item * >, >
1730              
1731             Call the 'cursor-pagedown' routine: move
1732             the cursor to the next page.
1733              
1734             =item * >, >
1735              
1736             Call the 'cursor-pageup' routine: move the
1737             cursor to the previous page.
1738              
1739             =item * >
1740              
1741             Call the 'search-forward' routine. This will make a 'less'-like
1742             search system appear in the textviewer. A searchstring can be
1743             entered. After that the user can search for the next occurance
1744             using the 'n' key or the previous occurance using the 'N' key.
1745              
1746             =item * >
1747              
1748             Call the 'search-backward' routine. This will do the same as
1749             the 'search-forward' routine, only it will search in the
1750             opposite direction.
1751              
1752             =back
1753              
1754              
1755              
1756              
1757             =head1 SEE ALSO
1758              
1759             L,
1760             L
1761             L
1762             L,
1763             L
1764              
1765              
1766              
1767              
1768             =head1 AUTHOR
1769              
1770             Copyright (c) 2001-2002 Maurice Makaay. All rights reserved.
1771              
1772             Maintained by Marcus Thiesen (marcus@cpan.thiesenweb.de)
1773              
1774              
1775             This package is free software and is provided "as is" without express
1776             or implied warranty. It may be used, redistributed and/or modified
1777             under the same terms as perl itself.
1778