File Coverage

lib/Devel/ebug/Wx/View/Expressions.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Devel::ebug::Wx::View::Expressions;
2              
3 1     1   1845 use Wx;
  0            
  0            
4              
5             use strict;
6             use base qw(Wx::Panel Devel::ebug::Wx::View::Base);
7             use Devel::ebug::Wx::Plugin qw(:plugin);
8              
9             # FIXME: ought to be a service, too
10             __PACKAGE__->mk_accessors( qw(tree model) );
11              
12             use Wx qw(:treectrl :textctrl :sizer WXK_DELETE);
13             use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_EXPANDING EVT_TEXT_ENTER
14             EVT_TREE_BEGIN_LABEL_EDIT EVT_TREE_END_LABEL_EDIT
15             EVT_TREE_KEY_DOWN);
16             use Wx::Perl::TreeView;
17              
18             sub tag { 'expressions' }
19             sub description { 'Expressions' }
20              
21             # FIXME backport to wxPerl
22             sub _call_on_idle($&) {
23             my( $window, $code ) = @_;
24              
25             use Wx::Event qw(EVT_IDLE);
26             # Disconnecting like this is unsafe...
27             my $callback = sub {
28             EVT_IDLE( $window, undef );
29             $code->();
30             };
31             EVT_IDLE( $window, $callback );
32             }
33              
34             sub new : View {
35             my( $class, $parent, $wxebug, $layout_state ) = @_;
36             my $self = $class->SUPER::new( $parent, -1 );
37              
38             $self->wxebug( $wxebug );
39             my $tree = Wx::TreeCtrl->new( $self, -1, [-1,-1], [-1,-1],
40             wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS |
41             wxTR_EDIT_LABELS );
42             $self->{model} = Devel::ebug::Wx::View::Expressions::Model->new
43             ( { _expressions => [],
44             _values => [],
45             ebug => $self->ebug } );
46             $self->{tree} = Wx::Perl::TreeView->new( $tree, $self->model );
47              
48             my $refresh = Wx::Button->new( $self, -1, 'Refresh' );
49             my $add = Wx::Button->new( $self, -1, 'Add' );
50             my $expression = Wx::TextCtrl->new( $self, -1, '', [-1, -1], [-1, -1],
51             wxTE_PROCESS_ENTER );
52              
53             my $sz = Wx::BoxSizer->new( wxVERTICAL );
54             my $cntrl = Wx::BoxSizer->new( wxHORIZONTAL );
55             $cntrl->Add( $refresh, 0, 0 );
56             $cntrl->Add( $add, 0, 0 );
57             $cntrl->Add( $expression, 1, 0 );
58             $sz->Add( $cntrl, 0, wxGROW );
59             $sz->Add( $self->tree->treectrl, 1, wxGROW );
60             $self->SetSizer( $sz );
61              
62             $self->subscribe_ebug( 'state_changed', sub { $self->_refresh( @_ ) } );
63             $self->set_layout_state( $layout_state ) if $layout_state;
64             $self->register_view;
65              
66             EVT_BUTTON( $self, $refresh, sub { $self->refresh } );
67             EVT_BUTTON( $self, $add, sub {
68             $self->add_expression( $expression->GetValue );
69             } );
70             EVT_TEXT_ENTER( $self, $expression,
71             sub { $self->add_expression( $expression->GetValue ) } );
72             EVT_TREE_BEGIN_LABEL_EDIT( $self, $tree, \&_begin_edit );
73             EVT_TREE_END_LABEL_EDIT( $self, $tree, \&_end_edit );
74             EVT_TREE_KEY_DOWN( $self, $tree, \&_key_down );
75              
76             $self->SetSize( $self->default_size );
77              
78             return $self;
79             }
80              
81             sub get_state {
82             my( $self ) = @_;
83              
84             return $self->model->_expressions;
85             }
86              
87             sub set_state {
88             my( $self, $state ) = @_;
89              
90             $self->model->{_expressions} = $state; # FIXME check
91             $self->refresh;
92             }
93              
94             sub add_expression {
95             my( $self, $expression ) = @_;
96              
97             $self->model->add_expression( $expression );
98             $self->refresh;
99             }
100              
101             sub _is_expression {
102             return $_[0]->GetItemParent( $_[1] ) == $_[0]->GetRootItem;
103             }
104              
105             sub _key_down {
106             my( $self, $event ) = @_;
107              
108             return unless $event->GetKeyCode == WXK_DELETE;
109             my $item = $event->GetItem || $self->tree->GetSelection;
110             return unless _is_expression( $self->tree, $item );
111             $self->model->delete_expression( $self->tree->GetPlData( $item ) );
112             _call_on_idle $self, sub { $self->refresh };
113             }
114              
115             # only allow editing root items
116             sub _begin_edit {
117             my( $self, $event ) = @_;
118             my $tree = $self->tree;
119              
120             if( !_is_expression( $tree, $event->GetItem ) ) {
121             $event->Veto;
122             } else {
123             my $expr = $tree->GetPlData( $event->GetItem )->{expression};
124             $tree->SetItemText( $event->GetItem, $expr );
125             }
126             }
127              
128             sub _end_edit {
129             my( $self, $event ) = @_;
130              
131             $self->tree->GetPlData( $event->GetItem )->{expression} = $event->GetLabel;
132             _call_on_idle $self, sub { $self->refresh };
133             }
134              
135             sub _refresh {
136             my( $self, $ebug, $event, %params ) = @_;
137              
138             $self->refresh;
139             }
140              
141             sub refresh {
142             my( $self ) = @_;
143              
144             $self->model->_values( [] );
145             $self->tree->refresh;
146             }
147              
148             package Devel::ebug::Wx::View::Expressions::Model;
149              
150             use strict;
151             use base qw(Wx::Perl::TreeView::Model Class::Accessor::Fast);
152              
153             __PACKAGE__->mk_accessors( qw(_expressions _values ebug) );
154              
155             sub expressions { @{$_[0]->_expressions} }
156              
157             sub add_expression {
158             my( $self, $expression ) = @_;
159              
160             push @{$self->_expressions}, { expression => $expression,
161             level => 0,
162             };
163             }
164              
165             sub delete_expression {
166             my( $self, $expression ) = @_;
167              
168             $self->_expressions( [ grep $_ ne $expression, $self->expressions ] );
169             }
170              
171             sub get_root { return ( '', 'root', undef, undef ) }
172              
173             sub _get {
174             my( $self, $index, $level ) = @_;
175             my $e = $self->_expressions->[$index];
176             if( $e->{level} < $level ) {
177             $e->{level} = $level + 1;
178             $self->_values->[$index] = undef;
179             }
180             my $r = $self->_values->[$index] ||=
181             [ reverse
182             $self->ebug->eval_level( $e->{expression}, $e->{level} ) ];
183             return ( $e, $r );
184             }
185              
186             sub _find_node {
187             my( $self, $cookie, $more ) = @_;
188             my( $expr, @path ) = split /,/, $cookie;
189             my( $e, $r ) = _get( $self, $expr, @path + $more );
190             return _traverse( $self, $r, @path );
191             }
192              
193             sub _traverse {
194             my( $self, $r, @path ) = @_;
195             return $r if @path == 0;
196             return unless ref( $r->[1] ) && $r->[1]{keys};
197             my $index = shift @path;
198             return $r->[1]{keys}[$index] if @path == 0;
199             return _traverse( $self, $r->[1]{keys}[$index], @path );
200             }
201              
202             sub get_child_count {
203             my( $self, $cookie ) = @_;
204             return scalar $self->expressions unless length $cookie;
205             my $node = _find_node( $self, $cookie, -1 );
206             return 0 if $cookie !~ /,/ && $node->[0];
207             return $node->[1]{childs} || scalar @{$node->[1]{keys} || []};
208             }
209              
210             sub get_child {
211             my( $self, $cookie, $index ) = @_;
212              
213             if( !length $cookie ) {
214             my( $e, $r ) = _get( $self, $index, 0 );
215             if( $r->[0] ) {
216             chomp $r->[1];
217             return ( $index, "$e->{expression} = $r->[1]", undef, $e );
218             } else {
219             return ( $index, "$e->{expression} = $r->[1]->{string}", undef, $e );
220             }
221             } else {
222             my $el = _find_node( $self, "$cookie,$index", 0 );
223             return ( "$cookie,$index", $el->[0] . ' => ' . $el->[1]->{string} );
224             }
225             }
226              
227             1;