TreePanel added to PaintRefresher, not TreeCanvas
[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       treeCanvas = new TreeCanvas(this, ap, scrollPane);
121       scrollPane.setViewportView(treeCanvas);
122
123       buildAssociatedViewMenu();
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     public void viewMenu_menuSelected()
152     {
153       buildAssociatedViewMenu();
154     }
155
156
157     void buildAssociatedViewMenu()
158     {
159       AlignmentPanel [] aps = PaintRefresher.getAssociatedPanels(av.getSequenceSetId());
160       if(aps.length==1 && treeCanvas.ap==aps[0])
161       {
162         associateLeavesMenu.setVisible(false);
163         return;
164       }
165
166       associateLeavesMenu.setVisible(true);
167
168       if( (viewMenu.getItem(viewMenu.getItemCount()-2) instanceof JMenuItem))
169       {
170         viewMenu.insertSeparator(viewMenu.getItemCount()-1);
171       }
172
173       associateLeavesMenu.removeAll();
174
175
176       JRadioButtonMenuItem item;
177       ButtonGroup buttonGroup = new ButtonGroup();
178       int i, iSize = aps.length;
179       final TreePanel thisTreePanel = this;
180       for(i=0; i<iSize; i++)
181       {
182           final AlignmentPanel ap = aps[i];
183           item = new JRadioButtonMenuItem(ap.av.viewName, ap==treeCanvas.ap );
184           buttonGroup.add(item);
185           item.addActionListener(new ActionListener()
186               {
187                 public void actionPerformed(ActionEvent evt)
188                 {
189                   treeCanvas.applyToAllViews = false;
190                   treeCanvas.ap = ap;
191                   treeCanvas.av = ap.av;
192                   PaintRefresher.RemoveComponent(thisTreePanel);
193                   PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
194                 }
195               });
196
197           associateLeavesMenu.add(item);
198       }
199
200       final JRadioButtonMenuItem itemf =
201           new JRadioButtonMenuItem("All Views", treeCanvas.applyToAllViews);
202       buttonGroup.add(itemf);
203       itemf.addActionListener(new ActionListener()
204       {
205         public void actionPerformed(ActionEvent evt)
206         {
207           treeCanvas.applyToAllViews = itemf.isSelected();
208         }
209       });
210       associateLeavesMenu.add(itemf);
211
212     }
213
214     class TreeLoader extends Thread
215     {
216       NewickFile newtree;
217       jalview.datamodel.AlignmentView odata=null;
218       public TreeLoader(NewickFile newtree)
219       {
220         this.newtree = newtree;
221         if (newtree != null)
222         {
223           // Must be outside run(), as Jalview2XML tries to
224           // update distance/bootstrap visibility at the same time
225           showBootstrap(newtree.HasBootstrap());
226           showDistances(newtree.HasDistances());
227         }
228       }
229
230       public void run()
231       {
232
233         if(newtree!=null)
234         {
235           if (odata==null) {
236             tree = new NJTree(av.alignment.getSequencesArray(),
237                               newtree);
238           } else {
239             tree = new NJTree(av.alignment.getSequencesArray(), odata, newtree);
240           }
241           if (!tree.hasOriginalSequenceData())
242             allowOriginalSeqData(false);
243         }
244         else
245         {
246           int start, end;
247           SequenceI [] seqs;
248           AlignmentView seqStrings = av.getAlignmentView(av.getSelectionGroup()!=null);
249           if(av.getSelectionGroup()==null)
250           {
251             start = 0;
252             end = av.alignment.getWidth();
253             seqs = av.alignment.getSequencesArray();
254           }
255           else
256           {
257             start = av.getSelectionGroup().getStartRes();
258             end = av.getSelectionGroup().getEndRes()+1;
259             seqs = av.getSelectionGroup().getSequencesInOrder(av.alignment);
260           }
261
262           tree = new NJTree(seqs, seqStrings, type, pwtype, start, end);
263           showDistances(true);
264         }
265
266
267         tree.reCount(tree.getTopNode());
268         tree.findHeight(tree.getTopNode());
269         treeCanvas.setTree(tree);
270         treeCanvas.repaint();
271         av.setCurrentTree(tree);
272
273       }
274     }
275
276     public void showDistances(boolean b)
277     {
278       treeCanvas.setShowDistances(b);
279       distanceMenu.setSelected(b);
280     }
281
282     public void showBootstrap(boolean b)
283     {
284       treeCanvas.setShowBootstrap(b);
285       bootstrapMenu.setSelected(b);
286     }
287
288     public void showPlaceholders(boolean b)
289     {
290       placeholdersMenu.setState(b);
291       treeCanvas.setMarkPlaceholders(b);
292     }
293
294     private void allowOriginalSeqData(boolean b) {
295       originalSeqData.setVisible(b);
296     }
297
298
299
300     /**
301      * DOCUMENT ME!
302      *
303      * @return DOCUMENT ME!
304      */
305     public NJTree getTree()
306     {
307         return tree;
308     }
309
310
311     /**
312      * DOCUMENT ME!
313      *
314      * @param e DOCUMENT ME!
315      */
316     public void textbox_actionPerformed(ActionEvent e)
317     {
318         CutAndPasteTransfer cap = new CutAndPasteTransfer();
319
320         StringBuffer buffer = new StringBuffer();
321
322         if (type.equals("AV"))
323         {
324             buffer.append("Average distance tree using ");
325         }
326         else
327         {
328             buffer.append("Neighbour joining tree using ");
329         }
330
331         if (pwtype.equals("BL"))
332         {
333             buffer.append("BLOSUM62");
334         }
335         else
336         {
337             buffer.append("PID");
338         }
339
340         Desktop.addInternalFrame(cap, buffer.toString(), 500, 100);
341
342         jalview.io.NewickFile fout = new jalview.io.NewickFile(tree.getTopNode());
343         cap.setText(fout.print(tree.isHasBootstrap(), tree.isHasDistances(), tree.isHasRootDistance()));
344     }
345
346     /**
347      * DOCUMENT ME!
348      *
349      * @param e DOCUMENT ME!
350      */
351     public void saveAsNewick_actionPerformed(ActionEvent e)
352     {
353         JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(
354                     "LAST_DIRECTORY"));
355         chooser.setFileView(new JalviewFileView());
356         chooser.setDialogTitle("Save tree as newick file");
357         chooser.setToolTipText("Save");
358
359         int value = chooser.showSaveDialog(null);
360
361         if (value == JalviewFileChooser.APPROVE_OPTION)
362         {
363             String choice = chooser.getSelectedFile().getPath();
364             jalview.bin.Cache.setProperty("LAST_DIRECTORY",
365                 chooser.getSelectedFile().getParent());
366
367             try
368             {
369                 jalview.io.NewickFile fout = new jalview.io.NewickFile(tree.getTopNode());
370                 String output = fout.print(tree.isHasBootstrap(), tree.isHasDistances(), tree.isHasRootDistance());
371                 java.io.PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
372                             choice));
373                 out.println(output);
374                 out.close();
375             }
376             catch (Exception ex)
377             {
378                 ex.printStackTrace();
379             }
380         }
381     }
382
383     /**
384      * DOCUMENT ME!
385      *
386      * @param e DOCUMENT ME!
387      */
388     public void printMenu_actionPerformed(ActionEvent e)
389     {
390         //Putting in a thread avoids Swing painting problems
391         treeCanvas.startPrinting();
392     }
393
394
395     public void originalSeqData_actionPerformed(ActionEvent e)
396     {
397       if (!tree.hasOriginalSequenceData())
398       {
399         jalview.bin.Cache.log.info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
400         return;
401       }
402       // decide if av alignment is sufficiently different to original data to warrant a new window to be created
403       // create new alignmnt window with hidden regions (unhiding hidden regions yields unaligned seqs)
404       // or create a selection box around columns in alignment view
405       // test Alignment(SeqCigar[])
406       Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(av.
407           getGapCharacter());
408
409
410       if (alAndColsel != null && alAndColsel[0]!=null)
411        {
412          // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
413
414          Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
415          Alignment dataset = av.getAlignment().getDataset();
416          if (dataset != null)
417          {
418            al.setDataset(dataset);
419          }
420
421          if (true)
422          {
423            // make a new frame!
424            AlignFrame af = new AlignFrame(al, (ColumnSelection) alAndColsel[1],
425                                            AlignFrame.DEFAULT_WIDTH,
426                                            AlignFrame.DEFAULT_HEIGHT
427 );
428
429            //>>>This is a fix for the moment, until a better solution is found!!<<<
430            // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
431
432        //           af.addSortByOrderMenuItem(ServiceName + " Ordering",
433        //                                     msaorder);
434
435            Desktop.addInternalFrame(af, "Original Data for " + this.title,
436                                     AlignFrame.DEFAULT_WIDTH,
437                                     AlignFrame.DEFAULT_HEIGHT);
438          }
439        }
440     }
441
442
443     /**
444      * DOCUMENT ME!
445      *
446      * @param e DOCUMENT ME!
447      */
448     public void fitToWindow_actionPerformed(ActionEvent e)
449     {
450         treeCanvas.fitToWindow = fitToWindow.isSelected();
451         repaint();
452     }
453
454     /**
455      * DOCUMENT ME!
456      *
457      * @param e DOCUMENT ME!
458      */
459     public void font_actionPerformed(ActionEvent e)
460     {
461         if (treeCanvas == null)
462         {
463             return;
464         }
465
466         new FontChooser(this);
467     }
468
469     public Font getTreeFont()
470     {
471         return treeCanvas.font;
472     }
473
474     public void setTreeFont(Font font)
475     {
476       if(treeCanvas!=null)
477       treeCanvas.setFont(font);
478     }
479
480     /**
481      * DOCUMENT ME!
482      *
483      * @param e DOCUMENT ME!
484      */
485     public void distanceMenu_actionPerformed(ActionEvent e)
486     {
487         treeCanvas.setShowDistances(distanceMenu.isSelected());
488     }
489
490     /**
491      * DOCUMENT ME!
492      *
493      * @param e DOCUMENT ME!
494      */
495     public void bootstrapMenu_actionPerformed(ActionEvent e)
496     {
497         treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
498     }
499
500     /**
501      * DOCUMENT ME!
502      *
503      * @param e DOCUMENT ME!
504      */
505     public void placeholdersMenu_actionPerformed(ActionEvent e)
506     {
507         treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
508     }
509
510     /**
511      * DOCUMENT ME!
512      *
513      * @param e DOCUMENT ME!
514      */
515     public void epsTree_actionPerformed(ActionEvent e)
516     {
517       boolean accurateText = true;
518
519       String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING",
520           "Prompt each time");
521
522     // If we need to prompt, and if the GUI is visible then
523     // Prompt for EPS rendering style
524       if (renderStyle.equalsIgnoreCase("Prompt each time")
525           && !
526           (System.getProperty("java.awt.headless") != null
527            && System.getProperty("java.awt.headless").equals("true")))
528       {
529         EPSOptions eps = new EPSOptions();
530         renderStyle = eps.getValue();
531
532         if (renderStyle==null || eps.cancelled)
533           return;
534
535
536       }
537
538       if (renderStyle.equalsIgnoreCase("text"))
539       {
540         accurateText = false;
541       }
542
543         int width = treeCanvas.getWidth();
544         int height = treeCanvas.getHeight();
545
546         try
547         {
548             jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(jalview.bin.Cache.getProperty(
549                         "LAST_DIRECTORY"), new String[] { "eps" },
550                     new String[] { "Encapsulated Postscript" },
551                     "Encapsulated Postscript");
552             chooser.setFileView(new jalview.io.JalviewFileView());
553             chooser.setDialogTitle("Create EPS file from tree");
554             chooser.setToolTipText("Save");
555
556             int value = chooser.showSaveDialog(this);
557
558             if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
559             {
560                 return;
561             }
562
563             jalview.bin.Cache.setProperty("LAST_DIRECTORY",
564                                           chooser.getSelectedFile().getParent());
565
566             FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
567             EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width,
568                                                  height);
569
570             pg.setAccurateTextMode(accurateText);
571
572             treeCanvas.draw(pg, width, height);
573
574             pg.flush();
575             pg.close();
576         }
577         catch (Exception ex)
578         {
579             ex.printStackTrace();
580         }
581     }
582
583     /**
584      * DOCUMENT ME!
585      *
586      * @param e DOCUMENT ME!
587      */
588     public void pngTree_actionPerformed(ActionEvent e)
589     {
590         int width = treeCanvas.getWidth();
591         int height = treeCanvas.getHeight();
592
593         try
594         {
595             jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(jalview.bin.Cache.getProperty(
596                         "LAST_DIRECTORY"), new String[] { "png" },
597                     new String[] { "Portable network graphics" },
598                     "Portable network graphics");
599
600             chooser.setFileView(new jalview.io.JalviewFileView());
601             chooser.setDialogTitle("Create PNG image from tree");
602             chooser.setToolTipText("Save");
603
604             int value = chooser.showSaveDialog(this);
605
606             if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
607             {
608                 return;
609             }
610
611             jalview.bin.Cache.setProperty("LAST_DIRECTORY",
612                 chooser.getSelectedFile().getParent());
613
614             FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
615
616             BufferedImage bi = new BufferedImage(width, height,
617                     BufferedImage.TYPE_INT_RGB);
618             Graphics png = bi.getGraphics();
619
620             treeCanvas.draw(png, width, height);
621
622             ImageIO.write(bi, "png", out);
623             out.close();
624         }
625         catch (Exception ex)
626         {
627             ex.printStackTrace();
628         }
629     }
630 }