merge from 2_4_Release branch
[jalview.git] / src / jalview / gui / TreePanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.beans.*;
22 import java.io.*;
23 import java.util.*;
24 import javax.imageio.*;
25
26 import java.awt.*;
27 import java.awt.event.*;
28 import java.awt.image.*;
29 import javax.swing.*;
30
31 import org.jibble.epsgraphics.*;
32 import jalview.analysis.*;
33 import jalview.datamodel.*;
34 import jalview.io.*;
35 import jalview.jbgui.*;
36
37 /**
38  * DOCUMENT ME!
39  * 
40  * @author $author$
41  * @version $Revision$
42  */
43 public class TreePanel extends GTreePanel
44 {
45   String type;
46
47   String pwtype;
48
49   TreeCanvas treeCanvas;
50
51   NJTree tree;
52
53   AlignViewport av;
54
55   /**
56    * Creates a new TreePanel object.
57    * 
58    * @param av
59    *                DOCUMENT ME!
60    * @param seqVector
61    *                DOCUMENT ME!
62    * @param type
63    *                DOCUMENT ME!
64    * @param pwtype
65    *                DOCUMENT ME!
66    * @param s
67    *                DOCUMENT ME!
68    * @param e
69    *                DOCUMENT ME!
70    */
71   public TreePanel(AlignmentPanel ap, String type, String pwtype)
72   {
73     super();
74     initTreePanel(ap, type, pwtype, null, null);
75
76     // We know this tree has distances. JBPNote TODO: prolly should add this as
77     // a userdefined default
78     // showDistances(true);
79   }
80
81   /**
82    * Creates a new TreePanel object.
83    * 
84    * @param av
85    *                DOCUMENT ME!
86    * @param seqVector
87    *                DOCUMENT ME!
88    * @param newtree
89    *                DOCUMENT ME!
90    * @param type
91    *                DOCUMENT ME!
92    * @param pwtype
93    *                DOCUMENT ME!
94    */
95   public TreePanel(AlignmentPanel ap, String type, String pwtype,
96           NewickFile newtree)
97   {
98     super();
99     initTreePanel(ap, type, pwtype, newtree, null);
100   }
101
102   public TreePanel(AlignmentPanel av, String type, String pwtype,
103           NewickFile newtree, AlignmentView inputData)
104   {
105     super();
106     initTreePanel(av, type, pwtype, newtree, inputData);
107   }
108
109   public AlignmentI getAlignment()
110   {
111     return treeCanvas.av.getAlignment();
112   }
113
114   public AlignViewport getViewPort()
115   {
116     return treeCanvas.av;
117   }
118
119   void initTreePanel(AlignmentPanel ap, String type, String pwtype,
120           NewickFile newTree, AlignmentView inputData)
121   {
122
123     av = ap.av;
124     this.type = type;
125     this.pwtype = pwtype;
126
127     treeCanvas = new TreeCanvas(this, ap, scrollPane);
128     scrollPane.setViewportView(treeCanvas);
129
130     PaintRefresher.Register(this, ap.av.getSequenceSetId());
131
132     buildAssociatedViewMenu();
133
134     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
135     {
136       public void propertyChange(PropertyChangeEvent evt)
137       {
138         if (evt.getPropertyName().equals("alignment"))
139         {
140           if (tree == null)
141           {
142             System.out.println("tree is null");
143           }
144           if (evt.getNewValue() == null)
145           {
146             System.out
147                     .println("new alignment sequences vector value is null");
148           }
149
150           tree.UpdatePlaceHolders((Vector) evt.getNewValue());
151           treeCanvas.nameHash.clear(); // reset the mapping between canvas
152                                         // rectangles and leafnodes
153           repaint();
154         }
155       }
156     });
157
158     TreeLoader tl = new TreeLoader(newTree);
159     if (inputData != null)
160     {
161       tl.odata = inputData;
162     }
163     tl.start();
164
165   }
166
167   public void viewMenu_menuSelected()
168   {
169     buildAssociatedViewMenu();
170   }
171
172   void buildAssociatedViewMenu()
173   {
174     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
175             .getSequenceSetId());
176     if (aps.length == 1 && treeCanvas.ap == aps[0])
177     {
178       associateLeavesMenu.setVisible(false);
179       return;
180     }
181
182     associateLeavesMenu.setVisible(true);
183
184     if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
185     {
186       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
187     }
188
189     associateLeavesMenu.removeAll();
190
191     JRadioButtonMenuItem item;
192     ButtonGroup buttonGroup = new ButtonGroup();
193     int i, iSize = aps.length;
194     final TreePanel thisTreePanel = this;
195     for (i = 0; i < iSize; i++)
196     {
197       final AlignmentPanel ap = aps[i];
198       item = new JRadioButtonMenuItem(ap.av.viewName, ap == treeCanvas.ap);
199       buttonGroup.add(item);
200       item.addActionListener(new ActionListener()
201       {
202         public void actionPerformed(ActionEvent evt)
203         {
204           treeCanvas.applyToAllViews = false;
205           treeCanvas.ap = ap;
206           treeCanvas.av = ap.av;
207           PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
208         }
209       });
210
211       associateLeavesMenu.add(item);
212     }
213
214     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
215     buttonGroup.add(itemf);
216     itemf.setSelected(treeCanvas.applyToAllViews);
217     itemf.addActionListener(new ActionListener()
218     {
219       public void actionPerformed(ActionEvent evt)
220       {
221         treeCanvas.applyToAllViews = itemf.isSelected();
222       }
223     });
224     associateLeavesMenu.add(itemf);
225
226   }
227
228   class TreeLoader extends Thread
229   {
230     NewickFile newtree;
231
232     jalview.datamodel.AlignmentView odata = null;
233
234     public TreeLoader(NewickFile newtree)
235     {
236       this.newtree = newtree;
237       if (newtree != null)
238       {
239         // Must be outside run(), as Jalview2XML tries to
240         // update distance/bootstrap visibility at the same time
241         showBootstrap(newtree.HasBootstrap());
242         showDistances(newtree.HasDistances());
243       }
244     }
245
246     public void run()
247     {
248
249       if (newtree != null)
250       {
251         if (odata == null)
252         {
253           tree = new NJTree(av.alignment.getSequencesArray(), newtree);
254         }
255         else
256         {
257           tree = new NJTree(av.alignment.getSequencesArray(), odata,
258                   newtree);
259         }
260         if (!tree.hasOriginalSequenceData())
261         {
262           allowOriginalSeqData(false);
263         }
264       }
265       else
266       {
267         int start, end;
268         SequenceI[] seqs;
269         AlignmentView seqStrings = av.getAlignmentView(av
270                 .getSelectionGroup() != null);
271         if (av.getSelectionGroup() == null)
272         {
273           start = 0;
274           end = av.alignment.getWidth();
275           seqs = av.alignment.getSequencesArray();
276         }
277         else
278         {
279           start = av.getSelectionGroup().getStartRes();
280           end = av.getSelectionGroup().getEndRes() + 1;
281           seqs = av.getSelectionGroup().getSequencesInOrder(av.alignment);
282         }
283
284         tree = new NJTree(seqs, seqStrings, type, pwtype, start, end);
285         showDistances(true);
286       }
287
288       tree.reCount(tree.getTopNode());
289       tree.findHeight(tree.getTopNode());
290       treeCanvas.setTree(tree);
291       treeCanvas.repaint();
292       av.setCurrentTree(tree);
293
294     }
295   }
296
297   public void showDistances(boolean b)
298   {
299     treeCanvas.setShowDistances(b);
300     distanceMenu.setSelected(b);
301   }
302
303   public void showBootstrap(boolean b)
304   {
305     treeCanvas.setShowBootstrap(b);
306     bootstrapMenu.setSelected(b);
307   }
308
309   public void showPlaceholders(boolean b)
310   {
311     placeholdersMenu.setState(b);
312     treeCanvas.setMarkPlaceholders(b);
313   }
314
315   private void allowOriginalSeqData(boolean b)
316   {
317     originalSeqData.setVisible(b);
318   }
319
320   /**
321    * DOCUMENT ME!
322    * 
323    * @return DOCUMENT ME!
324    */
325   public NJTree getTree()
326   {
327     return tree;
328   }
329
330   /**
331    * DOCUMENT ME!
332    * 
333    * @param e
334    *                DOCUMENT ME!
335    */
336   public void textbox_actionPerformed(ActionEvent e)
337   {
338     CutAndPasteTransfer cap = new CutAndPasteTransfer();
339
340     StringBuffer buffer = new StringBuffer();
341
342     if (type.equals("AV"))
343     {
344       buffer.append("Average distance tree using ");
345     }
346     else
347     {
348       buffer.append("Neighbour joining tree using ");
349     }
350
351     if (pwtype.equals("BL"))
352     {
353       buffer.append("BLOSUM62");
354     }
355     else
356     {
357       buffer.append("PID");
358     }
359
360     Desktop.addInternalFrame(cap, buffer.toString(), 500, 100);
361
362     jalview.io.NewickFile fout = new jalview.io.NewickFile(tree
363             .getTopNode());
364     cap.setText(fout.print(tree.isHasBootstrap(), tree.isHasDistances(),
365             tree.isHasRootDistance()));
366   }
367
368   /**
369    * DOCUMENT ME!
370    * 
371    * @param e
372    *                DOCUMENT ME!
373    */
374   public void saveAsNewick_actionPerformed(ActionEvent e)
375   {
376     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
377             .getProperty("LAST_DIRECTORY"));
378     chooser.setFileView(new JalviewFileView());
379     chooser.setDialogTitle("Save tree as newick file");
380     chooser.setToolTipText("Save");
381
382     int value = chooser.showSaveDialog(null);
383
384     if (value == JalviewFileChooser.APPROVE_OPTION)
385     {
386       String choice = chooser.getSelectedFile().getPath();
387       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
388               .getSelectedFile().getParent());
389
390       try
391       {
392         jalview.io.NewickFile fout = new jalview.io.NewickFile(tree
393                 .getTopNode());
394         String output = fout.print(tree.isHasBootstrap(), tree
395                 .isHasDistances(), tree.isHasRootDistance());
396         java.io.PrintWriter out = new java.io.PrintWriter(
397                 new java.io.FileWriter(choice));
398         out.println(output);
399         out.close();
400       } catch (Exception ex)
401       {
402         ex.printStackTrace();
403       }
404     }
405   }
406
407   /**
408    * DOCUMENT ME!
409    * 
410    * @param e
411    *                DOCUMENT ME!
412    */
413   public void printMenu_actionPerformed(ActionEvent e)
414   {
415     // Putting in a thread avoids Swing painting problems
416     treeCanvas.startPrinting();
417   }
418
419   public void originalSeqData_actionPerformed(ActionEvent e)
420   {
421     if (!tree.hasOriginalSequenceData())
422     {
423       jalview.bin.Cache.log
424               .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
425       return;
426     }
427     // decide if av alignment is sufficiently different to original data to
428     // warrant a new window to be created
429     // create new alignmnt window with hidden regions (unhiding hidden regions
430     // yields unaligned seqs)
431     // or create a selection box around columns in alignment view
432     // test Alignment(SeqCigar[])
433     char gc = '-';
434     try
435     {
436       // we try to get the associated view's gap character
437       // but this may fail if the view was closed...
438       gc = av.getGapCharacter();
439
440     } catch (Exception ex)
441     {
442     }
443     ;
444     Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(gc);
445
446     if (alAndColsel != null && alAndColsel[0] != null)
447     {
448       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
449
450       Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
451       Alignment dataset = (av != null && av.getAlignment() != null) ? av
452               .getAlignment().getDataset() : null;
453       if (dataset != null)
454       {
455         al.setDataset(dataset);
456       }
457
458       if (true)
459       {
460         // make a new frame!
461         AlignFrame af = new AlignFrame(al,
462                 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
463                 AlignFrame.DEFAULT_HEIGHT);
464
465         // >>>This is a fix for the moment, until a better solution is
466         // found!!<<<
467         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
468
469         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
470         // msaorder);
471
472         Desktop.addInternalFrame(af, "Original Data for " + this.title,
473                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
474       }
475     }
476   }
477
478   /**
479    * DOCUMENT ME!
480    * 
481    * @param e
482    *                DOCUMENT ME!
483    */
484   public void fitToWindow_actionPerformed(ActionEvent e)
485   {
486     treeCanvas.fitToWindow = fitToWindow.isSelected();
487     repaint();
488   }
489
490   /**
491    * DOCUMENT ME!
492    * 
493    * @param e
494    *                DOCUMENT ME!
495    */
496   public void font_actionPerformed(ActionEvent e)
497   {
498     if (treeCanvas == null)
499     {
500       return;
501     }
502
503     new FontChooser(this);
504   }
505
506   public Font getTreeFont()
507   {
508     return treeCanvas.font;
509   }
510
511   public void setTreeFont(Font font)
512   {
513     if (treeCanvas != null)
514     {
515       treeCanvas.setFont(font);
516     }
517   }
518
519   /**
520    * DOCUMENT ME!
521    * 
522    * @param e
523    *                DOCUMENT ME!
524    */
525   public void distanceMenu_actionPerformed(ActionEvent e)
526   {
527     treeCanvas.setShowDistances(distanceMenu.isSelected());
528   }
529
530   /**
531    * DOCUMENT ME!
532    * 
533    * @param e
534    *                DOCUMENT ME!
535    */
536   public void bootstrapMenu_actionPerformed(ActionEvent e)
537   {
538     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
539   }
540
541   /**
542    * DOCUMENT ME!
543    * 
544    * @param e
545    *                DOCUMENT ME!
546    */
547   public void placeholdersMenu_actionPerformed(ActionEvent e)
548   {
549     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
550   }
551
552   /**
553    * DOCUMENT ME!
554    * 
555    * @param e
556    *                DOCUMENT ME!
557    */
558   public void epsTree_actionPerformed(ActionEvent e)
559   {
560     boolean accurateText = true;
561
562     String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING",
563             "Prompt each time");
564
565     // If we need to prompt, and if the GUI is visible then
566     // Prompt for EPS rendering style
567     if (renderStyle.equalsIgnoreCase("Prompt each time")
568             && !(System.getProperty("java.awt.headless") != null && System
569                     .getProperty("java.awt.headless").equals("true")))
570     {
571       EPSOptions eps = new EPSOptions();
572       renderStyle = eps.getValue();
573
574       if (renderStyle == null || eps.cancelled)
575       {
576         return;
577       }
578
579     }
580
581     if (renderStyle.equalsIgnoreCase("text"))
582     {
583       accurateText = false;
584     }
585
586     int width = treeCanvas.getWidth();
587     int height = treeCanvas.getHeight();
588
589     try
590     {
591       jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
592               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
593               { "eps" }, new String[]
594               { "Encapsulated Postscript" }, "Encapsulated Postscript");
595       chooser.setFileView(new jalview.io.JalviewFileView());
596       chooser.setDialogTitle("Create EPS file from tree");
597       chooser.setToolTipText("Save");
598
599       int value = chooser.showSaveDialog(this);
600
601       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
602       {
603         return;
604       }
605
606       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
607               .getSelectedFile().getParent());
608
609       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
610       EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, height);
611
612       pg.setAccurateTextMode(accurateText);
613
614       treeCanvas.draw(pg, width, height);
615
616       pg.flush();
617       pg.close();
618     } catch (Exception ex)
619     {
620       ex.printStackTrace();
621     }
622   }
623
624   /**
625    * DOCUMENT ME!
626    * 
627    * @param e
628    *                DOCUMENT ME!
629    */
630   public void pngTree_actionPerformed(ActionEvent e)
631   {
632     int width = treeCanvas.getWidth();
633     int height = treeCanvas.getHeight();
634
635     try
636     {
637       jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
638               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
639               { "png" }, new String[]
640               { "Portable network graphics" }, "Portable network graphics");
641
642       chooser.setFileView(new jalview.io.JalviewFileView());
643       chooser.setDialogTitle("Create PNG image from tree");
644       chooser.setToolTipText("Save");
645
646       int value = chooser.showSaveDialog(this);
647
648       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
649       {
650         return;
651       }
652
653       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
654               .getSelectedFile().getParent());
655
656       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
657
658       BufferedImage bi = new BufferedImage(width, height,
659               BufferedImage.TYPE_INT_RGB);
660       Graphics png = bi.getGraphics();
661
662       treeCanvas.draw(png, width, height);
663
664       ImageIO.write(bi, "png", out);
665       out.close();
666     } catch (Exception ex)
667     {
668       ex.printStackTrace();
669     }
670   }
671 }