File Coverage

blib/lib/Circle/Session/Tabbed.pm
Criterion Covered Total %
statement 27 201 13.4
branch 0 84 0.0
condition 0 17 0.0
subroutine 9 29 31.0
pod 1 16 6.2
total 37 347 10.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of the GNU General Public License
2             #
3             # (C) Paul Evans, 2008-2014 -- leonerd@leonerd.org.uk
4              
5             package Circle::Session::Tabbed;
6              
7 1     1   7305 use strict;
  1         1  
  1         31  
8 1     1   4 use base qw( Tangence::Object Circle::Commandable Circle::Configurable );
  1         1  
  1         110  
9 1     1   5 use Carp;
  1         2  
  1         1085  
10              
11             sub _session_type
12             {
13 0     0     my ( $opts ) = @_;
14              
15 0 0         keys %$opts or return __PACKAGE__;
16              
17 0           print STDERR "Need Tabbed FE session for extra options:\n";
18 0           print STDERR " ".join( "|", sort keys %$opts )."\n";
19              
20 0           return undef;
21             }
22              
23             sub new
24             {
25 0     0 0   my $class = shift;
26 0           my %args = @_;
27              
28 0           my $self = $class->SUPER::new( %args );
29              
30 0           $self->{root} = $args{root};
31 0           $self->{identity} = $args{identity};
32              
33             # Start with just the root object in first tab
34              
35 0           $self->set_prop_tabs( [ $args{root} ] );
36 0           $self->{items} = {};
37              
38 0           return $self;
39             }
40              
41             sub items
42             {
43 0     0 0   my $self = shift;
44 0           return @{ $self->get_prop_tabs };
  0            
45             }
46              
47             sub describe
48             {
49 0     0 1   my $self = shift;
50 0           return __PACKAGE__."[$self->{identity}]";
51             }
52              
53             sub _item_to_index
54             {
55 0     0     my $self = shift;
56 0           my ( $item ) = @_;
57              
58 0           my @items = $self->items;
59 0   0       $items[$_] == $item and return $_ for 0 .. $#items;
60              
61 0           return undef;
62             }
63              
64             sub show_item
65             {
66 0     0 0   my $self = shift;
67 0           my ( $item ) = @_;
68              
69 0 0         return if grep { $_ == $item } $self->items;
  0            
70              
71 0           $self->push_prop_tabs( $item );
72             }
73              
74             sub unshow_item
75             {
76 0     0 0   my $self = shift;
77 0           my ( $item ) = @_;
78              
79 0           my $index;
80 0 0         if( ref $item ) {
81 0           $index = $self->_item_to_index( $item );
82 0 0         return unless defined $index;
83             }
84             else {
85 0           $index = $item;
86             }
87              
88 0           $self->splice_prop_tabs( $index, 1, () );
89             }
90              
91             sub new_item
92             {
93 0     0 0   my $self = shift;
94 0           my ( $item ) = @_;
95              
96             # Did we know about it?
97 0 0         return if exists $self->{items}->{$item};
98              
99 0           $self->{items}->{$item} = 1;
100 0           $self->show_item( $item );
101             }
102              
103             sub delete_item
104             {
105 0     0 0   my $self = shift;
106 0           my ( $item ) = @_;
107              
108 0           delete $self->{items}->{$item};
109 0           $self->unshow_item( $item );
110             }
111              
112             sub clonefrom
113             {
114 0     0 0   my $self = shift;
115 0           my ( $src ) = @_;
116              
117 0           my @srcitems = $src->items;
118              
119 0           foreach my $index ( 0 .. $#srcitems ) {
120 0           my $item = $srcitems[$index];
121              
122 0           my $curindex = $self->_item_to_index( $item );
123 0 0         if( !defined $curindex ) {
    0          
124 0           $self->splice_prop_tabs( $index, 0, $item );
125             }
126             elsif( $curindex != $index ) {
127 0           $self->move_prop_tabs( $curindex, $index - $curindex );
128             }
129             }
130              
131 0           $self->splice_prop_tabs( scalar @srcitems, scalar $self->items - scalar @srcitems, () );
132             }
133              
134             sub _get_item
135             {
136 0     0     my $self = shift;
137 0           my ( $path, $curitem, $create ) = @_;
138              
139 0 0 0       $curitem or $path =~ m{^/} or croak "Cannot walk a relative path without a start item";
140              
141 0 0         $curitem = $self->{root} if $path =~ s{^/}{};
142              
143 0           foreach ( split( m{/}, $path ) ) {
144 0 0         next unless length $_; # skip empty path elements
145              
146 0           my $nextitem;
147 0 0         if( $curitem->can( "get_item" ) ) {
    0          
148 0           $nextitem = $curitem->get_item( $_, $create );
149             }
150             elsif( $curitem->can( "enumerate_items" ) ) {
151 0           $nextitem = $curitem->enumerate_items->{$_};
152             }
153             else {
154 0           die "@{[ $curitem->describe ]} has no child items\n";
  0            
155             }
156              
157 0 0         defined $nextitem or die "@{[ $curitem->describe ]} has no child item called $_\n";
  0            
158              
159 0           $curitem = $nextitem;
160             }
161              
162 0           return $curitem;
163             }
164              
165             sub _cat_path
166             {
167 0     0     my ( $p, $q ) = @_;
168              
169 0 0         return $q if $p eq "";
170 0 0         return "/$q" if $p eq "/";
171 0           return "$p/$q";
172             }
173              
174             sub load_configuration
175             {
176 0     0 0   my $self = shift;
177 0           my ( $ynode ) = @_;
178              
179 0           $self->set_prop_tabs( [] );
180              
181 0           foreach my $tab ( @{ $ynode->{tabs} } ) {
  0            
182 0           my $item = $self->_get_item( $tab, $self->{root}, 1 );
183 0           $self->push_prop_tabs( $item );
184             }
185             }
186              
187             sub store_configuration
188             {
189 0     0 0   my $self = shift;
190 0           my ( $ynode ) = @_;
191              
192             $ynode->{tabs} = [ map {
193 0           my $item = $_;
  0            
194 0           my @components;
195 0           while( $item ) {
196 0           unshift @components, $item->enumerable_name;
197 0           $item = $item->parent;
198             }
199 0           join "/", @components;
200             } $self->items ];
201             }
202              
203             sub command_list
204             : Command_description("List showable window items")
205             : Command_arg('path?')
206             : Command_opt('all=+', desc => "list all the items")
207             {
208 0     0 0 0 my $self = shift;
209 0         0 my ( $itempath, $opts, $cinv ) = @_;
210              
211 0         0 my @items;
212            
213 0 0       0 if( $opts->{all} ) {
214 0         0 @items = ( [ "/" => $self->{root} ] );
215             }
216             else {
217 0         0 @items = ( [ "" => $cinv->invocant ] );
218             }
219              
220 0 0       0 if( defined $itempath ) {
221 0 0       0 if( $itempath =~ m{^/} ) {
222 0         0 $items[0]->[0] = $itempath;
223             }
224             else {
225 0         0 $items[0]->[0] .= $itempath;
226             }
227 0         0 $items[0]->[1] = $self->_get_item( $itempath, $items[0]->[1] );
228             }
229              
230 0 0       0 $cinv->respond( "The following items exist" . ( defined $itempath ? " from path $itempath" : "" ) );
231              
232             # Walk a tree without using a recursive function
233 0         0 my @table;
234 0         0 while( my $i = pop @items ) {
235 0         0 my ( $name, $item ) = @$i;
236              
237 0 0       0 push @table, [ " $name", ref($item) ] if length $name;
238              
239 0 0 0     0 if( my $subitems = $item->can( "enumerate_items" ) && $item->enumerate_items ) {
240 0         0 push @items, [ _cat_path( $name, $_ ) => $subitems->{$_} ] for reverse sort keys %$subitems;
241             }
242             }
243              
244 0         0 $cinv->respond_table( \@table, colsep => " - " );
245              
246 0         0 return;
247 1     1   6 }
  1         1  
  1         7  
248              
249             sub command_show
250             : Command_description("Show a window item")
251             : Command_arg("path?")
252             : Command_opt("all=+", desc => "show all the non-visible items")
253             {
254 0     0 0 0 my $self = shift;
255 0         0 my ( $itempath, $opts, $cinv ) = @_;
256              
257 0         0 my @items;
258 0 0       0 if( $opts->{all} ) {
    0          
259 0         0 my %visible = map { $_ => 1 } $self->items;
  0         0  
260              
261 0         0 my @more = ( $self->{root} );
262 0         0 while( my $item = pop @more ) {
263 0 0       0 push @items, $item if !$visible{$item};
264              
265 0 0 0     0 if( my $subitems = $item->can( "enumerate_items" ) && $item->enumerate_items ) {
266 0         0 push @more, @{$subitems}{sort keys %$subitems};
  0         0  
267             }
268             }
269             }
270             elsif( defined $itempath ) {
271 0         0 @items = $self->_get_item( $itempath, $cinv->invocant );
272             }
273             else {
274 0         0 $cinv->responderr( "show: require PATH or -all" );
275 0         0 return;
276             }
277              
278 0         0 $self->show_item( $_ ) for @items;
279              
280 0         0 return;
281 1     1   306 }
  1         2  
  1         3  
282              
283             sub command_hide
284             : Command_description("Hide a window item")
285             {
286 0     0 0 0 my $self = shift;
287 0         0 my ( $cinv ) = @_;
288              
289 0         0 my $item = $cinv->invocant;
290              
291 0 0       0 if( $item->isa( "Circle::RootObj" ) ) {
292 0         0 $cinv->responderr( "Cannot hide the global tab" );
293 0         0 return;
294             }
295              
296 0         0 $self->unshow_item( $item );
297              
298 0         0 return;
299 1     1   157 }
  1         1  
  1         3  
300              
301             sub command_tab
302             : Command_description("Manipulate window item tabs")
303       0 0   {
304 1     1   98 }
  1         2  
  1         3  
305              
306             sub command_tab_move
307             : Command_description("Move the tab to elsewhere in the window ordering\n".
308             "POSITION may be an absolute number starting from 1,\n".
309             " a relative number with a leading + or -,\n".
310             " one of first | left | right | last")
311             : Command_subof('tab')
312             : Command_arg('position')
313             {
314 0     0 0 0 my $self = shift;
315 0         0 my ( $position, $cinv ) = @_;
316              
317 0         0 my $tabs = $self->get_prop_tabs;
318              
319 0         0 my $item = $cinv->invocant;
320              
321 0         0 my $index;
322 0   0     0 $tabs->[$_] eq $item and ( $index = $_, last ) for 0 .. $#$tabs;
323              
324 0 0       0 defined $index or return $cinv->responderr( "Cannot find current index of item" );
325              
326 0 0       0 $position = "+1" if $position eq "right";
327 0 0       0 $position = "-1" if $position eq "left";
328 0 0       0 $position = "1" if $position eq "first"; # 1-based
329 0 0       0 $position = @$tabs if $position eq "last"; # 1-based
330              
331 0         0 my $delta;
332 0 0       0 if( $position =~ m/^[+-]/ ) {
    0          
333             # relative
334 0         0 $delta = $position+0;
335             }
336             elsif( $position =~ m/^\d+$/ ) {
337             # absolute; but input from user was 1-based.
338 0         0 $position -= 1;
339 0         0 $delta = $position - $index;
340             }
341             else {
342 0         0 return $cinv->responderr( "Unrecognised position/movement specification: $position" );
343             }
344              
345 0 0       0 return $cinv->responderr( "Cannot move that far left" ) if $index + $delta < 0;
346 0 0       0 return $cinv->responderr( "Cannot move that far right" ) if $index + $delta > $#$tabs;
347              
348 0         0 $self->move_prop_tabs( $index, $delta );
349 0         0 return;
350 1     1   285 }
  1         3  
  1         4  
351              
352             sub command_tab_goto
353             : Command_description("Activate a numbered tab\n".
354             "POSITION may be an absolute number starting from 1,\n".
355             " a relative number with a leading + or -,\n".
356             " one of first | left | right | last")
357             : Command_subof('tab')
358             : Command_arg('position')
359             {
360 0     0 0   my $self = shift;
361 0           my ( $position, $cinv ) = @_;
362              
363 0           my $tabs = $self->get_prop_tabs;
364              
365 0           my $item = $cinv->invocant;
366              
367 0           my $index;
368 0   0       $tabs->[$_] eq $item and ( $index = $_, last ) for 0 .. $#$tabs;
369              
370 0 0         defined $index or return $cinv->responderr( "Cannot find current index of item" );
371              
372 0 0         $position = "+1" if $position eq "right";
373 0 0         $position = "-1" if $position eq "left";
374 0 0         $position = "1" if $position eq "first"; # 1-based
375 0 0         $position = @$tabs if $position eq "last"; # 1-based
376              
377 0 0         if( $position =~ m/^[+-]/ ) {
    0          
378             # relative
379 0           $index += $position;
380             }
381             elsif( $position =~ m/^\d+$/ ) {
382             # absolute; but input from user was 1-based.
383 0           $index = $position - 1;
384             }
385             else {
386 0           return $cinv->responderr( "Unrecognised position/movement specification: $position" );
387             }
388              
389 0           $self->get_prop_tabs->[$index]->fire_event( raise => () );
390 0           return;
391 1     1   293 }
  1         2  
  1         3  
392              
393             0x55AA;