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