File Coverage

lib/Gtk2/Ex/MindMapView.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Gtk2::Ex::MindMapView;
2              
3             our $VERSION = '0.000001';
4              
5 11     11   775381 use warnings;
  11         29  
  11         1380  
6 11     11   65 use strict;
  11         33  
  11         516  
7 11     11   66 use Carp;
  11         23  
  11         1293  
8              
9 11     11   6894 use Gnome2::Canvas;
  0            
  0            
10              
11             use Gtk2::Ex::MindMapView::Graph;
12             use Gtk2::Ex::MindMapView::Connection;
13             use Gtk2::Ex::MindMapView::Layout::Balanced;
14              
15             use POSIX qw(DBL_MAX);
16              
17             use Glib ':constants';
18              
19             use Glib::Object::Subclass
20             Gnome2::Canvas::,
21             properties => [
22             Glib::ParamSpec->string ('connection-arrows', 'arrows',
23             'Type of arrow to display.', 'none', G_PARAM_READWRITE),
24              
25             Glib::ParamSpec->scalar ('connection-color-gdk','connection_color_gdk',
26             'The color of the connection.', G_PARAM_READWRITE),
27             ]
28             ;
29              
30              
31             sub INIT_INSTANCE
32             {
33             my $self = shift(@_);
34              
35             $self->{graph} = Gtk2::Ex::MindMapView::Graph->new();
36              
37             $self->{signals} = {}; # HoH
38              
39             $self->{connections} = {}; # HoA
40              
41             $self->{connection_color_gdk} = Gtk2::Gdk::Color->parse('darkblue');
42              
43             $self->{connection_arrows} = 'none';
44              
45             return $self;
46             }
47              
48              
49             sub SET_PROPERTY
50             {
51             my ($self, $pspec, $newval) = @_;
52              
53             my $param_name = $pspec->get_name();
54              
55             if ($param_name eq 'connection_arrows')
56             {
57             if (!grep { $_ eq $newval } qw(none one-way two-way))
58             {
59             croak "You may only set the connection arrows " .
60             "to: 'none', 'one-way', 'two-way'.\n"
61             }
62              
63             $self->{connection_arrows} = $newval;
64              
65             return;
66             }
67              
68             if ($param_name eq 'connection_color_gdk')
69             {
70             if (!$newval->isa('Gtk2::Gdk::Color'))
71             {
72             croak "You may only set the connection color to " .
73             "a Gtk2::Gdk::Color.\n";
74             }
75              
76             $self->{connection_color_gdk} = $newval;
77              
78             return;
79             }
80              
81             $self->{$param_name} = $newval;
82              
83             return;
84             }
85              
86              
87             # $view->add_item($item);
88             # $view->add_item($predecessor_item, $item);
89              
90             sub add_item
91             {
92             my ($self, $arg1, $arg2) = @_;
93              
94             my $predecessor_item = (defined $arg2) ? $arg1 : undef;
95              
96             my $item = (defined $arg2) ? $arg2 : $arg1;
97              
98             if (!$item->isa('Gtk2::Ex::MindMapView::Item'))
99             {
100             croak "You may only add a Gtk2::Ex::MindMapView::Item.\n";
101             }
102              
103             if ((defined $predecessor_item) &&
104             (!$predecessor_item->isa('Gtk2::Ex::MindMapView::Item')))
105             {
106             croak "You may only add items that have a " .
107             "Gtk2::Ex::MindMapView::Item as predecessor.\n";
108             }
109              
110             if (!defined $self->{signals}{$item})
111             {
112             $self->{signals}{$item} =
113             $item->signal_connect('layout'=>sub { $self->layout(); });
114             }
115              
116             $item->set(graph=>$self->{graph});
117              
118             if (!defined $predecessor_item)
119             {
120             $self->{graph}->add($item);
121              
122             return;
123             }
124              
125             $self->{graph}->add($predecessor_item, $item);
126              
127             _add_connection($self, $predecessor_item, $item);
128             }
129              
130              
131             # $view->clear();
132              
133             sub clear
134             {
135             my $self = shift(@_);
136              
137             return if (scalar $self->{graph}->num_items() == 0);
138              
139             my $root_item = $self->{graph}->get_root();
140              
141             my @successors = $self->{graph}->successors($root_item);
142              
143             foreach my $successor_item (@successors)
144             {
145             $self->{graph}->traverse_postorder_edge($root_item,
146             $successor_item, sub { $self->remove_item($_[0], $_[1]); });
147             }
148              
149             $self->remove_item($root_item);
150             }
151              
152              
153             # $view->layout();
154              
155             sub layout
156             {
157             my $self = shift(@_);
158              
159             if (scalar $self->{graph}->num_items())
160             {
161             my $layout =
162             Gtk2::Ex::MindMapView::Layout::Balanced->new(graph=>$self->{graph});
163              
164             $layout->layout();
165             }
166             }
167              
168              
169             # @predecessors = $view->predecessors($item);
170              
171             sub predecessors
172             {
173             my ($self, $item) = @_;
174              
175             if (!$item->isa('Gtk2::Ex::MindMapView::Item'))
176             {
177             croak "You may only get the predecessors of a " .
178             "Gtk2::Ex::MindMapView::Item.\n";
179             }
180              
181             return $self->{graph}->predecessors($item);
182             }
183              
184              
185             # $view->remove_item($item);
186             # $view->remove_item($predecessor_item, $item);
187              
188             sub remove_item
189             {
190             my ($self, $arg1, $arg2) = @_;
191              
192             my $predecessor_item = (defined $arg2) ? $arg1 : undef;
193              
194             my $item = (defined $arg2) ? $arg2 : $arg1;
195              
196             if (!$item->isa('Gtk2::Ex::MindMapView::Item'))
197             {
198             croak "You may only remove a Gtk2::Ex::MindMapView::Item.\n";
199             }
200              
201             if ((defined $predecessor_item) &&
202             (!$predecessor_item->isa('Gtk2::Ex::MindMapView::Item')))
203             {
204             croak "You may only remove items that have a " .
205             "Gtk2::Ex::MindMapView::Item as predecessor.\n";
206             }
207              
208             if (scalar $self->{graph}->successors($item))
209             {
210             croak "You must remove the successors of this item prior " .
211             "to removing this item.\n";
212             }
213              
214             if (defined $self->{signals}{$item})
215             {
216             $item->signal_handler_disconnect($self->{signals}{$item});
217              
218             delete $self->{signals}{$item};
219             }
220              
221             if (!defined $predecessor_item)
222             {
223             $self->{graph}->remove($item);
224              
225             $item->destroy();
226              
227             return;
228             }
229              
230             $self->{graph}->remove($predecessor_item, $item);
231              
232             if (exists $self->{connections}{$item})
233             {
234             _remove_connection($self, $predecessor_item, $item);
235              
236             if (scalar @{$self->{connections}{$item}} == 0)
237             {
238             delete $self->{connections}{$item};
239             }
240             }
241              
242             $item->destroy();
243             }
244              
245              
246             # $view->set_root($item);
247              
248             sub set_root
249             {
250             my ($self, $item) = @_;
251              
252             if (!$item->isa('Gtk2::Ex::MindMapView::Item'))
253             {
254             croak "You may only set the root to a Gtk2::Ex::MindMapView::Item.\n";
255             }
256              
257             if (!$self->{graph}->has_item($item))
258             {
259             croak "You may only set the root to a Gtk2::Ex::MindMapView::Item " .
260             "that's been added to the view.\n";
261             }
262              
263             _clear_connections($self);
264              
265             $self->{graph}->set_root($item);
266              
267             my @successors = $self->{graph}->successors($item);
268              
269             foreach my $successor_item (@successors)
270             {
271             $self->{graph}->traverse_preorder_edge($item,
272             $successor_item, sub { _add_connection($self, $_[0], $_[1]); });
273             }
274             }
275              
276              
277             # @successors = $view->successors($item);
278              
279             sub successors
280             {
281             my ($self, $item) = @_;
282              
283             if (!$item->isa('Gtk2::Ex::MindMapView::Item'))
284             {
285             croak "You may only get the successors of a " .
286             "Gtk2::Ex::MindMapView::Item.\n";
287             }
288              
289             return $self->{graph}->successors($item);
290             }
291              
292              
293             sub _add_connection
294             {
295             my ($self, $predecessor_item, $item) = @_;
296              
297             my $connection = Gnome2::Canvas::Item->new($self->root,
298             'Gtk2::Ex::MindMapView::Connection',
299             predecessor_item=>$predecessor_item,
300             item=>$item,
301             arrows=>$self->{connection_arrows},
302             width_pixels=>1,
303             outline_color_gdk=>$self->{connection_color_gdk},
304             fill_color=>'darkblue');
305              
306             push @{$self->{connections}{$item}}, $connection;
307             }
308              
309              
310             sub _clear_connections
311             {
312             my $self = shift(@_);
313              
314             my $root_item = $self->{graph}->get_root();
315              
316             my @successors = $self->{graph}->successors($root_item);
317              
318             foreach my $successor_item (@successors)
319             {
320             $self->{graph}->traverse_preorder_edge($root_item,
321             $successor_item, sub { _remove_connection($self, $_[0], $_[1]); });
322             }
323              
324             $self->{connections} = undef;
325             }
326              
327              
328             sub _remove_connection
329             {
330             my ($self, $predecessor_item, $item) = @_;
331              
332             my $index = 0;
333              
334             my @connections = @{$self->{connections}{$item}};
335              
336             foreach my $connection (@connections)
337             {
338             if ($connection->get('predecessor_item') == $predecessor_item)
339             {
340             $connection->disconnect();
341              
342             $connection->destroy();
343              
344             last;
345             }
346              
347             $index++;
348             }
349              
350             splice @{$self->{connections}{$item}}, $index, 1;
351             }
352              
353              
354             1; # Magic true value required at end of module
355             __END__