File Coverage

blib/lib/Circle/Session/Tabbed.pm
Criterion Covered Total %
statement 52 201 25.8
branch 3 84 3.5
condition 0 17 0.0
subroutine 15 29 51.7
pod 1 16 6.2
total 71 347 20.4


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