File Coverage

blib/lib/Config/Model/CursesUI.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1              
2             my $verb_wiz = 1 ;
3              
4             package Config::Model::CursesUI ;
5             require Exporter;
6 1     1   286032 use strict ;
  1         4  
  1         26  
7 1     1   5 use Config::Model::Exception ;
  1         2  
  1         20  
8 1     1   5 use Carp;
  1         3  
  1         41  
9 1     1   5 use warnings ;
  1         2  
  1         29  
10              
11 1     1   5 use Config::Model 2.095; # for get_help_as_text
  1         17  
  1         28  
12 1     1   5 use Config::Model::ObjTreeScanner ;
  1         1  
  1         20  
13 1     1   435 use Curses::UI ;
  0            
  0            
14              
15             our $VERSION = '1.106';
16              
17             my @help_settings = qw/-bg green -fg black -border 1
18             -titlereverse 0
19             -padbottom 1 -wrapping 1/ ;
20              
21             sub new {
22             my $class = shift ;
23             my %args = @_ ;
24             my $self = { init_done => 0 , stack => [], debug => 0 } ;
25              
26             $self->{debug} = $args{debug} if defined $args{debug} ;
27             foreach my $param (qw/store load/) {
28             $self->{tree}{$param} = $args{$param} if defined $args{$param} ;
29             }
30              
31             $self->{cui} = new Curses::UI (
32             -color_support => 1,
33             #-default_colors=> 0,
34             #-clear_on_exit => 1,
35             #-compat => 1,
36             #-debug => 1
37             );
38              
39             my %cb_set
40             = (
41             # scanner self
42             list_element_cb => sub {shift; shift->display_hash_element (@_); },
43             check_list_element_cb => sub {shift; shift->display_check_list_element(@_); },
44             hash_element_cb => sub {shift; shift->display_hash_element (@_); },
45             node_element_cb => sub {shift; shift->display_node_element (@_); },
46              
47             node_content_cb => sub {shift; shift->display_node_content (@_); },
48              
49             leaf_cb => sub {shift; shift->display_leaf (@_); },
50             string_value_cb => sub {shift; shift->display_string (@_); },
51             reference_value_cb => sub {shift; shift->display_enum (@_); },
52             enum_value_cb => sub {shift; shift->display_enum (@_); },
53             integer_value_cb => sub {shift; shift->display_string (@_); },
54             number_value_cb => sub {shift; shift->display_string (@_); },
55             boolean_value_cb => sub {shift; shift->display_boolean (@_); },
56             ) ;
57              
58             eval {
59             $self->{scan} = Config::Model::ObjTreeScanner
60             -> new (
61             fallback => 'all',
62             %cb_set ,
63             ) ;
64             };
65              
66             $self->{cui}->fatalerror("Could not create ObjTreeScanner:\n$@")
67             if $@ ;
68              
69             bless $self,$class ;
70             }
71              
72             # create dialog windows
73             sub init {
74             my $self = shift ;
75             my $cui = $self->{cui};
76              
77             $self->create_explanation ;
78              
79             # Bind to quit.
80             my $quit_sub = sub {
81             $self->store_config ;
82             exit;
83             } ;
84             $cui->set_binding( $quit_sub, "\cQ" );
85              
86             # Bind to quit.
87             $cui->set_binding( sub {exit;}, "\cC" );
88              
89             # Bind to reset.
90             $cui->set_binding( sub {$self->reset_screen} , "\cR" );
91              
92             # Bind to back.
93             $cui->set_binding( sub {$self->back}, "\cB" );
94              
95             # Bind to menubar.
96             $cui->set_binding( sub{ $self->{cui}->root->focus('menu') }, "\cX" );
97              
98             $self->{init_done} = 1;
99             }
100              
101             sub back {
102             my $self=shift ;
103             return unless @{$self->{stack}} > 1;
104             pop @{$self->{stack}};
105             $self->reset_screen ;
106             }
107              
108             sub reset_screen {
109             my $self=shift ;
110             return unless @{$self->{stack}};
111             &{ pop @{$self->{stack}} };
112             }
113              
114             sub create_explanation {
115             my $self = shift ;
116             my $cui = $self->{cui} ;
117              
118             my $w_bottom = $cui->add( 'bottom', 'Window',
119             -border => 1,
120             '-y' => -1,
121             -height => 3
122             );
123              
124             $w_bottom->add( 'explain', 'Label',
125             -text => "CTRL-Q: save & quit CTRL+C: exit CTRL+X: menu CTRL+B: "
126             . "back CTRL-R: reset screen"
127             );
128              
129             my $w_url = $cui->add( 'undef', 'Window',
130             -border => 1,
131             '-y' => 1,
132             -height => 3
133             );
134              
135             $self->{conf_label} = $w_url->add ('conf_label',
136             'Label', -x => 1,
137             -text => "",
138             -width => 15
139             );
140              
141             $self->{loc_label} = $w_url->add ( 'location', 'Label',
142             -bg => 'blue',
143             -fg => 'white',
144             -bold => 1,
145             '-padleft' => 16,
146             -text => "." x 60
147             );
148             }
149              
150             sub set_center_window {
151             $_[0]->{cui}->delete('center') ;
152             $_[0]->{cui}->add ( 'center', 'Window',
153             -border => 1,
154             -titlereverse => 0,
155             -padtop => 4,
156             -padbottom => 3,
157             -ipad => 1,
158             -title => $_[1]
159             ) ;
160             }
161              
162             sub add_debug_label {
163             my ($self,$win) = @_ ;
164              
165             my @a = caller (1) ;
166             my $f = $a[3] ;
167             $f =~ s/.*::// ;
168             $win->add(undef, 'Label', -x => 40,
169             -text => "debug: '$f()',l $a[2]") if $self->{debug} ;
170             }
171              
172             sub start {
173             my $self = shift ;
174             my $model_obj = shift ;
175              
176             $self->{model_obj} = $model_obj ;
177              
178             $self->init unless $self->{init_done} ;
179              
180             $self->create_menu ;
181             $self->{conf_label} -> text('') ;
182              
183             my $start = sub {$self->start($model_obj)} ;
184             push @{$self->{stack}} , $start ;
185             $self->{start_all} = $start ;
186             $self->{loc_label}->text('') ;
187              
188             my @inst_names = $model_obj -> instance_names ;
189             warn "found @inst_names\n";
190              
191             my $win = $self->set_center_window("XXX configuration");
192              
193             # create an instance screen if more than one instance was passed
194             if (@inst_names > 1) {
195             # TBD scan the tree to get a name
196              
197             $self->add_debug_label($win) ;
198              
199             $win->add(undef, 'Label',
200             -text => "Choose your configuration instance");
201              
202             my $y = 2 ;
203             foreach my $i_name (@inst_names) {
204             $win->add ( undef, 'Buttonbox',
205             '-x' => 2,
206             '-y'=> $y ,
207             -width => 15,
208             '-buttons'
209             => [
210             {
211             -label => "< $i_name >",
212             -onpress => sub {$self->start_config($i_name) ;},
213             },
214             ]
215             ) ;
216              
217             $y++ ;
218             }
219              
220             $win->focus ;
221             }
222             else {
223             $self->start_config(@inst_names) ;
224             }
225              
226             $self->{cui}->mainloop;
227             }
228              
229             sub reset_config {
230             my ($self,$inst_name) = @_ ;
231             $self->{cui}->status("Reseting $inst_name ...") ;
232             $self->{tree}{root}
233             = $self->{model_obj}->instance(name => $inst_name)
234             -> reset_config ;
235              
236             $self->{tree}{load}->() if defined $self->{tree}{load} ;
237              
238             $self->{cui}->nostatus ;
239             return $self->{tree}{root} ;
240             }
241              
242             sub load_config {
243             my ($self,$inst_name) = @_ ;
244              
245             $self->{cui}->status("Loading $inst_name ...") ;
246             warn "Loading config $inst_name ...\n" ;
247              
248             my $root = $self->{tree}{root} =
249             $self->{model_obj}->instance(name => $inst_name)->config_root ;
250              
251             $self->{tree}{load}->() if defined $self->{tree}{load} ;
252              
253             $self->{cui}->nostatus ;
254             return $root;
255             }
256              
257             sub store_config {
258             my ($self) = @_ ;
259              
260             my $label = $self->{tree}{root}->instance->name ;
261              
262             if (defined $self->{tree}{store}) {
263             warn "Storing config $label with provided store call-back...\n" ;
264             $self->{tree}{store}->() ;
265             }
266             else {
267             warn "Storing config $label with model call-back...\n" ;
268             $self->{tree}{root}->instance->write_back;
269             }
270              
271             $self->{cui}->nostatus ;
272             }
273              
274             sub start_config {
275             my $self = shift ;
276             my $inst_name = shift ;
277              
278             $self->{start_config} = sub {$self->start_config($inst_name) } ;
279              
280             my $inst = $self->{model_obj}->instance(name => $inst_name) ;
281              
282             $self->{conf_label} -> text($inst_name.':') ;
283             $self->create_config_menu($inst_name) ;
284              
285             # reset location label
286             $self->{loc_label}->text('') ;
287              
288             $self->{tree}{root} ||= $self->load_config($inst_name);
289             my $root = $self->{tree}{root} ;
290              
291             $self->init unless $self->{init_done} ;
292              
293             my $win = $self->set_center_window($inst->name." configuration");
294              
295             $self->add_debug_label($win) ;
296              
297             $win->add(undef, 'Label',
298             -text => $root->name." configuration");
299              
300             $win
301             ->add ( undef, 'Buttonbox', '-y' => 2, -vertical => 1,
302             '-buttons'
303             => [ { -label => "< config wizard >",
304             -onpress => sub{$self->wizard($root,1) ;},
305             },
306             {
307             -label => "< open >",
308             -onpress => sub{$self->scan('node',$root) ;},
309             },
310             {
311             -label => "< search >",
312             -onpress => sub{$self->display_all_elements($root) ;},
313             },
314             {
315             -label => "< overall tabular view >",
316             '-onpress'
317             => sub{$self->display_view_list($root,
318             'std',
319             'tabular') ;},
320             },
321             {
322             -label => "< overall tabular audit >",
323             -onpress => sub{$self->display_view_list($root,
324             'audit',
325             'tabular') ;},
326             },
327             {
328             -label => "< overall view >",
329             -onpress => sub{$self->display_view_list($root,
330             'std',
331             'tree') ;},
332             },
333             {
334             -label => "< overall audit >",
335             -onpress => sub{$self->display_view_list($root,
336             'audit',
337             'tree') ;},
338             },
339             {
340             -label => "< look for errors >",
341             -onpress => sub{$self->wizard($root,0) ;},
342             },
343             ]
344             );
345              
346             $self->{displayed_object} = $_[0] ;
347              
348             push @{$self->{stack}} , $self->{start_config};
349              
350             $self->{cui}->getobj('center')->focus ;
351              
352             # must add:
353             # button to access a view style list
354             }
355              
356             # update the location label with config element path
357             # add the current screen on user's call stack
358             sub wrap_screen {
359             my ($self,$node,$element,$idx) = @_ ;
360              
361             $self->{displayed_object} = $node ;
362              
363             $self->update_location($node,$element,$idx) ;
364              
365             my $scan_type = defined $idx ? 'hash'
366             : defined $element ? 'element'
367             : 'node' ;
368              
369             push @{$self->{stack}} , sub{$self->scan($scan_type,$node,$element,$idx)};
370              
371             $self->{cui}->getobj('center')->focus ;
372             }
373              
374             sub update_location {
375             my ($self,$node,$element,$idx) = @_ ;
376              
377             my $loc = $node->location ;
378             $loc .= ' ' if $loc ;
379             $loc .= $element if defined $element ;
380             $loc .= ":$idx" if defined $idx ;
381              
382             $self->{loc_label}->text($loc) ;
383             }
384              
385             sub scan {
386             my ($self,$what,@args) = @_ ;
387              
388             my $meth = 'scan_'.$what ;
389              
390             eval {$self->{scan}->$meth($self,@args) ; };
391              
392             # we may want to handle differently the exception
393             $self->{cui}->fatalerror("Error in $meth:\n$@")
394             if $@ ;
395             }
396              
397             sub display_node_content {
398             my ($self,$node,@element) = @_ ;
399              
400             my $win = $self->set_center_window("Node ".$node->name) ;
401              
402             $self->add_debug_label($win) ;
403              
404             $win->add(undef, 'Label', '-y' => 0,
405             -text => "Choose one of the elements:");
406              
407             my $valuew = $win->add(undef, 'Label', -bg => 'yellow',
408             '-y' => 2, '-x' => 40, -width => 38 );
409             my $permw = $win->add(undef, 'Label',
410             '-y' => 3, '-x' => 40, -width => 38 );
411             my $selw = $win->add(undef, 'Label',
412             '-y' => 4, '-x' => 40, -width => 38 );
413             my $helpw = $win->add(undef, 'TextViewer',
414             '-y' => 5, '-x' => 40, -width => 38,
415             '-title' => 'Help on element',
416             @help_settings);
417              
418             my $listbox ;
419             my $buttons ;
420             my $lb_change = sub {
421             my $sel = ($listbox->get)[0];
422             $selw->text("selected $sel ");
423             $buttons -> focus ;
424             } ;
425              
426             my $lb_sel_change = sub {
427             my $sel = ($listbox->get_active_value)[0];
428             return unless defined $sel ; # may happen with empty node
429             my $help = $node->get_help_as_text($sel) ;
430             $help = "no help for $sel" unless $help ;
431             $helpw->text($help) ;
432             my $type = $node->element_type($sel) ;
433             my $elt = $node->fetch_element($sel) ;
434             my $v_str = '' ;
435             if ($type eq 'leaf') {
436             my $v = $elt->fetch_no_check ;
437             $v_str = 'value: ';
438             $v_str .= defined $v ? "'$v'" : '';
439             }
440             elsif ($type =~ 'node') {
441             $v_str = 'node: '.$elt->config_class_name ;
442             }
443             else {
444             $v_str = 'type: '.$type ;
445             }
446             $valuew -> text ( $v_str ) ;
447             };
448              
449             $listbox
450             = $win->add (
451             'mylistbox',
452             'Listbox',
453             -border => 1,
454             '-y' => 2,
455             -width => 38 ,
456             -padbottom => 1,
457             -title => 'element',
458             -vscrollbar => 1,
459             -onchange => $lb_change ,
460             -onselchange => $lb_sel_change ,
461             -values => \@element,
462             -selected => 0, # automatically select first item
463             );
464              
465             $listbox->focus ;
466              
467             my $go = {
468             -label => '< GO >',
469             -onpress => sub {
470             my @sel = $listbox->get;
471             if (@sel) {
472             $self->scan('element',$node,$sel[0]);
473             }
474             else {
475             $self->{cui}->dialog(-message =>
476             "Please select an element");
477             }
478             }
479             } ;
480              
481             my $help = {
482             -label => '< Help on node >',
483             -onpress => sub {
484             my $help= $node->get_help_as_text ;
485             $help = "Sorry, no help available"
486             unless defined $help;
487             $self->{cui}->dialog($help) ;
488             }
489             } ;
490              
491             my $parent = $node->parent ;
492              
493             # closure: don't remove the $buttons assignment
494             $buttons = $self->add_std_button($win,$parent,undef,$help,$go) ;
495              
496             $self->wrap_screen($node) ;
497              
498             # display value and help of selected element (i.e. -selected 0)
499             my $sel = ($listbox->get)[0];
500             $selw->text("selected $sel ");
501             &$lb_sel_change() ;
502             }
503              
504             # node_element_cb
505             sub display_node_element {
506             my ($self,$node,$element,$key, $contained_node) = @_ ;
507              
508             # here, there's no need to define a screen, just fetch the
509             # node and scan it
510             if (not $node->is_accessible($element)) {
511             my $str = "Node ".$node->name." element: $element";
512             $str .= " key $key" if defined $key;
513             my $win = $self->set_center_window($str);
514             $win->add (
515             undef, 'Label',
516             -text => "Node is currently unavailable.\n"
517             . "To make it available, change one "
518             . "of the following items"
519             );
520              
521             my $y = 3 ;
522             foreach my $master ($contained_node->get_all_warper_object) {
523             my $s = $master->element_name ;
524             my $cb = sub {
525             my $p = $master->parent ;
526             $self->scan('element',$p,$s) ;
527             };
528              
529             $win->add (
530             undef, 'Buttonbox',
531             '-y' => $y++ ,
532             -buttons =>
533             [{
534             -label => "< ".$master->name." >",
535             -value => $master,
536             -width => 20,
537             -onpress => $cb
538             }]
539             );
540             no warnings "uninitialized" ;
541             $win->add
542             (
543             undef, 'Label',
544             '-y' => $y++ ,
545             '-x' => 3 ,
546             #-width => 20,
547             -text => "* $s value '".$master->fetch."'"
548             );
549             }
550              
551             $self->wrap_screen($node,$element,$key) ;
552              
553             }
554             else {
555             $self->{scan}->scan_node($self,$contained_node);
556             }
557             }
558              
559             sub display_hash_element {
560             my ($self,$node,$element,@keys) = @_ ;
561              
562             my $win = $self->set_center_window(ucfirst($node->element_type($element)));
563              
564             $self->add_debug_label($win) ;
565              
566             my $listbox = $self->layout_hash($win, $node,$element,@keys) ;
567              
568             my @but =
569             (
570             { -label => '< GO >',
571             -onpress => sub
572             {
573             my @sel = $listbox->get;
574             return unless @sel;
575             $self->scan('hash',$node,$element,$sel[0]);
576             }
577             }
578             ) ;
579              
580             $self->add_std_button_with_help($win,$node,$element,@but) ;
581              
582             $self->wrap_screen($node,$element) ;
583             return $win ;
584             }
585              
586             sub layout_hash {
587             my ($self,$win,$node,$element,@keys) = @_ ;
588              
589             $win->add(undef, 'Label', -text => "Select or add one element:");
590              
591             my $lb_sel_change ;
592              
593             my $listbox = $win->add (
594             'mylistbox', 'Listbox',
595             -border => 1,
596             '-y' => 2,
597             -padbottom => 1,
598             -width => 40 ,
599             -title => $element.' elements',
600             -onselchange => $lb_sel_change ,
601             -vscrollbar => 1,
602             -values => \@keys,
603             -selected => 0, # automatically select first item
604             );
605              
606             my $hash_obj = $node->fetch_element($element) ;
607              
608             my $redraw
609             = sub {
610             my @rkeys = $self->{scan}->get_keys($node,$element) ;
611             warn "redraw: keys are @rkeys\n" ;
612             $listbox->values( \@rkeys ) ;
613             #$listbox->layout ;
614             $listbox->draw ; #intellidraw ;
615             #$win->intellidraw ;
616             } ;
617              
618             $listbox->focus ;
619              
620             $win->add(undef, 'Label',
621             '-x' => 41, '-y' => 2,
622             -text => "Id to add, rm, cp, mv:");
623              
624             my $editor = $win -> add ( undef, 'TextEntry',
625             -sbborder => 1,
626             '-x' => 41,
627             '-y' => 3,
628             -width => 15,
629             -text => ''
630             );
631              
632             # $node and $element are closure
633              
634             my $add_sub
635             = sub {
636             my $add = $editor->get;
637             if ($add) {
638             my $res = $self->try_it(sub {$hash_obj->fetch_with_id($add);}) ;
639             &$redraw;
640             }
641             else {
642             $self->{cui}->dialog(-message =>
643             "Please type in an id to add");
644             }
645             };
646              
647             my $del_sub
648             = sub {
649             my $del = $listbox->get;
650             if ($del) {
651             $self->try_it(sub {$hash_obj->delete($del);})
652             or return ;
653             &$redraw;
654             }
655             else {
656             $self->{cui}->error(-message =>
657             "Please type in an id to remove");
658             }
659             };
660              
661             my $copy_sub
662             = sub {
663             my @sel = $listbox->get;
664             my $to = $editor->get;
665              
666             unless (@sel) {
667             $self->{cui}->error(-message =>
668             "Please select an id to copy from");
669             return ;
670             }
671             unless ($to) {
672             $self->{cui}->error(-message =>
673             "Please type in an id to copy to") ;
674             return ;
675             }
676              
677             $self->try_it(sub { $hash_obj -> copy ($sel[0],$to) ;} ) ;
678             # redraw the screen
679             &$redraw;
680             };
681              
682             my $move_sub
683             = sub {
684             my @sel = $listbox->get;
685             my $to = $editor->get;
686              
687             unless (@sel) {
688             $self->{cui}->error(-message =>
689             "Please select an id to move from");
690             return ;
691             }
692             unless ($to) {
693             $self->{cui}->error(-message =>
694             "Please type in an id to move to");
695             return ;
696             }
697              
698             $self->try_it(sub { $hash_obj -> move($sel[0], $to) ;} );
699             # redraw the screen
700             &$redraw;
701             } ;
702              
703             $win->add(undef, 'Label', '-x' => 41, '-y' => 4, -text => "do: " );
704              
705             $win->add ( undef, 'Buttonbox',
706             '-y' => 4,
707             '-x' => 45,
708             #-buttonalignment => 'left',
709             -width => 20,
710             -vertical => 0,
711             -buttons =>
712             [
713             {
714             -label => '' , -onpress => $add_sub },
715             {
716             -label => '' , -onpress => $del_sub },
717             {
718             -label => '' , -onpress => $copy_sub },
719             {
720             -label => '', -onpress => $move_sub }
721             ]
722             );
723              
724             $win->add(undef, 'Label',
725             '-x' => 41, '-y' => 5, -bg => 'yellow',
726             -text => "Cargo type: ".$hash_obj->cargo_type );
727              
728             my $value_w = $win-> add(undef, 'Label',
729             '-x' => 41, '-y' => 6,-width => 38,
730             -bg => 'yellow',
731             -text => "content: " );
732              
733             $lb_sel_change = sub {
734             my $sel = ($listbox->get_active_value)[0];
735             return unless defined $sel ; # may happen with empty hash
736             my $ct = $hash_obj -> cargo_type ;
737             my $value = $ct eq 'leaf' ? $hash_obj->fetch_with_id($sel) -> fetch
738             : $ct =~ /node/ ? "node " . $hash_obj->config_class_name
739             : "type $ct" ;
740              
741             $value_w->text("content: ".$value) ;
742             };
743              
744             &$lb_sel_change ; # to display selected value ;
745              
746             my $helpw = $win->add(undef, 'TextViewer',
747             '-y' => 7, '-x' => 41, -width => 38,
748             '-title' => 'Help on element',
749             @help_settings);
750             my $help = $node->get_help_as_text($element) || "no help for $element" ;
751             $helpw->text($help) ;
752              
753             return $listbox ;
754             }
755              
756              
757             sub display_check_list_element {
758             my ($self,$node,$element,@check_items) = @_ ;
759              
760             my $win = $self->set_center_window("Check list");
761              
762             $self->layout_checklist($win, $node,$element) ;
763              
764             $self->wrap_screen($node,$element) ;
765             return $win ;
766             }
767              
768             sub layout_checklist {
769             my ($self,$win,$node,$element) = @_ ;
770              
771             my $check_list_obj = $node->fetch_element($element) ;
772              
773             my $notebook = $win->add(undef, 'Notebook', -intellidraw => 1);
774              
775             my $content_page = $notebook->add_page('edit content');
776             $self->layout_checklist_editor($content_page,$node,$element) ;
777              
778             if ($check_list_obj -> ordered ) {
779             my $lb ;
780             my $c_sub = sub {
781             my @values = $check_list_obj->get_checked_list ;
782             $lb->values(\@values) ;
783             };
784             my $order_page = $notebook->add_page('change order',
785             -on_activate => $c_sub ) ;
786             $lb = $self->layout_checklist_order($order_page,$node,$element) ;
787             }
788              
789             }
790              
791             sub layout_checklist_info {
792             my ($self,$win,$node,$element, $yr,$text) = @_ ;
793             my $check_list_obj = $node->fetch_element($element) ;
794              
795             $win->add(undef, 'Label', '-y' => $$yr , -text => "Current value :");
796              
797             my $cur_val_w
798             = $win->add(undef, 'Label', '-y' => $$yr++ , '-x' => 16 );
799              
800             $win->add(undef, 'Label', '-y' => $$yr++ , -text => $text);
801              
802             my @values = $check_list_obj->get_choice ;
803              
804             my $help_w = $win -> add ( undef, 'TextViewer',
805             '-x' => 42 ,
806             '-y' => $$yr ,
807             -width => 35,
808             -text => $node->get_help_as_text($element) ,
809             '-title' => 'Help on value',
810             @help_settings ) ;
811              
812             my $help_update = sub {
813             my $widget = shift ;
814             my $choice = $values[$widget->get_active_id] ;
815             $help_w->text($check_list_obj->get_help_as_text($choice)) ;
816             } ;
817              
818             return ($cur_val_w,$help_update) ;
819             }
820              
821             sub layout_checklist_editor {
822             my ($self,$win,$node,$element) = @_ ;
823              
824             my $y = 1 ;
825             my ($cur_val_w,$help_update)
826             = $self->layout_checklist_info($win,$node,$element,\$y,
827             "Check one or more:" ) ;
828              
829             my $check_list_obj = $node->fetch_element($element) ;
830             my @values = $check_list_obj->get_choice ;
831             my $listbox = $win->add (
832             'mylistbox', 'Listbox',
833             -border => 1,
834             '-y' => $y,
835             -multi => 1 ,
836             -padbottom => 1,
837             -width => 40 ,
838             -title => $element.' elements',
839             -vscrollbar => 1,
840             -onselchange => $help_update ,
841             -selected => { 0 => 1, 1 => 1 } ,
842             -values => \@values ,
843             );
844              
845             my $update_value = sub {
846             $cur_val_w->text(join(",",$check_list_obj->get_checked_list)) ;
847             my %new_hash = $check_list_obj->get_checked_list_as_hash ;
848             my $idx = 0;
849             $listbox->clear_selection ;
850             foreach my $v (sort keys %new_hash) {
851             warn "set $v ($idx) to $new_hash{$v} for @{$listbox->{-values}}\n";
852             $listbox->set_selection($idx) if $new_hash{$v} ;
853             $idx ++ ;
854             }
855             # Tk::ObjScanner::scan_object($listbox) ;
856             $listbox->draw ;
857             } ;
858              
859             $update_value->() ;
860              
861             my $ok_sub = sub {
862             my (@set) = $listbox->get ;
863             $check_list_obj->set_checked_list(@set) ;
864             $update_value->() ;
865             } ;
866              
867             my @buttons = (
868             {
869             -label => '< Store >', -onpress => $ok_sub }
870             ) ;
871              
872             $self->add_std_button_with_help($win,$node,$element, @buttons ) ;
873              
874             $listbox->focus ;
875              
876             return $listbox ;
877             }
878              
879             sub layout_checklist_order {
880             my ($self,$win,$node,$element) = @_ ;
881              
882             my $y = 1;
883             my ($cur_val_w,$help_update)
884             = $self->layout_checklist_info($win,$node,$element,\$y,
885             "Current value :");
886              
887             my $check_list_obj = $node->fetch_element($element) ;
888             my @values = $check_list_obj->get_checked_list ;
889             my $listbox = $win->add (
890             'mylistbox', 'Listbox',
891             -border => 1,
892             '-y' => $y,
893             -padbottom => 1,
894             -width => 40 ,
895             -title => $element.' elements',
896             -vscrollbar => 1,
897             -onselchange => $help_update ,
898             -values => \@values ,
899             );
900              
901             my $update_value = sub {
902             my $set = shift ;
903             my @new_list = $check_list_obj->get_checked_list ;
904             $cur_val_w->text(join(",",$check_list_obj->get_checked_list)) ;
905             $listbox->values(\@new_list) ;
906             # Tk::ObjScanner::scan_object($listbox) ;
907             $listbox->set_selection($set) if defined $set ;
908             $listbox->draw ;
909             } ;
910              
911             $win->onFocus(sub {$update_value->()} ) ; ;
912              
913             my $up_sub = sub {
914             my ($item) = $listbox->get || return ; # no selection
915             my ($idx) = $listbox->id || return ; # first item selected
916             $check_list_obj->move_up($item) ;
917             $update_value->($idx - 1) ;
918             } ;
919              
920             my $down_sub = sub {
921             my ($item) = $listbox->get || return ;
922             my ($idx) = $listbox->id ;
923             my @new_list = $check_list_obj->get_checked_list ;
924             return if $idx >= $#new_list ; # last item selected
925             $check_list_obj->move_down($item) ;
926             $update_value->($idx + 1) ;
927             } ;
928              
929             my @buttons = (
930             {
931             -label => '< up >', -onpress => $up_sub } ,
932             {
933             -label => '< down >', -onpress => $down_sub } ,
934             ) ;
935              
936             $self->add_std_button_with_help($win,$node,$element, @buttons ) ;
937              
938             $listbox->focus ;
939              
940             return $listbox ;
941             }
942             ## end check_list
943              
944             sub display_leaf {
945             my ($self,$node,$element,$index,$leaf) = @_ ;
946              
947             my $win = $self->set_center_window($element);
948              
949             my $editor = $self->layout_leaf_value($win,$node,$element,$index,$leaf ) ;
950              
951             $editor -> focus;
952             $self->add_std_button_with_help($win,$node,$element) ;
953             $self->wrap_screen($node,$element,$index);
954             }
955              
956             sub layout_leaf_value
957             {
958             goto &layout_string_value ;
959             }
960              
961             sub set_leaf_value {
962             my ($self,$leaf,$new) = @_ ;
963              
964             my $sub = sub {
965             no warnings "uninitialized" ;
966             warn "set_leaf_value: ", $leaf->name,"-> store( $new )\n";
967             my $v = $leaf->store($new);
968             } ;
969              
970             $self->try_it($sub) ;
971             }
972              
973             sub try_it {
974             my ($self,$sub) = @_ ;
975              
976             eval {
977             &$sub ;
978             warn "try_it: call to sub succeeded\n" if $verb_wiz ;
979             } ;
980              
981             my $e = $@;
982             if (ref($e) and $e -> isa('Config::Model::Exception::User')) {
983             my $oops = $e->full_message ;
984             $oops =~ s/\t//g;
985             chomp($oops) ;
986             $self->{cui}->error(-message => $oops ) ;
987             return undef;
988             }
989             elsif ($@) {
990             warn $@ ;
991             $self->{cui}->fatalerror("try_it: $@") ;
992             # does not return ...
993             }
994             }
995              
996             sub display_enum {
997             my ($self,$node,$element,$index, $leaf) = @_ ;
998              
999             my $win = $self->set_center_window("display_enum $element");
1000              
1001             my $lb = $self->layout_enum_value($win,$node,$element,$index, $leaf) ;
1002              
1003             my $but = { -label => '< OK >',
1004             -onpress => sub {$self->back} } ;
1005              
1006             $lb->focus ;
1007             $self->add_std_button_with_help($win,$node,$element,$but) ;
1008             $self->wrap_screen($node,$element,$index);
1009             }
1010              
1011             sub layout_enum_value {
1012             my ($self,$win,$node,$element,$index, $leaf) = @_ ;
1013              
1014             $self->add_debug_label($win) ;
1015              
1016             my ($orig_value,$current_value_widget,$help) =
1017             $self->value_info($win,$leaf, 40, 1) ;
1018              
1019             $help -> text ($leaf->get_help_as_text($orig_value) ) ;
1020              
1021             my $y = 0;
1022              
1023             if ($leaf->value_type eq 'reference') {
1024             $win -> add ( undef, 'Label',
1025             '-y' => $y++,
1026             -text => "Enum values are taken from:"
1027             ) ;
1028              
1029             foreach my $c_obj ($leaf->reference_object->compute_obj) {
1030             my $button ;
1031             my $path = $c_obj -> user_formula ;
1032             if (defined $path) {
1033             my $target = $leaf->grab($path) ;
1034             my $p_target = $target->parent ;
1035             my $n_target = $target->element_name ;
1036             my $go = sub { $self->scan('element',$p_target, $n_target) ; } ;
1037             $button = { -label => "< go to '$path' >", -onpress => $go } ;
1038             }
1039             else {
1040             my $go = sub {$self->{cui}->fatalerror( $c_obj->compute_info )} ;
1041             $button = { -label => "< info on undef '$path' >",
1042             -onpress => $go } ;
1043             }
1044             $win -> add ( undef, 'Buttonbox',
1045             '-y' => $y++,
1046             '-x' => 0 ,
1047             -buttons => [ $button ] ,
1048             ) ;
1049             }
1050             $y ++ ;
1051             }
1052              
1053             $win -> add ( undef, 'Label',
1054             '-y' => $y,
1055             -text => "Select new value.\nPress for a"
1056             . "'less'-like\nsearch through the choice list."
1057             ) ;
1058             $y += 3 ;
1059              
1060             my $listbox ;
1061             my $value = $orig_value ;
1062              
1063             my $lb_change = sub {
1064             my ($new) = $listbox->get;
1065             if (not defined $orig_value or $new ne $value) {
1066             $self->set_leaf_value($leaf,$new);
1067             $value = $new ;
1068             $current_value_widget->text($new) ;
1069             }
1070             } ;
1071              
1072             my $lb_sel_change = sub {
1073             my ($new) = $listbox->get_active_value;
1074             $help ->text($leaf->get_help_as_text($new)) ;
1075             } ;
1076              
1077             $listbox = $win -> add ( undef, 'Listbox',
1078             '-y' => $y ,
1079             -padbottom => 1,
1080             -values => $leaf->choice,
1081             -width => 35,
1082             -border => 1,
1083             -title => 'Enum choice',
1084             -vscrollbar => 1,
1085             -onchange => $lb_change ,
1086             -onselchange => $lb_sel_change ,
1087             ) ;
1088              
1089             return $listbox ;
1090             }
1091              
1092              
1093             sub display_boolean {
1094             my ($self,$node,$element,$index, $leaf) = @_ ;
1095              
1096             my $win = $self->set_center_window("display_boolean $element");
1097              
1098             my $listbox = $self->layout_boolean_value($win,$node,$element,$index, $leaf) ;
1099             $listbox->focus;
1100              
1101             my $but = { -label => '< OK >',
1102             -onpress => sub {$self->back} } ;
1103              
1104             $self->add_std_button_with_help($win,$node,$element,$but) ;
1105             $self->wrap_screen($node,$element,$index);
1106             }
1107              
1108             sub layout_boolean_value {
1109             my ($self,$win,$node,$element,$index, $leaf) = @_ ;
1110              
1111             my ($orig_value,$current_value_widget, $help)
1112             = $self->value_info($win,$leaf, 0, 4, 75) ;
1113              
1114             $orig_value ||= 0 ; # avoid undef boolean values
1115             my $value = $orig_value ;
1116             my $check_box ;
1117              
1118             my $set = sub {
1119             my ($new) = $check_box->get;
1120             if (not defined $orig_value or $new ne $value) {
1121             $self->set_leaf_value($leaf , 0+$new ) ;
1122             $value = $new ;
1123             $current_value_widget->text( 0+$new ) ;
1124             $help ->text($leaf->get_help_as_text($new ? '1' : '0')) ;
1125             }
1126             } ;
1127              
1128             $check_box = $win -> add ( undef, 'Checkbox',
1129             -label => "Toggle checkbox for new value",
1130             '-y' => 1,
1131             -checked => $orig_value ,
1132             -onchange => $set
1133             ) ;
1134              
1135             my $reset = sub {
1136             my $meth = $orig_value == 1 ? 'check' : 'uncheck' ;
1137             $check_box->$meth() ;
1138             $check_box ->draw ;
1139             $set->() ;
1140             } ;
1141              
1142             $win->add(undef,
1143             'Buttonbox',
1144             '-y' => 2 ,
1145             '-x' => 0 ,
1146             '-width' => 40 ,
1147             -buttons =>
1148             [ { -label => '< Reset value >', -onpress => $reset} ]
1149             ) ;
1150              
1151             return $check_box ;
1152             }
1153              
1154             sub display_string {
1155             my ($self,$node,$element,$index, $leaf) = @_ ;
1156              
1157             my $win = $self->set_center_window("display_string_v $element");
1158              
1159             my $editor = $self->layout_string_value($win,$node,$element,$index, $leaf ) ;
1160             $editor -> focus;
1161              
1162             my $but = { -label => '< OK >',
1163             -onpress => sub {$self->back} } ;
1164             $self->add_std_button_with_help($win,$node,$element, $but) ;
1165             $self->wrap_screen($node,$element,$index);
1166             }
1167              
1168             sub layout_string_value {
1169             my ($self,$win,$node,$element,$index, $leaf) = @_ ;
1170              
1171             $self->add_debug_label($win) ;
1172             my $v_type = $leaf->value_type;
1173             my $height = $v_type eq 'uniline' ? 1 : 4 ;
1174              
1175             my ($orig_value,$current_value_widget, $help)
1176             = $self->value_info($win,$leaf, 0, $height + 2 , 75) ;
1177              
1178             $win -> add ( undef, 'Label', '-y' => 0, -bold => 1,
1179             -text => "Enter new value:") ;
1180              
1181             my $editor = $win -> add ( undef,
1182             $v_type eq 'string' ? 'TextEditor' : 'TextEntry',
1183             -sbborder => 1,
1184             '-y' => 1,
1185             '-height' => $height,
1186             -width => 70,
1187             -wrapping => 1,
1188             -showhardreturns => 1,
1189             -text => $orig_value
1190             );
1191              
1192              
1193             my $value = $orig_value ;
1194             my $store = sub {
1195             my ($new) = $editor->get;
1196             if (not defined $orig_value or $new ne $value) {
1197             $self->set_leaf_value($leaf,$new) ;
1198             $value = $new ;
1199             $current_value_widget->text($new) ;
1200             }
1201             else {
1202             $editor -> focus;
1203             }
1204             } ;
1205              
1206             my $reset = sub {
1207             my $reset_value = defined $orig_value ? $orig_value : '';
1208             $self->set_leaf_value($leaf , $orig_value );
1209             $editor->text($orig_value || '') ;
1210             $current_value_widget->text($reset_value) ;
1211             } ;
1212              
1213             $win->add(undef,
1214             'Buttonbox',
1215             '-y' => $height + 1 ,
1216             '-x' => 0 ,
1217             '-width' => 40 ,
1218             -buttons =>
1219             [ { -label => '< Reset value >', -onpress => $reset},
1220             {
1221             -label => '< store >', -onpress => $store }
1222             ]
1223             ) ;
1224              
1225             return $editor ;
1226             }
1227              
1228             sub value_info {
1229             my ($self,$win,$leaf, $x,$y, $width) = @_ ;
1230             my $inst = $leaf->instance ;
1231              
1232             no warnings "uninitialized";
1233             my $value = $leaf->fetch(check => 'no') ;
1234             $win -> add ( undef, 'Label', -text => "current value: ",
1235             '-x' => $x, '-y' => $y ) ;
1236             my $display_value = defined $value ? $value : '' ;
1237             my $cur_win =
1238             $win -> add ( undef, 'Label', -text => $display_value ,
1239             -bg => 'yellow',
1240             -width => $width || 35 ,
1241             '-x' => $x + 15, '-y' => $y++ ) ;
1242              
1243             my @items = ();
1244             if (defined $leaf->upstream_default) {
1245             push @items, "upstream_default value: " . $leaf->upstream_default ;
1246             }
1247             elsif (defined $leaf->fetch_standard) {
1248             push @items, "default value: " . $leaf->fetch_standard ;
1249             }
1250              
1251             my $m = $leaf->mandatory ;
1252             push @items, "is mandatory: ".($m ? 'yes':'no') if defined $m;
1253              
1254             my @minmax ;
1255             foreach my $what (qw/min max/) {
1256             my $v = $leaf->$what() ;
1257             push @minmax, "$what: $v" if defined $v;
1258             }
1259              
1260             push @items, join(', ',@minmax) if @minmax ;
1261              
1262             $win -> add ( undef, 'Label',
1263             '-x' => $x, '-y' => $y,
1264             '-text' => join("\n",@items),
1265             ) ;
1266             my $help =
1267             $win -> add ( undef, 'TextViewer',
1268             '-x' => $x ,
1269             '-y' => $y + scalar @items ,
1270             -width => $width || 35,
1271             '-title' => 'Help on value',
1272             @help_settings ) ;
1273              
1274             return ($value, $cur_win, $help) ;
1275             }
1276              
1277             sub create_menu {
1278             my $self = shift ;
1279              
1280             $self->{cui}->delete('menu') ;
1281              
1282             my $file_menu = [
1283             { -label => 'Quit',
1284             -value => sub { exit(0) ;}
1285             },
1286             ];
1287              
1288             my $menu = [ { -label => 'File', -submenu => $file_menu }, ];
1289              
1290             $self->{cui}->add('menu', 'Menubar', -menu => $menu);
1291             }
1292              
1293             sub create_config_menu {
1294             my ($self,$label) = @_ ;
1295              
1296             $self->{cui}->delete('menu') ;
1297              
1298             my $file_menu
1299             = [
1300             { -label => 'Commit config' ,
1301             -value => sub {$self->store_config($label)} },
1302             { -label => 'Go back to config root',
1303             -value => $self->{start_config}},
1304             { -label => 'Reset config' ,
1305             -value => sub {$self->reset_config($label)} },
1306             {
1307             -label => 'Abort config', -value => $self->{start_all} },
1308             ];
1309              
1310             my @menu_data = ( ['View', 'std',' tree' ],
1311             ['View Audit', 'audit','tree' ],
1312             ['Tabular View', 'std', 'tabular'],
1313             ['Tabular View Audit', 'audit','tabular'],
1314             ) ;
1315              
1316             my @nav_menu ;
1317             foreach my $i (@menu_data) {
1318             my $sub = sub {
1319             $self->display_view_list(
1320             $self->{displayed_object} || $self->{root},
1321             $i->[1],$i->[2]
1322             ) ;
1323             };
1324             push @nav_menu , {-label => $i->[0], -value => $sub } ;
1325             }
1326              
1327             my $menu = [
1328             {
1329             -label => 'File', -submenu => $file_menu },
1330             {
1331             -label => 'Navigate', -submenu => \@nav_menu }
1332             ];
1333              
1334             $self->{cui}->add('menu', 'Menubar', -menu => $menu);
1335             }
1336              
1337             sub add_std_button_with_help {
1338             my ($self,$win,$node,$element,@buttons) = @_ ;
1339              
1340             my $help = $self->show_node_element_help($node,$element) ;
1341              
1342             unshift @buttons, { -label => '< More help >',
1343             -onpress => sub{$self->{cui}->dialog($help);}
1344             }
1345             if $help ;
1346              
1347             $self->add_std_button($win,$node,$element,@buttons) ;
1348             }
1349              
1350             sub add_std_button {
1351             my ($self,$win,$node,$element,@buttons) = @_ ;
1352              
1353             my $up = defined $node ? sub {$self->scan('node',$node);}
1354             : $self->{start_config} ;
1355              
1356             unshift @buttons,
1357             { -label => '< Back >',
1358             -onpress => sub {$self->back}
1359             },
1360             {
1361             -label => '< Up >',
1362             -onpress => $up
1363             },
1364             {
1365             -label => '< Reset >',
1366             -onpress => sub {$self->reset_screen ;}
1367             },
1368             {
1369             -label => '< Top >',
1370             -onpress => $self->{start_config}
1371             } ;
1372              
1373             $win->add (undef, 'Buttonbox',
1374             '-y' => $win->canvasheight-1 ,
1375             -buttonalignment => 'middle',
1376             -buttons => \@buttons,
1377             -selected => $#buttons, # select last button
1378             ) ;
1379             }
1380              
1381              
1382             ##### explore with Searcher
1383              
1384             sub display_all_elements {
1385             my ($self,$root) = @_;
1386              
1387             unless (defined $self->{searcher}) {
1388             $self->{searcher} = $root->searcher ;
1389             }
1390              
1391             my $searcher = $self->{searcher} ;
1392              
1393             my $win = $self->set_center_window("Search for an element");
1394              
1395             $win -> add ( undef, 'Label',
1396             -text => "Select the element you're looking for. \n"
1397             . "Press for a"
1398             . "'less'-like search through the list."
1399             ) ;
1400              
1401             my @searchable_elements = $self->{searcher}->get_searchable_elements ;
1402              
1403              
1404             # The searcher must be set in manual mode
1405              
1406             my $listbox ;
1407             my $sub = sub {
1408             my ($searched) = $listbox->get;
1409             $searcher->prepare(element => $searched) ;
1410             my $choices = $searcher->next_choice ;
1411             if (@$choices ) {
1412             $self->display_possible_element ($root,@$choices) ;
1413             }
1414             else {
1415             # go fetch the searched object
1416             my $target = $searcher->current_object ;
1417             warn "Search found ",$target->name,"\n";
1418             }
1419             } ;
1420              
1421             $listbox = $win -> add (
1422             undef, 'Listbox',
1423             '-y' => 3,
1424             -values => \@searchable_elements,
1425             -width => 30,
1426             -border => 1,
1427             -title => 'Search element',
1428             -vscrollbar => 1,
1429             -onchange => $sub ,
1430             ) ;
1431              
1432             $listbox->focus ;
1433              
1434             #$self->add_std_button($win,$node,$but) ;
1435             push @{$self->{stack}} , sub{$self->display_all_elements($root)};
1436             }
1437              
1438             sub search_dispatch {
1439             my ($self, $object) = @_ ;
1440             my $obj_type = $object->get_type ;
1441             my $elt_name = $object->element_name ;
1442             my $idx_value = $object->index_value ;
1443              
1444             my $scan_type = $obj_type eq 'leaf' ? 'element'
1445             : $obj_type ;
1446             my $scan_object = $obj_type eq 'leaf' ? $object->parent : $object ;
1447              
1448             $self->scan($scan_type, $scan_object, $elt_name, $idx_value ) ;
1449             }
1450              
1451             sub add_id_elt_in_search {
1452             my ($self,$node,$element,@keys) = @_ ;
1453              
1454             my $win = $self->set_center_window(ucfirst($node->element_type($element)));
1455              
1456             my $listbox = $self->layout_hash($win, $node,$element,@keys) ;
1457              
1458             my @but =
1459             (
1460             { -label => '< Done >',
1461             -onpress => sub
1462             {
1463             my @sel = $listbox->get;
1464             if (scalar @sel) {
1465             $self->search_choose_jump($sel[0]) ;
1466             }
1467             else {
1468             $self->{cui}->error(-message => "Please select an id");
1469             }
1470             }
1471             }
1472             ) ;
1473              
1474             $self->add_std_button_with_help($win,$node,$element,@but) ;
1475              
1476             $self->wrap_screen($node,$element) ;
1477             return $win ;
1478              
1479             }
1480              
1481             sub search_choose_jump {
1482             my $self = shift ;
1483             my $id = shift ;
1484             $self->{searcher}->choose($id) ;
1485             warn "choose $id\n";
1486             my $next_choices = $self->{searcher}->next_choice ;
1487             my $next_object = $self->{searcher}->current_object ;
1488             warn "jump: to ",$next_object->name," with @$next_choices\n";
1489              
1490             if ($next_object->get_type =~ /list|hash/ or scalar @$next_choices ) {
1491             $self->display_possible_element ($next_object,@$next_choices) ;
1492             }
1493             else {
1494             # go fetch the searched object
1495             warn "Search found ",$next_object->name,"\n";
1496             $self->search_dispatch($next_object) ;
1497             }
1498             }
1499              
1500             sub display_possible_element
1501             {
1502             my ($self,$object, @choices) = @_;
1503              
1504             $self->update_location($object) ;
1505              
1506             my $obj_type = $object->get_type ;
1507             my $elt_name = $object->element_name ;
1508             my $idx_value = $object->index_value ;
1509              
1510             my $searched = $self->{searcher}->searched ;
1511              
1512             my $win = $self->set_center_window("Select a path for $searched");
1513              
1514             $win -> add ( undef, 'Label',
1515             -text => "'$searched' can be found in all these\n"
1516             . "configuration elements. Please select one.");
1517              
1518             $self->add_debug_label($win) ;
1519              
1520             if ($obj_type eq 'list' or $obj_type eq 'hash') {
1521             $win->add (undef, 'Buttonbox',
1522             '-y'=> 3 ,
1523             -buttons =>
1524             [
1525             {
1526             -label => "< jump to '$elt_name' to add an id >",
1527             -onpress => sub{$self->add_id_elt_in_search($object->parent,$elt_name,@choices) ;},
1528             },
1529             ]
1530             ) ;
1531             }
1532              
1533             my $jump = sub {
1534             my $id = shift->get;
1535             $self -> search_choose_jump($id) ;
1536             } ;
1537              
1538             my $listbox = $win -> add
1539             ( undef, 'Listbox',
1540             '-y' => 5,
1541             -values => \@choices,
1542             -width => 30,
1543             -border => 1,
1544             -title => 'Select path',
1545             -vscrollbar => 1,
1546             -onchange => $jump ,
1547             ) ;
1548              
1549             $listbox->focus ;
1550              
1551             #$self->add_std_button($win,$node,$but) ;
1552             push @{$self->{stack}} ,
1553             sub{$self->display_possible_element($object,@choices)};
1554              
1555             }
1556              
1557             ##### explore through view like list
1558              
1559             sub display_view_list {
1560             my ($self,$root,$select,$view_type,$pre_select) = @_;
1561              
1562             # reset location label
1563             $self->{loc_label}->text('') ;
1564              
1565             my $audit_cb = sub {
1566             my ($scanner, $data_ref,$node,$element_name,$index, $leaf_object) = @_ ;
1567             my $custom = $leaf_object->fetch_custom;
1568             push @$data_ref, [ $node,$element_name,$index , $custom ] if defined $custom;
1569             } ;
1570              
1571             my $std_cb = sub {
1572             my ($scanner, $data_ref,$node,$element_name,$index, $leaf_object) = @_ ;
1573             my $value = $leaf_object->fetch(check => 'no') ;
1574             my $value_str = length($value) ? $value
1575             : $leaf_object->mandatory ? '*MISSING*'
1576             : undef ;
1577             $value_str = '"'.$value_str.'"' if defined $value_str && $value_str =~ /\s/ ;
1578             push @$data_ref, [ $node, $element_name, $index , $value_str ] ;
1579             } ;
1580              
1581             my $hash_cb = sub {
1582             my ($scanner, $data_ref,$node,$element_name,@keys) = @_ ;
1583              
1584             foreach my $k (@keys) {
1585             push @$data_ref, [ $node, $element_name, undef, $k ] ;
1586             $scanner->scan_hash($data_ref,$node,$element_name,$k) ;
1587             }
1588             } ;
1589              
1590             my $node_cb = sub {
1591             my ($scanner, $data_ref,$node,$element_name,$key, $contained_node) = @_ ;
1592             push @$data_ref, [ $node, $element_name, $key ] ;
1593             $scanner->scan_node($data_ref,$contained_node);
1594             } ;
1595              
1596             my $leaf_cb = ($select eq 'audit') ? $audit_cb : $std_cb ;
1597              
1598             my @scan_args = (
1599             fallback => 'all',
1600             hash_element_cb => $hash_cb ,
1601             leaf_cb => $leaf_cb ,
1602             node_element_cb => $node_cb ,
1603             check => 'no',
1604             );
1605              
1606             my $view_scanner = Config::Model::ObjTreeScanner->new (@scan_args);
1607              
1608             my @leaves ;
1609             eval {
1610             # perform the scan that fills @leaves
1611             $view_scanner-> scan_node(\@leaves, $root) ;
1612             } ;
1613              
1614             if ($@) {
1615             warn "$@" ;
1616             $self->{cui}->fatalerror("display_view_list: $@") ;
1617             }
1618             ;
1619              
1620             my $idx = 0;
1621             my @good_leaves = $view_type eq 'tree' ? @leaves : grep { @$_ == 4 } @leaves ;
1622              
1623             my %labels = map {
1624             my ($node,$element,$index,$value) = @$_ ;
1625             my $name = defined $index ? "$element:$index" : $element ;
1626             my $loc = $node->location ;
1627             no warnings "uninitialized" ;
1628             my $str ;
1629             if ($view_type eq 'tabular') {
1630             $str =sprintf("%-28s | %-10s | %-30s", $name,$value,$node->name) ;
1631             }
1632             else {
1633             my @level = split m/ +/ ,$loc ;
1634             $str = ('. ' x scalar @level) . $name ;
1635             $str .= " = '$value'" if @$_ == 4;
1636             }
1637             ($idx++,$str) ;
1638             } @good_leaves ;
1639              
1640             my $win = $self->set_center_window("View ".$root->name);
1641              
1642             $win -> add ( undef, 'Label',
1643             -text => "Select the item you're looking for. \n"
1644             . "Press for a "
1645             . "'less'-like search through the list."
1646             ) ;
1647              
1648             my $listbox ;
1649             my $sub = sub {
1650             my ($searched) = $listbox->get;
1651             my ($node,$element,$index,$value) = @{$good_leaves[$searched]} ;
1652              
1653             # replace call with a call with a selected value
1654             pop @{$self->{stack}} ;
1655             push @{$self->{stack}} ,
1656             sub{$self->display_view_list($root,$select,$view_type,$searched)};
1657              
1658             if (defined $index) {
1659             $self->scan('hash',$node,$element,$index) ;
1660             }
1661             else {
1662             $self->scan('element',$node,$element) ;
1663             }
1664             } ;
1665              
1666             $listbox = $win -> add ( undef, 'Listbox',
1667             '-y' => 3,
1668             -values => [0 .. $#good_leaves],
1669             -labels => \%labels ,
1670             -border => 1,
1671             -title => 'Search element',
1672             -vscrollbar => 1,
1673             -onchange => $sub ,
1674             -selected => $pre_select
1675             ) ;
1676              
1677             $listbox->focus ;
1678              
1679             #$self->add_std_button($win,$node,$but) ;
1680             push @{$self->{stack}} ,
1681             sub{$self->display_view_list($root,$select,$view_type,$pre_select)};
1682              
1683             }
1684              
1685              
1686             ##### wizard: explore depth first and stop on "important" or undefined
1687             ##### mandatory elements (or on erroneous elements ?)
1688             sub wizard {
1689             my ($self,$root, $stop_on_important) = @_;
1690              
1691             # reset location label
1692             $self->{loc_label}->text('') ;
1693              
1694             eval {
1695             $self->wiz_walk( $stop_on_important , $root) ;
1696             } ;
1697              
1698             my $e = $@;
1699             if ( ref($e) and $e->isa('Config::Model::CursesUI::AbortWizard')) {
1700             # ignored
1701             }
1702             elsif ($@) {
1703             warn "$@" ;
1704             $self->{cui}->fatalerror("search: $@") ;
1705             }
1706             ;
1707              
1708             $self->{start_config}->() ;
1709             }
1710              
1711             # do not delete
1712             sub display_hash_wizard {
1713             my ($self, $node, $element) = @_ ;
1714             my $win = $self->set_center_window('wizard') ;
1715              
1716             $self->layout_hash($win,$node,$element)->focus ;
1717             $self->update_location($node, $element) ;
1718             $self->wrap_wizard($win, $node, $element) ;
1719             }
1720              
1721             sub show_node_element_help {
1722             my ($self,$node, $element) = @_ ;
1723             my $text = '' ;
1724              
1725             return $text unless defined $node ;
1726             my $node_help = $node->get_help_as_text();
1727              
1728             my $element_name = $node->element_name() ; # may be undef for root class
1729             if ($node_help) {
1730             $text .= "$element_name:\n " if defined $element_name;
1731             $text .= "$node_help\n" ;
1732             }
1733              
1734             if (defined $element) {
1735             my $element_help = $node->get_help_as_text($element);
1736             $text .= "$element:\n $element_help\n" if $element_help ;
1737             }
1738              
1739             return $text ;
1740             }
1741              
1742             my $loop_c = 0 ;
1743              
1744             sub wrap_wizard {
1745             my ($self,$win, $node, $element) = @_ ;
1746              
1747             my $keep_wiz = 1 ;
1748             my $abort_wiz = 0 ;
1749              
1750             my @buttons
1751             = (
1752             {
1753             -label => '< Exit wizard >',
1754             -onpress => sub {$keep_wiz=0 ; $abort_wiz = 1 ;}
1755             }
1756             );
1757              
1758             my $help = $self->show_node_element_help($node, $element) ;
1759              
1760             push @buttons, {
1761             -label => '< More help >',
1762             -onpress => sub { $self->{cui}->dialog($help) ;}
1763             } if $help ;
1764              
1765             push @buttons, {
1766             -label => '< Back >',
1767             -onpress => sub {$self->{wizard}->go_backward ; $keep_wiz = 0 ;}
1768             },
1769             {
1770             -label => "< Next >",
1771             -onpress => sub {$self->{wizard}->go_forward ; $keep_wiz = 0 ;}
1772             } ;
1773              
1774             my $buttons = $win->add ( undef, 'Buttonbox',
1775             '-y' => $win->canvasheight-1 ,
1776             -buttonalignment => 'middle',
1777             -selected => $#buttons , # select < Next > at startup
1778             -buttons => \@buttons
1779             ) ;
1780              
1781             $buttons -> focus ;
1782              
1783             $self->{cui}->draw ;
1784              
1785             warn "entered local loop ",++$loop_c,"\n";
1786             while ($keep_wiz) {
1787             $self->{cui}->do_one_event ;
1788             }
1789             warn "exited local loop ",$loop_c,"\n";
1790              
1791             $self->{cui}->delete('wizard');
1792              
1793             if ($abort_wiz) {
1794             Config::Model::CursesUI::AbortWizard->throw ;
1795             }
1796             }
1797              
1798             # callback is used for tests only
1799             sub wiz_walk {
1800             my ($self, $stop_on_important , $root) = @_ ;
1801              
1802             # mode can be wizard or error check
1803             warn "wiz_walk called on '", $root->name, "'\n"
1804             if $verb_wiz;
1805              
1806             my ($sort_element, $sort_idx) ;
1807              
1808             my $hash_element_cb = sub
1809             {
1810             my ($scanner, $data_ref,$node,$element_name,@keys) = @_ ;
1811              
1812             warn "wiz_walk, hash_cb (element $element_name) called on '", $node->name,
1813             "' keys: '@keys' \n" if $verb_wiz;
1814             $self->display_hash_wizard($node, $element_name) ;
1815             } ;
1816              
1817             my %cb_hash ;
1818             my %override_meth = ( integer_value => 'layout_string_value',
1819             number_value => 'layout_string_value',
1820             leaf => 'layout_leaf_value',
1821             check_list_element => 'layout_checklist' ,
1822             ) ;
1823              
1824             foreach my $leaf_item (qw/leaf enum_value
1825             integer_value number_value
1826             boolean_value string_value/) {
1827             my $layout_meth = $override_meth{$leaf_item} || 'layout_'.$leaf_item ;
1828             $cb_hash{$leaf_item.'_cb'} = sub {
1829             my @cb_args = @_ ;
1830             splice @cb_args,0,2; # remove scanner and data_ref from cb args
1831             warn "called $layout_meth for $leaf_item";
1832             my $win = $self->set_center_window('wizard') ;
1833             $self->$layout_meth($win,@cb_args) ;
1834             $self->update_location(@cb_args) ;
1835             $self->wrap_wizard($win,@cb_args) ;
1836             } ;
1837             }
1838              
1839             my @wiz_args = (
1840             hash_element_cb => $hash_element_cb ,
1841             %cb_hash
1842             );
1843              
1844             #Tk::ObjScanner::scan_object(\@wiz_args) ;
1845             $self->{wizard} = $root->instance->iterator (@wiz_args);
1846              
1847             my $result;
1848             eval {$self->{wizard}->start ;} ;
1849             my $e = $@;
1850              
1851             if (ref($e) and $e->isa('Config::Model::CursesUI::AbortWizard')) {
1852             $e -> rethrow ; # propagate up
1853             }
1854             elsif ($e) {
1855             # really die
1856             warn "$e" ;
1857             $self->{cui}->fatalerror("display_view_list: $e") ;
1858             }
1859              
1860             return $result ;
1861             }
1862              
1863             package Config::Model::CursesUI::AbortWizard;
1864             use Mouse;
1865             extends 'Config::Model::Exception';
1866              
1867             sub _desc { 'wizard found a highlighted item' }
1868              
1869             1;
1870             __END__