File Coverage

blib/lib/Tk/GraphItems/Connector.pm
Criterion Covered Total %
statement 15 118 12.7
branch 0 38 0.0
condition 0 16 0.0
subroutine 5 18 27.7
pod 5 12 41.6
total 25 202 12.3


line stmt bran cond sub pod time code
1              
2             package Tk::GraphItems::Connector;
3              
4             =head1 NAME
5              
6             Tk::GraphItems::Connector - Display edges of relation-graphs on a Tk::Canvas
7              
8             =head1 SYNOPSIS
9              
10              
11             require Tk::GraphItems::TextBox;
12             require Tk::GraphItems::Connector;
13              
14              
15             my $conn = Tk::GraphItems::Connector->new(
16             source => $a_TextBox,
17             target => $another_TextBox,
18             );
19             $conn->colour( 'red' );
20             $conn->arrow( 'both' );
21             $conn->width( 2 );
22             $conn->detach;
23             $conn = undef;
24              
25              
26              
27              
28             =head1 DESCRIPTION
29              
30             Tk::GraphItems::Connector provides objects to display edges of relation-graphs on a Tk::Canvas widget.
31              
32              
33             =head1 METHODS
34              
35             B supports the following methods:
36              
37             =over 4
38              
39             =item B source => $a_GraphItems-Node,
40             target => $a_GraphItems-NodeB,
41             colour => $a_TkColour,
42             width => $width_pixels,
43             arrow => $where,
44             autodestroy => $bool<)>
45              
46              
47             Create a new Connector instance and display it on the Canvas of 'source' and 'target'.
48             If 'autodestroy' is set to a true value, the Connector will get destroyed when its reference goes out of scope. This is recommended for easy use with Graph.pm or other models which allow to store objects for their edges. See gi-graph.pl for an example. The default for 'autodestroy' is 0. That means the Connector will stay 'alive' until either one of its source/target nodes gets destroyed or Connector->detach is called and references to Connector are deleted.
49              
50             =item B [$a_Tk_colour] B<)>
51              
52             Sets the colour to $a_Tk_colour, if the argument is given. Returns the current colour, if called without an argument.
53              
54             =item B 'source'|'target'|'none'|'both' B<)>
55              
56             Sets the style of the Connectors line-endings. Defaults to 'target'.
57              
58             =item B $line_width B<)>
59              
60             Sets Connectors linewidth in points. Defaults to 1.
61              
62             =item B 'event', $coderef B<)>
63              
64             Binds the given 'event' sequence to $coderef. This binding will exist for all Connector instances on the Canvas displaying the invoking object. The binding will not exist for Connectors that are displayed on other Canvas instances. The Connector instance which is the 'current' one at the time the event is triggered will be passed to $coderef as an argument. If $coderef contains an empty string, the binding for 'event' is deleted.
65              
66             =item B
67              
68             Detach the Connector instance from its source and target so it can be DESTROYED. - It will however stay 'alive' as long as you hold any references to it. If you do not hold a reference to 'Connector' (you don't have to, unless you want to change it's properties...), it will be DESTROYED when either of its 'source'- or 'target'-nodes is destroyed.
69              
70              
71             =back
72              
73             =head1 SEE ALSO
74              
75             Documentation of Tk::GraphItems::TextBox .
76             Examples in Tk/GraphItems/Examples
77              
78             =head1 AUTHOR
79              
80             Christoph Lamprecht, ch.l.ngre@online.de
81              
82             =head1 COPYRIGHT AND LICENSE
83              
84             Copyright (C) 2007 by Christoph Lamprecht
85              
86             This library is free software; you can redistribute it and/or modify
87             it under the same terms as Perl itself, either Perl version 5.8.7 or,
88             at your option, any later version of Perl 5 you may have available.
89              
90              
91             =cut
92 4     4   4132 use 5.008;
  4         14  
  4         209  
93             our $VERSION = '0.11';
94              
95 4     4   19 use Scalar::Util qw(weaken);
  4         6  
  4         189  
96             #use Data::Dumper;
97             require UNIVERSAL;
98 4     4   17 use warnings;
  4         6  
  4         88  
99 4     4   27 use strict;
  4         6  
  4         154  
100 4     4   27 use Carp;
  4         5  
  4         6721  
101             require Tk::GraphItems::GraphItem;
102             our @ISA = ('Tk::GraphItems::GraphItem');
103             my %arrow=(source=>'first',
104             first =>'first',
105             target=>'last',
106             last =>'last',
107             1 =>'last',
108             both =>'both',
109             all =>'both',
110             none =>'none',
111             0 =>'none');
112             sub initialize{
113 0     0 0   my $self = shift;
114 0 0         if (@_%2) {
115 0           croak "wrong number of args! ";
116             }
117 0           my %args = @_;
118 0           my ($source,$target,$colour,$width,$arrow_type,$autodestroy) =
119             @args{qw/source target colour width arrow autodestroy/};
120 0   0       $arrow_type ||= 'target';
121 0           for (qw/source target/) {
122 0           my $node = $args{$_};
123 0 0         eval{$node->isa('Tk::GraphItems::Node')}
  0            
124             ||croak " argument '$_': <$node> is no valid GraphItem::Node! $@ ";
125             }
126            
127 0           my $can =$source->get_canvas ;
128 0 0         if ($can ne $target->get_canvas) {
129 0           croak "Can't connect Nodes on different Canvases!";
130             }
131              
132 0           my @coords ;
133 0           for ($source, $target) {
134 0           push @coords, $_->connector_coords();
135             }
136            
137              
138 0   0       my $id = eval{$can->createLine(@coords,
  0   0        
      0        
139             -fill => $colour||'black',
140             -width => $width||1,
141             -tags =>[
142             'ConnectorBind'],
143             -arrow =>$arrow{$arrow_type}||'last',
144             -arrowshape=>[7,9,3],
145             )};
146 0 0         if ($@) {
147 0           croak "Connector creation failed: $@";
148             }
149              
150              
151 0           $self->{line_id} = $id;
152 0           $self->{dependents} = {};
153 0           $self->{canvas} = $can;
154 0           $self->{source} = $source;
155 0           $self->{target} = $target;
156 0   0       $self->{autodestroy} = $autodestroy ||= 0;
157              
158 0           $self->SUPER::initialize;
159              
160 0           $self->_set_layer(0);
161 0           for (qw/source target/) {
162 0 0         if ($autodestroy) {
163 0           $self->{$_}->add_dependent_weak($self);
164             } else {
165 0           $self->{$_}->add_dependent($self);
166             }
167 0           $self->set_master($_,$self->{$_});
168 0           weaken($self->{$_});
169             }
170 0           for (qw/source target/) {
171 0           $self->set_coords($_,$self->{$_}->connector_coords($self))
172             }
173 0           return $self;
174             }
175              
176              
177             sub canvas_items{
178 0     0 0   my $self = shift;
179 0           return ($self->{line_id});
180             }
181              
182             sub destroy_myself{
183 0     0 0   my $self = shift;
184 0           $self->detach;
185             }
186             sub detach{
187 0     0 1   my $self = shift;
188 0           for (@$self{qw/source target/}) {
189 0 0         if (UNIVERSAL::can($_ , 'remove_dependent')) {
190             # print"d_f_m $_\n";
191 0           $_->remove_dependent($self);
192             }
193             }
194             }
195              
196             sub get_coords{
197 0     0 0   my ($self,$where) = @_;
198 0           my ($can,$id) = @$self{qw/canvas line_id/};
199 0           my @coords = $can->coords($id);
200 0 0 0       if (($where||'') eq 'source') {
201 0           splice (@coords,-2);
202             }
203 0 0 0       if (($where||'') eq 'target') {
204 0           splice (@coords,0,2);
205             }
206 0 0         return wantarray ? @coords : \@coords;
207             }
208              
209             sub set_coords{
210 0     0 0   my ($self,$where,$x,$y)=@_;
211 0           my ($can,$l_id) = @$self{qw/canvas line_id/};
212 0 0         if ($where !~ /source|target/) {
213 0           return;
214             }
215 0           my @coords = $can->coords($l_id);
216 0 0         if ($where eq 'source') {
217 0           @coords[0,1] = ($x,$y);
218             } else {
219 0           @coords[2,3] = ($x,$y);
220             }
221 0           $can->coords($l_id,@coords);
222             }
223              
224             sub set_master{
225 0     0 0   my ($self,$where,$master) = @_;
226 0 0         return unless $where =~ /source|target/;
227 0           $self->{master}{$master}=$where;
228             }
229              
230             sub colour{
231 0     0 1   my $self = shift;
232 0           my $can = $self->get_canvas;
233 0 0         if (@_) {
234 0           eval{$can->itemconfigure($self->{line_id},-fill=>$_[0]);};
  0            
235 0 0         croak " setting colour to <$_[0]> not possible: $@" if $@;
236 0           return $self;
237             } else {
238 0           return $can->itemcget($self->{line_id},'-fill');
239             }
240             }
241              
242             sub arrow{
243 0     0 1   my $self = shift;
244 0           my ($arr_type) = $_[0];
245 0           my $can = $self->get_canvas;
246 0 0         if ( defined $arr_type){
247 0 0         if ( ! $arrow{$arr_type}) {
248 0           croak " setting arrow to <$arr_type> not possible.\n"
249             ."Arrow type must be one of \n"
250             .join ("\n",keys %arrow)
251             ."\n$@";
252             }
253 0   0       $can->itemconfigure($self->{line_id},-arrow=>$arrow{$arr_type}||'last');
254 0           return $self;
255             }
256 0           return $can->itemcget($self->{line_id},'-arrow');
257             }
258              
259             sub width{
260 0     0 1   my $self = shift;
261 0           my $can = $self->get_canvas;
262 0 0         if (@_) {
263 0           eval{$can->itemconfigure($self->{line_id},-width=>$_[0]);};
  0            
264 0 0         croak " setting width to <$_[0]> not possible: $@" if $@;
265 0           return $self;
266             } else {
267 0           return $can->itemcget($self->{line_id},'-width');
268             }
269             }
270              
271             sub position_changed{
272 0     0 0   my ($self,$master) = @_;
273 0           my $first = $self->{master}{$master};
274 0 0         my $second= $first eq 'source'?'target':'source';
275 0           for my $where ($first,$second) {
276 0           $master = $self->{$where};
277 0           my ($x,$y) = $master->connector_coords($self);
278 0           $self->set_coords($where,$x,$y);
279             }
280             }
281              
282             sub bind_class{
283 0     0 1   my ($self,$event,$code) = @_;
284 0           my $can = $self->{canvas};
285 0           $self->_bind_this_class($event,'ConnectorBind',$code);
286             }
287              
288              
289              
290              
291             sub DESTROY {
292 0     0     my $self = shift;
293 0           $self -> detach;
294 0           $self -> SUPER::DESTROY;
295             }
296              
297              
298             1;
299              
300              
301              
302              
303             __END__