Merge pull request #42 from solgenomics/topic/duplicate_image_warning
[cxgn-corelibs.git] / lib / CXGN / Phylo / Tree_browser.pm
blobac5cd231f8dabdabfa80878f9508edea7c5c8763
1 #!/usr/bin/perl
3 use strict;
4 use File::Temp;
6 use CXGN::Phylo::Configuration;
8 package CXGN::Phylo::Tree_browser;
10 sub new {
11 my $class = shift;
12 my $directives = shift;
14 my $args= {};
15 my $self = bless $args, $class;
17 # set the default using the setter functions
19 $self->set_tree(undef);
20 #want this to work outside of mod_perl: (ccarpita)
21 unless($directives->{no_apache}){
22 $self->set_temp_file(undef);
24 $self->set_temp_url(undef);
25 $self->set_tree_string(undef);
26 $self->set_temp_dir(undef);
27 $self->set_hilite(0);
29 # build the function hash
31 $self->add_code_table("r", "function_rotate_node", 2);
32 $self->add_code_table("h", "function_hide_node", 2);
33 $self->add_code_table("s", "function_prune_to_subtree", 1);
34 $self->add_code_table("t", "function_reset_root", 1);
36 return $self;
39 =head2 function get_tree()
41 Synopsis:
42 Arguments:
43 Returns:
44 Side effects:
45 Description:
47 =cut
49 sub get_tree {
50 my $self=shift;
51 return $self->{tree};
54 =head2 function set_tree
56 Synopsis:
57 Arguments:
58 Returns:
59 Side effects:
60 Description:
62 =cut
64 sub set_tree {
65 my $self=shift;
66 $self->{tree}=shift;
69 sub create_temp_file {
70 my $self = shift;
72 my $tempdir = $self->get_temp_dir();
73 #print STDERR "TEMPDIR: $tempdir\n";
74 my ($tempfh, $tempfile) = File::Temp::tempfile("tree-XXXXXX", DIR=>$tempdir);
75 print $tempfh $self->get_tree_string();
76 $tempfh->close();
78 my $temp_url = $tempfile;
79 $temp_url =~ s/.*(tempfiles.*$)/$1/;
81 $temp_url = "/documents/$temp_url";
83 $self->set_temp_url($temp_url);
84 $self->set_temp_file($tempfile);
86 return ($tempfile, $temp_url);
89 sub read_temp_file {
90 my $self = shift;
92 my $file_contents="";
94 if( open my $fh, $self->get_temp_file() ) {
95 while (<$fh>) {
96 chomp;
97 $file_contents .= $_;
99 close $fh;
102 $self->set_tree_string($file_contents);
106 =head2 function get_temp_url()
108 Synopsis:
109 Arguments:
110 Returns:
111 Side effects:
112 Description:
114 =cut
116 sub get_temp_url {
117 my $self=shift;
118 if (!$self->{temp_url}) {
119 my $url = $self->get_temp_file();
120 $url = File::Basename::basename($url);
121 $url = "/documents/tempfiles/tree_browser/$url";
122 $self->{temp_url}=$url;
124 return $self->{temp_url};
127 =head2 function set_temp_url()
129 Synopsis:
130 Arguments:
131 Returns:
132 Side effects:
133 Description:
135 =cut
137 sub set_temp_url {
138 my $self=shift;
139 $self->{temp_url}=shift;
142 =head2 function get_temp_file()
144 Synopsis:
145 Arguments:
146 Returns:
147 Side effects:
148 Description:
150 =cut
152 sub get_temp_file {
153 my $self=shift;
155 my $tf = $self->{temp_file};
157 #print STDERR "in Tree_browser::get_temp_file(), temp_file: [", $tf, "]\n";
159 return $self->{temp_file};
162 =head2 function set_temp_file()
164 Synopsis: $browser->set_temp_file($filename);
165 Arguments: a valid filename
166 Returns: nothing
167 Side effects: tries to read the tree string from $filename
168 and sets it using set_tree_string()
169 Description:
171 =cut
173 sub set_temp_file {
174 my $self=shift;
175 $self->{temp_file}=shift;
176 if (!$self->get_tree_string()) { $self->read_temp_file(); }
179 =head2 function get_tree_string()
181 Synopsis:
182 Arguments:
183 Returns:
184 Side effects:
185 Description:
187 =cut
189 sub get_tree_string {
190 my $self=shift;
191 return $self->{tree_string};
194 =head2 function set_tree_string()
196 Synopsis:
197 Arguments:
198 Returns:
199 Side effects:
200 Description:
202 =cut
204 sub set_tree_string {
205 my $self=shift;
206 $self->{tree_string}=shift;
209 =head2 function get_temp_dir
211 Synopsis:
212 Arguments: none
213 Returns: a temp directory. If the directory was not set using
214 set_temp_dir(), it tries to come up with a good guess.
215 Side effects: the temp_dir property is used to write the temporary files
216 to. If it is not correct, the browser won\'t work.
217 Description:
219 =cut
221 sub get_temp_dir {
222 my $self=shift;
224 if (!exists($self->{temp_dir}) || !$self->{temp_dir}) {
225 $self->{temp_dir} = CXGN::Phylo::Configuration->new()->get_temp_dir();
227 return $self->{temp_dir};
230 =head2 function set_temp_dir
232 Synopsis:
233 Arguments:
234 Returns:
235 Side effects:
236 Description:
238 =cut
240 sub set_temp_dir {
241 my $self=shift;
242 $self->{temp_dir}=shift;
245 =head2 function recursive_manage_labels()
247 Synopsis:
248 Arguments:
249 Returns:
250 Side effects:
251 Description:
253 =cut
255 sub recursive_manage_labels {
256 my $self = shift;
257 my $node = shift;
259 # check the shortest branch length on this node
261 my $shortest = 1e200;
262 foreach my $c ($node->get_children()) {
263 if ($c->get_branch_length() < $shortest) {
264 $shortest=$c->get_branch_length();
268 my $scaling_factor = $self->get_tree()->get_layout()->get_horizontal_scaling_factor();
269 if ( ($shortest * $scaling_factor) > (length($node->get_name())*5) ) {
270 $node->get_label()->set_hidden(0);
272 elsif ($node->get_hilited()) {
273 $node->get_label()->set_hidden(0);
275 elsif ($node->is_leaf()) {
276 $node->get_label()->set_hidden(0);
278 elsif ($node->get_label()->get_hilite()) {
279 $node->get_label()->set_hidden(0);
281 elsif ($node->get_hidden()) {
282 $node->get_label()->set_hidden(0);
284 else {
285 $node->get_label()->set_hidden(1);
288 foreach my $c ($node->get_children()) {
289 $self->recursive_manage_labels($c);
297 =head2 function get_hilite
299 Synopsis:
300 Arguments:
301 Returns: the node key (NOT a node object) of the hilited node
302 Side effects:
303 Description:
305 =cut
307 sub get_hilite {
308 my $self=shift;
309 return $self->{hilite};
312 =head2 function set_hilite
314 Synopsis:
315 Arguments:
316 Returns:
317 Side effects:
318 Description:
320 =cut
322 sub set_hilite {
323 my $self=shift;
324 my $hilite = shift;
326 # if hilite is undefined, everything will be hilited,
327 # which is not the desired behavior.
329 if (!defined($hilite)) { $hilite=""; }
331 $self->{hilite}=$hilite;
334 sub get_node_operations {
335 my $self=shift;
336 if (!exists($self->{node_operations})) { @{$self->{node_operations}}=(); }
337 return @{$self->{node_operations}};
340 =head2 function toggle_node_operation()
342 Synopsis: $a_tree_browser->toggle_node_operation($the_operation);
343 Arguments: A node operation.
344 Returns:
345 Side effects: Removes the operation from the node_operation list, if initially present,
346 and pushes in onto the list if not initially present.
347 Description: node operations have to be played back in the same order
348 as they were generated by the user. The node operations
349 are therefore stored in an array. (in a first version,
350 they were stored in a hash which caused play back problems).
352 =cut
354 sub toggle_node_operation {
355 my $self=shift;
356 my $operation = shift;
357 if (!exists($self->{node_operations})) { #initialize node operations array to empty list
358 @{$self->{node_operations}}=();
360 my $exists = 0;
361 for (my $i=0; $i<@{$self->{node_operations}}; $i++) { # loop over node operations in the list
362 if (${$self->{node_operations}}[$i] =~ /^$operation$/i) {
363 $exists=1;
364 splice @{$self->{node_operations}}, $i, 1;
367 if (!$exists) {
368 push @{$self->{node_operations}}, $operation;
372 sub add_code_table {
373 my $self = shift;
374 my $code = shift;
375 my $function = shift;
376 my $priority = shift;
377 ${$self->{op_code}}{$code}=$function;
378 ${$self->{op_code_priority}}{$code}=$priority;
381 sub get_code_table {
382 my $self = shift;
383 my $code = shift;
384 return unless $code;
385 my $function;
386 if (exists(${$self->{op_code}}{$code})) {
387 return (${$self->{op_code}}{$code}, ${$self->{op_code_priority}}{$code});
389 else {
390 print STDERR "tree_browser.pm: $code is not a recognized function.\n";
391 return undef;
395 =head2 function play_back_operations()
397 Synopsis: $browser->play_back_operations();
398 Arguments: none
399 Returns: nothing
400 Side effects: takes the operations as set by set_node_operations() and
401 executes the commands on the tree data structure.
402 It is important that the operations are played back in the
403 exact order they were initiated by the user.
404 Description:
406 =cut
408 sub play_back_operations {
409 my $self = shift;
411 my @node_operations = $self->get_node_operations();
412 #print STDERR "Operations: ".(join "|", @node_operations)."\n";
414 foreach my $priority (1, 2) {
415 foreach my $operation (@node_operations) {
416 my $code=""; my $node_key=0;
417 if ($operation =~ m/([a-z]+)(\d+)/i) {
418 $code=$1;
419 $node_key=$2;
422 #print STDERR "OPERATION: $code ON NODE $node_key\n";
423 my $node = $self->get_tree()->get_node($node_key);
425 if ($code && !$self->get_code_table($code)) { print STDERR "Operation $code is undefined!\n"; next; }
426 my ($operation_sub, $operation_priority) = $self->get_code_table($code);
427 #print STDERR "Executing function $code on node $node_key\n";
428 if ($node) {
429 if ($priority == $operation_priority) {
430 $self->$operation_sub($node);
436 # recalculate tree parameters
438 $self->get_tree()->regenerate_node_hash($self->get_tree()->get_root());
439 $self->get_tree()->get_root()->calculate_distances_from_root();
441 $self->get_tree()->calculate_leaf_list();
442 $self->get_tree()->get_root()->recursive_propagate_properties();
446 sub function_rotate_node {
447 my $self = shift;
448 my $node = shift;
449 #print STDERR "sub function_rotate_node: Rotating node: ".$node->get_node_key()."\n";
450 $node->rotate_node();
453 sub function_hide_node {
454 my $self = shift;
455 my $node = shift;
456 $node->set_hidden(1);
459 sub function_prune_to_subtree {
460 my $self = shift;
461 my $node = shift;
462 $self->get_tree->prune_to_subtree($node);
465 sub function_reset_root {
466 my $self = shift;
467 my $node = shift;
468 $self->get_tree()->reset_root($node);