File Coverage

blib/lib/Curses/UI/Listbox.pm
Criterion Covered Total %
statement 111 331 33.5
branch 25 148 16.8
condition 6 64 9.3
subroutine 20 41 48.7
pod 18 33 54.5
total 180 617 29.1


line stmt bran cond sub pod time code
1             # ----------------------------------------------------------------------
2             # Curses::UI::Listbox
3             #
4             # (c) 2001-2002 by Maurice Makaay. All rights reserved.
5             # (c) 2003-2005 by Marcus Thiesen.
6             # This file is part of Curses::UI. Curses::UI is free software.
7             # You can redistribute it and/or modify it under the same terms
8             # as perl itself.
9             #
10             # Currently maintained by Marcus Thiesen
11             # e-mail: marcus@cpan.thiesenweb.de
12             # ----------------------------------------------------------------------
13              
14             package Curses::UI::Listbox;
15              
16 1     1   8 use strict;
  1         2  
  1         46  
17 1     1   7 use Curses;
  1         2  
  1         3010  
18 1     1   8 use Curses::UI::Common;
  1         1  
  1         119  
19 1     1   5 use Curses::UI::Widget;
  1         1  
  1         161  
20 1     1   644 use Curses::UI::TextEntry;
  1         5  
  1         57  
21 1     1   779 use Curses::UI::TextViewer;
  1         4  
  1         46  
22 1     1   6 use Curses::UI::Searchable;
  1         2  
  1         81  
23             require Exporter;
24              
25 1         4466 use vars qw(
26             $VERSION
27             @ISA
28             @EXPORT
29 1     1   5 );
  1         3  
30              
31             $VERSION = '1.3';
32              
33             @ISA = qw(
34             Curses::UI::Widget Curses::UI::Common
35             Curses::UI::Searchable Exporter
36             );
37              
38             @EXPORT = qw(
39             maxlabelwidth
40             );
41              
42             my %routines = (
43             'loose-focus' => \&loose_focus,
44             'option-select' => \&option_select,
45             'option-check' => \&option_check,
46             'option-uncheck' => \&option_uncheck,
47             'option-next' => \&option_next,
48             'option-prev' => \&option_prev,
49             'option-nextpage' => \&option_nextpage,
50             'option-prevpage' => \&option_prevpage,
51             'option-first' => \&option_first,
52             'option-last' => \&option_last,
53             'search-forward' => \&search_forward,
54             'search-backward' => \&search_backward,
55             'mouse-button1' => \&mouse_button1,
56             );
57              
58             my %bindings = (
59             KEY_LEFT() => 'loose-focus',
60             "h" => 'loose-focus',
61             CUI_TAB() => 'loose-focus',
62             KEY_BTAB() => 'loose-focus',
63             KEY_ENTER() => 'option-select',
64             KEY_RIGHT() => 'option-select',
65             "l" => 'option-select',
66             CUI_SPACE() => 'option-select',
67             "1" => 'option-check',
68             "y" => 'option-check',
69             "0" => 'option-uncheck',
70             "n" => 'option-uncheck',
71             KEY_DOWN() => 'option-next',
72             "j" => 'option-next',
73             KEY_NPAGE() => 'option-nextpage',
74             KEY_UP() => 'option-prev',
75             "k" => 'option-prev',
76             KEY_PPAGE() => 'option-prevpage',
77             KEY_HOME() => 'option-first',
78             "\cA" => 'option-first',
79             KEY_END() => 'option-last',
80             "\cE" => 'option-last',
81             "/" => 'search-forward',
82             "?" => 'search-backward',
83             );
84              
85             sub new ()
86             {
87 1     1 1 3 my $class = shift;
88              
89 1         4 my %userargs = @_;
90 1         6 keys_to_lowercase(\%userargs);
91              
92 1         35 my %args = (
93             -values => [], # values to show
94             -labels => {}, # optional labels for the values
95             -active => 0, # the activated value
96             -width => undef, # the width of the listbox
97             -height => undef, # the height of the listbox
98             -x => 0, # the hor. pos. rel. to parent
99             -y => 0, # the vert. pos. rel. to parent
100             -multi => 0, # multiselection possible?
101             -radio => 0, # show radio buttons? Only for ! -multi
102             -selected => undef, # the selected item
103             -wraparound => 0, # wraparound on first/last item
104             -onchange => undef, # onChange event handler
105             -onselchange=> undef, # onSelectionChange event handler
106              
107             -bg => -1,
108             -fg => -1,
109            
110             %userargs,
111              
112             -routines => {%routines},
113             -bindings => {%bindings},
114              
115             -yscrpos => 0, # Value init
116             -focus => 0, # Value init
117             -nocursor => 1, # This widget does not use a cursor
118             );
119              
120 1 50       8 if ($args{-multi})
121             {
122 0         0 $args{-radio} = 0;
123 0 0       0 $args{-selected} = {}
124             unless ref $args{-selected} eq 'HASH';
125 0         0 $args{-ypos} = 0;
126             } else {
127 1 50       8 $args{-ypos} = defined $args{-selected} ? $args{-selected} : 0;
128             }
129              
130 1         17 my $this = $class->SUPER::new( %args );
131 1         5 $this->layout_content();
132              
133 1 50       62 if ($Curses::UI::ncurses_mouse) {
134 1         5 $this->set_mouse_binding('mouse-button1', BUTTON1_CLICKED());
135 1         9 $this->set_mouse_binding('mouse-button1', BUTTON1_DOUBLE_CLICKED());
136             }
137              
138 1         7 return $this;
139             }
140              
141 1     1 1 13 sub onChange(;$) { shift()->set_event('-onchange', shift()) }
142              
143 0     0 1 0 sub onSelectionChange(;$) { shift()->set_event('-onselchange', shift()) };
144              
145             sub values(;$)
146             {
147 1     1 1 852 my $this = shift;
148 1         3 my $values = shift;
149              
150 1 50 33     12 if (defined $values && ! ref $values) {
151 0         0 $values = [ $values, @_ ];
152             }
153            
154 1 50 33     9 if (defined $values and ref $values eq 'ARRAY') {
155             # Clear and go to first item if we get new data
156 1         5 $this->clear_selection();
157              
158 1         3 $this->{-values} = $values;
159 1 50       7 $this->option_first() if defined $values;
160              
161             # Make this widget non-focusable if there are
162             # no values in it.
163 1         2 $this->focusable(scalar(@{$values}));
  1         6  
164             }
165              
166 1         3 return $this->{-values}
167             }
168              
169             sub insert_at()
170             {
171            
172 0     0 1 0 my $this = shift;
173 0         0 my $pos = shift;
174 0         0 my $values = shift;
175              
176             # Clear and go to first item if we get new data
177 0         0 $this->clear_selection();
178              
179 0 0       0 if (defined $values ) {
180 0 0       0 if (ref $values eq 'ARRAY') {
181 0         0 my @newdata = (splice(@{$this->{-values}},0,$pos - 1), @{$values},
  0         0  
  0         0  
182 0         0 @{$this->{-values}});
183              
184 0         0 $this->{-values} = \@newdata;
185             } else {
186 0         0 my @newdata = (splice (@{$this->{-values}},0,$pos - 1), $values,
  0         0  
187 0         0 @{$this->{-values}});
188              
189 0         0 $this->{-values} = \@newdata;
190             }
191             }
192              
193 0         0 return $this->{-values};
194              
195             }
196              
197             sub labels(;$)
198             {
199 0     0 1 0 my $this = shift;
200 0         0 my $labels = shift;
201              
202 0 0 0     0 if (defined $labels and ref $labels eq 'HASH') {
203 0         0 $this->{-labels} = $labels;
204             }
205 0         0 return $this->{-labels}
206             }
207              
208              
209             sub add_labels(;$)
210             {
211 0     0 1 0 my $this = shift;
212 0         0 my $labels = shift;
213              
214 0 0 0     0 if (defined $labels and ref $labels eq 'HASH') {
215 0         0 map $this->{-labels}->{$_} = $labels->{$_}, keys %{$labels};
  0         0  
216             }
217 0         0 return $this->{-labels}
218             }
219              
220              
221             sub maxlabelwidth(@)
222             {
223 0     0 0 0 my %args = @_;
224            
225 0         0 my $maxwidth = 0;
226 0         0 foreach my $value (@{$args{-values}})
  0         0  
227             {
228 0         0 my $label = $value;
229 0 0       0 $label = $args{-labels}->{$value}
230             if defined $args{-labels}->{$value};
231 0 0       0 $maxwidth = length($label)
232             if length($label) > $maxwidth;
233             }
234 0         0 return $maxwidth;
235             }
236              
237             sub layout()
238             {
239 1     1 1 2 my $this = shift;
240 1 50       9 $this->SUPER::layout() or return;
241            
242 1         4 $this->layout_content;
243              
244             # Scroll up if we can and the number of visible lines
245             # is smaller than the number of available lines in the screen.
246 1         4 my $inscreen = ($this->canvasheight
247             - ($this->number_of_lines - $this->{-yscrpos}));
248 1   33     7 while ($this->{-yscrpos} > 0 and $inscreen < $this->canvasheight)
249             {
250 0         0 $this->{-yscrpos}--;
251 0         0 $inscreen = ($this->canvasheight
252             - ($this->number_of_lines - $this->{-yscrpos}));
253             }
254              
255 1         3 return $this;
256             }
257              
258             sub layout_content()
259             {
260 5     5 1 7 my $this = shift;
261 5 50       13 return $this if $Curses::UI::screen_too_small;
262              
263             # Check bounds for -ypos index.
264 5         7 $this->{-max_selected} = @{$this->{-values}} - 1;
  5         16  
265 5 50       26 $this->{-ypos} = $this->{-max_selected}
266             if $this->{-ypos} > $this->{-max_selected};
267 5 50       17 $this->{-ypos} = 0 if $this->{-ypos} < 0;
268              
269             # Scroll down if needed.
270 5         11 my $ycur = $this->{-ypos} - $this->{-yscrpos};
271 5 50       22 if ( $ycur > ($this->canvasheight-1)) {
    50          
272 0         0 $this->{-yscrpos} = $this->{-ypos} - $this->canvasheight + 1;
273             }
274             # Scroll up if needed.
275             elsif ( $ycur < 0 ) {
276 0         0 $this->{-yscrpos} = $this->{-ypos};
277             }
278              
279              
280 5         7 $this->{-vscrolllen} = @{$this->{-values}};
  5         16  
281 5         11 $this->{-vscrollpos} = $this->{-yscrpos};
282 5 50       5 if ( @{$this->{-values}} <= $this->canvasheight) {
  5         18  
283 5         12 undef $this->{-vscrolllen};
284             }
285              
286 5         8 return $this;
287             }
288              
289             sub getlabel($;)
290             {
291 0     0 0 0 my $this = shift;
292 0   0     0 my $idx = shift || 0;
293              
294 0         0 my $value = $this->{-values}->[$idx];
295 0         0 my $label = $value;
296 0 0       0 $label = $this->{-labels}->{$label}
297             if defined $this->{-labels}->{$label};
298 0         0 $label =~ s/\t/ /g; # do not show TABs
299            
300 0         0 return $label;
301             }
302              
303              
304             sub get_active_value($;)
305             {
306 1     1 1 3 my $this = shift;
307 1         4 my $id = $this->{-ypos};
308 1         3 my $value = $this->{'-values'}->[$id];
309 1         6 return $value;
310             }
311              
312             sub get_active_id($;)
313             {
314 0     0 1 0 my $this = shift;
315 0         0 return $this->{-ypos};;
316             }
317              
318             sub draw(;$)
319             {
320 3     3 1 6 my $this = shift;
321 3   50     18 my $no_doupdate = shift || 0;
322              
323             # Draw the widget
324 3 50       14 $this->SUPER::draw(1) or return $this;
325              
326 3         10 $this->layout_content;
327              
328             # Let there be color
329 3 50       9 if ($Curses::UI::color_support) {
330 0         0 my $co = $Curses::UI::color_object;
331 0         0 my $pair = $co->get_color_pair(
332             $this->{-fg},
333             $this->{-bg});
334              
335 0         0 $this->{-canvasscr}->attron(COLOR_PAIR($pair));
336              
337             }
338              
339             # No values?
340 3 50       4 if (not @{$this->{-values}})
  3         10  
341             {
342 3         26 $this->{-canvasscr}->attron(A_DIM);
343 3         488 $this->{-canvasscr}->addstr(0,0,'- no values -');
344 3         277 $this->{-canvasscr}->attroff(A_DIM);
345              
346             # There are values. Show them!
347             } else {
348 0         0 my $start_idx = $this->{-yscrpos};
349 0         0 my $end_idx = $this->{-yscrpos} + $this->canvasheight - 1;
350 0 0       0 $end_idx = $this->{-max_selected}
351             if $end_idx > $this->{-max_selected};
352              
353 0         0 my $y = 0;
354 0         0 my $cursor_y = 0;
355 0         0 my $cursor_x = 0;
356 0         0 for my $i ($start_idx .. $end_idx)
357             {
358             # The label to print.
359 0         0 my $label = $this->getlabel($i);
360              
361             # Clear up label.
362 0         0 $label =~ s/\n|\r//g;
363              
364             # Needed space for prefix.
365 0 0 0     0 my $prefix_len =
366             (($this->{-multi} or $this->{-radio}) ? 4 : 0);
367              
368             # Chop length if needed.
369 0         0 $label = $this->text_chop($label, ($this->canvaswidth-$prefix_len));
370              
371             # Show current entry in reverse mode and
372             # save cursor position.
373 0 0 0     0 if ($this->{-ypos} == $i and $this->{-focus})
374             {
375 0         0 $this->{-canvasscr}->attron(A_REVERSE);
376 0         0 $cursor_y = $y;
377 0         0 $cursor_x = $this->canvaswidth-1;
378             }
379              
380             # Show selected element bold.
381 0 0 0     0 if ( ( not $this->{-multi}
      0        
      0        
      0        
      0        
382             and defined $this->{-selected}
383             and $this->{-selected} == $i)
384             or ( $this->{-multi}
385             and defined $this->{-selected}
386             and $this->{-selected}->{$i}) ) {
387 0         0 $this->{-canvasscr}->attron(A_BOLD);
388             }
389            
390             # Make full line reverse or blank
391 0         0 $this->{-canvasscr}->addstr(
392             $y, $prefix_len,
393             " "x($this->canvaswidth-$prefix_len)
394             );
395              
396             # Show label
397 0         0 $this->text_draw($y, $prefix_len, $label);
398              
399 0         0 $this->{-canvasscr}->attroff(A_REVERSE);
400 0         0 $this->{-canvasscr}->attroff(A_BOLD);
401              
402             # Place a [X] for selected value in multi mode.
403 0 0       0 $this->{-canvasscr}->attron(A_BOLD) if $this->{-focus};
404 0 0       0 if ($this->{-multi}) {
    0          
405 0 0 0     0 if (defined $this->{-selected} and
406             $this->{-selected}->{$i}) {
407 0         0 $this->{-canvasscr}->addstr($y, 0, '[X]');
408             } else {
409 0         0 $this->{-canvasscr}->addstr($y, 0, '[ ]');
410             }
411             }
412              
413             # Place a for selected value in radio mode.
414             elsif ($this->{-radio}) {
415 0 0 0     0 if (defined $this->{-selected}
416             and $i == $this->{-selected}) {
417 0         0 $this->{-canvasscr}->addstr($y, 0, '');
418             } else {
419 0         0 $this->{-canvasscr}->addstr($y, 0, '< >');
420             }
421             }
422 0 0       0 $this->{-canvasscr}->attroff(A_BOLD) if $this->{-focus};
423              
424 0         0 $y++;
425             }
426              
427 0 0 0     0 $cursor_x = 1 if $this->{-multi} or $this->{-radio};
428 0         0 $this->{-canvasscr}->move($cursor_y, $cursor_x);
429             }
430              
431 3         255 $this->{-canvasscr}->noutrefresh();
432 3 50       12 doupdate() unless $no_doupdate;
433              
434 3         11 return $this;
435             }
436              
437             sub option_last()
438             {
439 0     0 0 0 my $this = shift;
440 0         0 $this->{-ypos} = $this->{-max_selected};
441 0         0 $this->run_event('-onselchange');
442 0         0 $this->schedule_draw(1);
443 0         0 return $this;
444             }
445              
446             sub option_nextpage()
447             {
448 0     0 0 0 my $this = shift;
449 0 0       0 if ($this->{-ypos} >= $this->{-max_selected}) {
450 0         0 $this->dobeep;
451 0         0 return $this;
452             }
453 0 0       0 if ($this->{-ypos} + $this->canvasheight - 1 >= $this->{-max_selected}) {
454 0         0 $this->{-ypos} = $this->{-max_selected};
455             } else {
456 0         0 $this->{-ypos} += $this->canvasheight - 1;
457             }
458 0         0 $this->run_event('-onselchange');
459 0         0 $this->schedule_draw(1);
460 0         0 return $this;
461             }
462              
463             sub option_prevpage()
464             {
465 0     0 0 0 my $this = shift;
466 0 0       0 if ($this->{-ypos} <= 0) {
467 0         0 $this->dobeep;
468 0         0 return $this;
469             }
470 0 0       0 if ($this->{-ypos} - $this->canvasheight - 1 < 0) {
471 0         0 $this->{-ypos} = 0;
472             } else {
473 0         0 $this->{-ypos} -= $this->canvasheight - 1;
474             }
475 0         0 $this->run_event('-onselchange');
476 0         0 $this->schedule_draw(1);
477 0         0 return $this;
478             }
479              
480             sub clear_selection()
481             {
482 2     2 1 3 my $this = shift;
483 2 50       10 if ($this->{-multi}) {
484 0         0 my $selection = $this->{-selected};
485 0 0       0 return unless defined $selection;
486 0         0 foreach my $id (keys %$selection) {
487 0         0 $selection->{$id} = 0;
488             }
489             } else {
490 2         5 $this->{-selected} = undef;
491             }
492 2         15 $this->schedule_draw(1);
493             }
494              
495             sub set_selection()
496             {
497 2     2 1 4 my $this = shift;
498 2         4 my $id;
499              
500 2         5 foreach $id (@_) {
501 6 100       7 next if $id > @{$this->{-values}};
  6         23  
502 5 50       12 if ($this->{-multi})
503             {
504 0 0       0 my $changed = ($this->{-selected}->{$id} ? 0 : 1);
505 0         0 $this->{-selected}->{$id} = 1;
506 0 0       0 $this->run_event('-onchange') if $changed;
507 0         0 $this->schedule_draw(1);
508             } else {
509 5   66     26 my $changed = (not defined $this->{-selected} or
510             ($this->{-selected} != $id));
511 5         9 $this->{-selected} = $id;
512 5 50       20 $this->run_event('-onchange') if $changed;
513 5         31 $this->schedule_draw(1);
514             }
515             }
516 2         7 return $this;
517             }
518              
519             sub option_next()
520             {
521 0     0 0 0 my $this = shift;
522 0 0       0 if ($this->{-ypos} >= $this->{-max_selected}) {
523 0 0       0 if ($this->{-wraparound}) {
524 0         0 $this->{-ypos} = 0;
525             } else {
526 0         0 $this->dobeep;
527             }
528             } else {
529 0         0 $this->{-ypos}++;
530             }
531 0         0 $this->layout_content;
532 0         0 $this->run_event('-onselchange');
533 0         0 $this->schedule_draw(1);
534 0         0 return $this;
535             }
536              
537             sub option_prev()
538             {
539 0     0 0 0 my $this = shift;
540 0 0       0 if ($this->{-ypos} <= 0) {
541 0 0       0 if ($this->{-wraparound}) {
542 0         0 $this->{-ypos} = $this->{-max_selected};
543             } else {
544 0         0 $this->dobeep;
545             }
546             } else {
547 0         0 $this->{-ypos}--;
548             }
549 0         0 $this->layout_content;
550 0         0 $this->run_event('-onselchange');
551 0         0 $this->schedule_draw(1);
552 0         0 return $this;
553             }
554              
555             sub option_select()
556             {
557 0     0 0 0 my $this = shift;
558              
559 0 0       0 if ($this->{-multi})
560             {
561 0         0 $this->{-selected}->{$this->{-ypos}} =
562             !$this->{-selected}->{$this->{-ypos}};
563 0         0 $this->run_event('-onselchange');
564 0         0 $this->run_event('-onchange');
565 0         0 $this->schedule_draw(1);
566 0         0 return $this;
567             } else {
568 0   0     0 my $changed = (not defined $this->{-selected} or
569             ($this->{-selected} != $this->{-ypos}));
570 0         0 $this->{-selected} = $this->{-ypos};
571 0 0       0 $this->run_event('-onselchange')if $changed;
572 0 0       0 $this->run_event('-onchange') if $changed;
573 0         0 $this->schedule_draw(1);
574 0 0       0 return ($this->{-radio} ? $this : 'LOOSE_FOCUS');
575             }
576             }
577              
578             sub option_first()
579             {
580 1     1 0 3 my $this = shift;
581 1         2 $this->{-ypos} = 0;
582 1         11 $this->run_event('-onselchange');
583 1         11 $this->schedule_draw(1);
584 1         2 return $this;
585             }
586              
587             sub option_check()
588             {
589 0     0 0 0 my $this = shift;
590              
591 0 0       0 if ($this->{-multi})
592             {
593 0 0       0 my $changed = ($this->{-selected}->{$this->{-ypos}} ? 0 : 1);
594 0         0 $this->{-selected}->{$this->{-ypos}} = 1;
595 0         0 $this->{-ypos}++;
596 0 0       0 $this->run_event('-onchange') if $changed;
597 0         0 $this->schedule_draw(1);
598 0         0 return $this;
599             } else {
600 0   0     0 my $changed = (not defined $this->{-selected} or
601             ($this->{-selected} != $this->{-ypos}));
602 0         0 $this->{-selected} = $this->{-ypos};
603 0 0       0 $this->run_event('-onchange') if $changed;
604 0         0 $this->schedule_draw(1);
605 0 0       0 return ($this->{-radio} ? $this : undef);
606             }
607             }
608              
609             sub option_uncheck()
610             {
611 0     0 0 0 my $this = shift;
612 0 0       0 if ($this->{-multi})
613             {
614 0 0       0 my $changed = ($this->{-selected}->{$this->{-ypos}} ? 1 : 0);
615 0         0 $this->{-selected}->{$this->{-ypos}} = 0;
616 0 0       0 $this->run_event('-onchange') if $changed;
617 0         0 $this->{-ypos}++;
618             } else {
619 0         0 $this->dobeep;
620             }
621 0         0 $this->schedule_draw(1);
622 0         0 return $this;
623             }
624              
625             sub mouse_button1($$$;)
626             {
627 0     0 0 0 my $this = shift;
628 0         0 my $event = shift;
629 0         0 my $x = shift;
630 0         0 my $y = shift;
631              
632 0 0       0 return unless $this->{-focusable};
633              
634 0         0 $this->layout_content;
635              
636 0 0       0 unless ($this->{-focus}) {
637 0         0 $this->focus;
638             }
639              
640 0         0 my $newypos = $this->{-yscrpos} + $y;
641 0 0 0     0 if (@{$this->{-values}} and
  0   0     0  
  0         0  
642             $newypos >= 0 and $newypos < @{$this->{-values}}) {
643 0         0 $this->{-ypos} = $newypos;
644 0         0 $this->do_routine('option-select');
645             }
646              
647 0         0 $this->schedule_draw(1);
648             }
649              
650             sub get()
651             {
652 4     4 1 12 my $this = shift;
653 4 100       18 return unless defined $this->{-selected};
654 2 50       8 if ($this->{-multi}) {
655 0         0 my @values = ();
656 0         0 while (my ($id, $val) = each %{$this->{-selected}}) {
  0         0  
657 0 0       0 next unless $val;
658 0         0 push @values, $this->{-values}->[$id];
659             }
660 0         0 return @values;
661             } else {
662 2         14 return $this->{-values}->[$this->{-selected}];
663             }
664             }
665              
666             sub id()
667             {
668 0     0 1 0 my $this = shift;
669 0 0       0 return unless defined $this->{-selected};
670 0 0       0 if ($this->{-multi}) {
671 0         0 my @values = ();
672 0         0 while (my ($id, $val) = each %{$this->{-selected}}) {
  0         0  
673 0 0       0 next unless $val;
674 0         0 push @values, $id;
675             }
676 0         0 return @values;
677             } else {
678 0         0 return $this->{-selected};
679             }
680             }
681              
682             sub get_selectedlabel()
683             {
684 0     0 0 0 my $this = shift;
685 0         0 my $value = $this->get;
686 0 0       0 return unless defined $value;
687 0         0 my $label = $this->{-labels}->{$value};
688 0 0       0 return (defined $label ? $label : $value);
689             }
690              
691             sub set_color_fg {
692 0     0 0 0 my $this = shift;
693 0         0 $this->{-fg} = shift;
694 0         0 $this->intellidraw;
695             }
696              
697             sub set_color_bg {
698 0     0 0 0 my $this = shift;
699 0         0 $this->{-bg} = shift;
700 0         0 $this->intellidraw;
701             }
702              
703              
704             # ----------------------------------------------------------------------
705             # Routines for search support
706             # ----------------------------------------------------------------------
707              
708 1     1 1 2 sub number_of_lines() { @{shift()->{-values}} }
  1         6  
709 0     0 1   sub getline_at_ypos($;) { shift()->getlabel(shift()) }
710              
711             1;
712              
713              
714             =pod
715              
716             =head1 NAME
717              
718             Curses::UI::Listbox - Create and manipulate listbox widgets
719              
720             =head1 CLASS HIERARCHY
721              
722             Curses::UI::Widget
723             Curses::UI::Searchable
724             |
725             +----Curses::UI::Listbox
726              
727              
728             =head1 SYNOPSIS
729              
730             use Curses::UI;
731             my $cui = new Curses::UI;
732             my $win = $cui->add('window_id', 'Window');
733              
734             my $listbox = $win->add(
735             'mylistbox', 'Listbox',
736             -values => [1, 2, 3],
737             -labels => { 1 => 'One',
738             2 => 'Two',
739             3 => 'Three' },
740             -radio => 1,
741             );
742              
743             $listbox->focus();
744             my $selected = $listbox->get();
745              
746              
747             =head1 DESCRIPTION
748              
749             Curses::UI::Listbox is a widget that can be used to create
750             a couple of different kinds of listboxes. These are:
751              
752             =over 4
753              
754             =item * B
755              
756             A list of values through which can be browsed. One of these
757             values can be selected. The selected value will be
758             highlighted. This kind of listbox looks somewhat like this:
759              
760             +------+
761             |One |
762             |Two |
763             |Three |
764             +------+
765              
766             =item * B
767              
768             This is also a list of values, but now more than one
769             value can be selected at once. This kind of listbox
770             looks somewhat like this:
771              
772             +----------+
773             |[X] One |
774             |[ ] Two |
775             |[X] Three |
776             +----------+
777              
778             =item * B
779              
780             This looks a lot like the default listbox (only one
781             value can be selected), but now there is clear
782             visual feedback on which value is selected. Before
783             each value "< >" is printed. If a value is selected,
784             "" is printed instead. This kind of listbox
785             looks somewhat like this:
786              
787             +----------+
788             |< > One |
789             | Two |
790             |< > Three |
791             +----------+
792              
793              
794             =item * B
795              
796             The listbox supports a primitive markup language to emphasize
797             entries:
798             reverse text
799             bold text
800             underlined text
801             blinking text
802             dim text
803             By using this markup tokens in the values array, you can make the
804             listbox draw the text in the according way. To enable the parser,
805             you have to create the listbox with the -htmltext option.
806              
807              
808             =back
809              
810              
811              
812              
813             =head1 STANDARD OPTIONS
814              
815             B<-parent>, B<-x>, B<-y>, B<-width>, B<-height>,
816             B<-pad>, B<-padleft>, B<-padright>, B<-padtop>, B<-padbottom>,
817             B<-ipad>, B<-ipadleft>, B<-ipadright>, B<-ipadtop>, B<-ipadbottom>,
818             B<-title>, B<-titlefullwidth>, B<-titlereverse>, B<-onfocus>,
819             B<-onblur>
820              
821             For an explanation of these standard options, see
822             L.
823              
824              
825              
826              
827             =head1 WIDGET-SPECIFIC OPTIONS
828              
829             =over 4
830              
831             =item * B<-values> < ARRAYREF >
832              
833             This option sets the values to use.
834             Unless a label is set for the value (see B<-labels>),
835             this value will be shown in the list.
836              
837             =item * B<-labels> < HASHREF >
838              
839             The keys of this hash reference correspond to the values of
840             the listbox (see B<-values>). The values of the hash are the
841             labels to show in the listbox. It's not obligatory to have
842             a label defined for each value. You may even omit -labels
843             completely.
844              
845             =item * B<-selected> < INDEX >
846              
847             In case the B<-multi> option is not set, INDEX is the index
848             of the value that should be selected.
849              
850             In case the B<-multi> option is set, INDEX is a hash reference
851             in which the keys are the indices of the B<-values> which are
852             selected and the values are any true value.
853              
854             =item * B<-multi> < BOOLEAN >
855              
856             If BOOLEAN has a true value, the listbox will be a multi-select
857             listbox (see DESCRIPTION).
858              
859             =item * B<-radio> < BOOLEAN >
860              
861             If BOOLEAN has a true value, the listbox will be a radiobutton
862             listbox (see DESCRIPTION).
863              
864             =item * B<-wraparound> < BOOLEAN >
865              
866             If BOOLEAN has a true value, wraparound is enabled. This means
867             that if the listbox is on its last value and a key is pressed
868             to go to the next value, the first value will be selected.
869             Also the last value will be selected if this first value is
870             selected and "goto previous value" is pressed.
871              
872             =item * B<-onchange> < CODEREF >
873              
874             This sets the onChange event handler for the listbox widget.
875             If a new item is selected, the code in CODEREF will be executed.
876             It will get the widget reference as its argument.
877              
878             =item * B<-onselchange> < CODEREF >
879              
880             This sets the onSelectionChange event handler for the listbox widget.
881             If a new item is marked as active CODEREF will be executed.
882             It will get the widget reference as its argument.
883              
884             =item * B<-htmltext> < BOOLEAN >
885              
886             Make the Listbox parse primitive markup to change the items
887             appearance. See above.
888              
889              
890             =back
891              
892              
893              
894              
895             =head1 METHODS
896              
897             =over 4
898              
899             =item * B ( OPTIONS )
900              
901             =item * B ( )
902              
903             =item * B ( BOOLEAN )
904              
905             =item * B ( )
906              
907             =item * B ( )
908              
909             =item * B ( CODEREF )
910              
911             =item * B ( CODEREF )
912              
913             These are standard methods. See L
914             for an explanation of these.
915              
916             =item * B ( )
917              
918             This method will return the values of the currently selected items
919             in the list. If the listbox is not a multi-select listbox only one
920             value will be returned of course.
921              
922             =item * B ( )
923              
924             This method will return the index of the currently selected items
925             in the list. If the listboy is not a multi-select listbox it will
926             only return one value.
927              
928             =item * B ( )
929              
930             This method will return the value of the currently active (i.e
931             highlighted line).
932              
933             =item * B ( )
934              
935             This method will return the index of the currently active (i.e
936             highlighted line).
937              
938             =item * B ( LIST )
939              
940             This method marks the items at the positions specified in LIST
941             as selected. In a multi-select listbox you can set multiple items
942             with giving multiple values, in a single-select listbox only the
943             last item in LIST will be selected
944              
945             =item * B ( )
946              
947             This method clears the selected objects of a multi and radiobutton
948             listbox.
949              
950             =item * B ( ARRAYREF )
951              
952             This method sets the values to use.
953              
954             =item * B < POS, ARRAYREF|SCALAR >
955              
956             This method adds ARRAYREF or SCALAR into the list of values at
957             pos.
958              
959             =item * B [ HASHREF ]
960              
961             This method sets the labels to use.
962              
963             =item * B [ HASHREF ]
964              
965             This method adds the given labels to the already defined ones.
966              
967             =item * B ( CODEREF )
968              
969             This method can be used to set the B<-onchange> event handler
970             (see above) after initialization of the listbox.
971              
972             =item * B ( CODEREF )
973              
974             This method can be used to set the B<-onselchange> event handler
975             (see above) after initialization of the listbox.
976              
977              
978             =back
979              
980              
981              
982              
983             =head1 DEFAULT BINDINGS
984              
985             =over 4
986              
987             =item * >, >, >
988              
989             Call the 'loose-focus' routine. This will have the widget
990             loose its focus.
991              
992             =item * , >, >, >
993              
994             Call the 'option-select' routine. This will select the
995             active item in the listbox.
996              
997             =item * >, >
998              
999             Call the 'option-check' routine. If the listbox is a
1000             multi-select listbox, the active item will become checked
1001             and the next item will become active.
1002              
1003             =item * >, >
1004              
1005             Call the 'option-uncheck' routine. If the listbox is a
1006             multi-select listbox, the active item will become unchecked
1007             and the next item will become active.
1008              
1009             =item * >, >
1010              
1011             Call the 'option-next' routine. This will make the next
1012             item of the list active.
1013              
1014             =item * >, >
1015              
1016             Call the 'option-prev' routine. This will make the previous
1017             item of the list active.
1018              
1019             =item * >
1020              
1021             Call the 'option-prevpage' routine. This will make the item
1022             on the previous page active.
1023              
1024             =item * >
1025              
1026             Call the 'option-nextpage' routine. This will make the item
1027             on the next page active.
1028              
1029             =item * >, >
1030              
1031             Call the 'option-first' routine. This will make the first
1032             item of the list active.
1033              
1034             =item * >, >
1035              
1036             Call the 'option-last' routine. This will make the last
1037             item of the list active.
1038              
1039             =item * >
1040              
1041             Call the 'search-forward' routine. This will make a 'less'-like
1042             search system appear in the listbox. A searchstring can be
1043             entered. After that the user can search for the next occurance
1044             using the 'n' key or the previous occurance using the 'N' key.
1045              
1046             =item * >
1047              
1048             Call the 'search-backward' routine. This will do the same as
1049             the 'search-forward' routine, only it will search in the
1050             opposite direction.
1051              
1052             =back
1053              
1054              
1055              
1056              
1057              
1058             =head1 SEE ALSO
1059              
1060             L,
1061             L,
1062             L
1063              
1064              
1065              
1066              
1067             =head1 AUTHOR
1068              
1069             Copyright (c) 2001-2002 Maurice Makaay. All rights reserved.
1070              
1071             Maintained by Marcus Thiesen (marcus@cpan.thiesenweb.de)
1072              
1073             This package is free software and is provided "as is" without express
1074             or implied warranty. It may be used, redistributed and/or modified
1075             under the same terms as perl itself.
1076