File Coverage

blib/lib/Config/Model/CursesUI.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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