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