fixes bootstrap bug noted by Chunlong Chen in email to jalview-help
[jalview.git] / src / jalview / gui / TreeCanvas.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 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.util.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import java.awt.print.*;
26 import javax.swing.*;
27
28 import jalview.analysis.*;
29 import jalview.datamodel.*;
30 import jalview.schemes.*;
31 import jalview.util.*;
32
33 /**
34  * DOCUMENT ME!
35  *
36  * @author $author$
37  * @version $Revision$
38  */
39 public class TreeCanvas
40     extends JPanel implements MouseListener, Runnable,
41     Printable, MouseMotionListener
42 {
43   /** DOCUMENT ME!! */
44   public static final String PLACEHOLDER = " * ";
45   NJTree tree;
46   JScrollPane scrollPane;
47   TreePanel tp;
48   AlignViewport av;
49   AlignmentPanel ap;
50   Font font;
51   FontMetrics fm;
52   boolean fitToWindow = true;
53   boolean showDistances = false;
54   boolean showBootstrap = false;
55   boolean markPlaceholders = false;
56   int offx = 20;
57   int offy;
58   float threshold;
59   String longestName;
60   int labelLength = -1;
61
62   Hashtable nameHash = new Hashtable();
63   Hashtable nodeHash = new Hashtable();
64   SequenceNode highlightNode;
65
66   boolean applyToAllViews = false;
67
68   /**
69    * Creates a new TreeCanvas object.
70    *
71    * @param av DOCUMENT ME!
72    * @param tree DOCUMENT ME!
73    * @param scroller DOCUMENT ME!
74    * @param label DOCUMENT ME!
75    */
76   public TreeCanvas(TreePanel tp,
77                     AlignmentPanel ap,
78                     JScrollPane scroller)
79   {
80     this.tp = tp;
81     this.av = ap.av;
82     this.ap = ap;
83     font = av.getFont();
84     scrollPane = scroller;
85     addMouseListener(this);
86     addMouseMotionListener(this);
87     ToolTipManager.sharedInstance().registerComponent(this);
88   }
89
90   /**
91    * DOCUMENT ME!
92    *
93    * @param sequence DOCUMENT ME!
94    */
95   public void treeSelectionChanged(SequenceI sequence)
96   {
97     AlignmentPanel[] aps = getAssociatedPanels();
98
99     for (int a = 0; a < aps.length; a++)
100     {
101       SequenceGroup selected = aps[a].av.getSelectionGroup();
102
103       if (selected == null)
104       {
105         selected = new SequenceGroup();
106         aps[a].av.setSelectionGroup(selected);
107       }
108
109       selected.setEndRes(aps[a].av.alignment.getWidth() - 1);
110       selected.addOrRemove(sequence, true);
111     }
112   }
113
114   /**
115    * DOCUMENT ME!
116    *
117    * @param tree DOCUMENT ME!
118    */
119   public void setTree(NJTree tree)
120   {
121     this.tree = tree;
122     tree.findHeight(tree.getTopNode());
123
124     // Now have to calculate longest name based on the leaves
125     Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
126     boolean has_placeholders = false;
127     longestName = "";
128
129     for (int i = 0; i < leaves.size(); i++)
130     {
131       SequenceNode lf = (SequenceNode) leaves.elementAt(i);
132
133       if (lf.isPlaceholder())
134       {
135         has_placeholders = true;
136       }
137
138       if (longestName.length() < ( (Sequence) lf.element()).getName()
139           .length())
140       {
141         longestName = TreeCanvas.PLACEHOLDER +
142             ( (Sequence) lf.element()).getName();
143       }
144     }
145
146     setMarkPlaceholders(has_placeholders);
147   }
148
149   /**
150    * DOCUMENT ME!
151    *
152    * @param g DOCUMENT ME!
153    * @param node DOCUMENT ME!
154    * @param chunk DOCUMENT ME!
155    * @param scale DOCUMENT ME!
156    * @param width DOCUMENT ME!
157    * @param offx DOCUMENT ME!
158    * @param offy DOCUMENT ME!
159    */
160   public void drawNode(Graphics g, SequenceNode node, float chunk,
161                        float scale, int width, int offx, int offy)
162   {
163     if (node == null)
164     {
165       return;
166     }
167
168     if ( (node.left() == null) && (node.right() == null))
169     {
170       // Drawing leaf node
171       float height = node.height;
172       float dist = node.dist;
173
174       int xstart = (int) ( (height - dist) * scale) + offx;
175       int xend = (int) (height * scale) + offx;
176
177       int ypos = (int) (node.ycount * chunk) + offy;
178
179       if (node.element() instanceof SequenceI)
180       {
181         SequenceI seq = (SequenceI) ( (SequenceNode) node).element();
182
183         if (av.getSequenceColour(seq) == Color.white)
184         {
185           g.setColor(Color.black);
186         }
187         else
188         {
189           g.setColor(av.getSequenceColour(seq).darker());
190         }
191       }
192       else
193       {
194         g.setColor(Color.black);
195       }
196
197       // Draw horizontal line
198       g.drawLine(xstart, ypos, xend, ypos);
199
200       String nodeLabel = "";
201
202       if (showDistances && (node.dist > 0))
203       {
204         nodeLabel = new Format("%-.2f").form(node.dist);
205       }
206
207       if (showBootstrap)
208       {
209         if (showDistances)
210         {
211           nodeLabel = nodeLabel + " : ";
212         }
213
214         nodeLabel = nodeLabel + String.valueOf(node.bootstrap);
215       }
216
217       if (!nodeLabel.equals(""))
218       {
219         g.drawString(nodeLabel, xstart + 2, ypos - 2);
220       }
221
222       String name = (markPlaceholders && node.isPlaceholder())
223           ? (PLACEHOLDER + node.getName()) : node.getName();
224
225       int charWidth = fm.stringWidth(name) + 3;
226       int charHeight = font.getSize();
227
228       Rectangle rect = new Rectangle(xend + 10, ypos - charHeight / 2,
229                                      charWidth, charHeight);
230
231       nameHash.put( (SequenceI) node.element(), rect);
232
233       // Colour selected leaves differently
234       SequenceGroup selected = av.getSelectionGroup();
235
236       if ( (selected != null) &&
237           selected.getSequences(null).contains( (SequenceI) node.element()))
238       {
239         g.setColor(Color.gray);
240
241         g.fillRect(xend + 10, ypos - charHeight / 2, charWidth,
242                    charHeight);
243         g.setColor(Color.white);
244       }
245
246       g.drawString(name, xend + 10, ypos + fm.getDescent());
247       g.setColor(Color.black);
248     }
249     else
250     {
251       drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
252                offy);
253       drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
254                offy);
255
256       float height = node.height;
257       float dist = node.dist;
258
259       int xstart = (int) ( (height - dist) * scale) + offx;
260       int xend = (int) (height * scale) + offx;
261       int ypos = (int) (node.ycount * chunk) + offy;
262
263       g.setColor( ( (SequenceNode) node).color.darker());
264
265       // Draw horizontal line
266       g.drawLine(xstart, ypos, xend, ypos);
267       if (node == highlightNode)
268       {
269         g.fillRect(xend - 3, ypos - 3, 6, 6);
270       }
271       else
272       {
273         g.fillRect(xend - 2, ypos - 2, 4, 4);
274       }
275
276       int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) +
277           offy;
278       int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) +
279           offy;
280
281       Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
282       nodeHash.put(node, pos);
283
284       g.drawLine( (int) (height * scale) + offx, ystart,
285                  (int) (height * scale) + offx, yend);
286
287       String nodeLabel = "";
288
289       if (showDistances && (node.dist > 0))
290       {
291         nodeLabel = new Format("%-.2f").form(node.dist);
292       }
293
294       if (showBootstrap)
295       {
296         if (showDistances)
297         {
298           nodeLabel = nodeLabel + " : ";
299         }
300
301         nodeLabel = nodeLabel + String.valueOf(node.bootstrap);
302       }
303
304       if (!nodeLabel.equals(""))
305       {
306         g.drawString(nodeLabel, xstart + 2, ypos - 2);
307       }
308     }
309   }
310
311   /**
312    * DOCUMENT ME!
313    *
314    * @param x DOCUMENT ME!
315    * @param y DOCUMENT ME!
316    *
317    * @return DOCUMENT ME!
318    */
319   public Object findElement(int x, int y)
320   {
321     Enumeration keys = nameHash.keys();
322
323     while (keys.hasMoreElements())
324     {
325       Object ob = keys.nextElement();
326       Rectangle rect = (Rectangle) nameHash.get(ob);
327
328       if ( (x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&
329           (y <= (rect.y + rect.height)))
330       {
331         return ob;
332       }
333     }
334
335     keys = nodeHash.keys();
336
337     while (keys.hasMoreElements())
338     {
339       Object ob = keys.nextElement();
340       Rectangle rect = (Rectangle) nodeHash.get(ob);
341
342       if ( (x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&
343           (y <= (rect.y + rect.height)))
344       {
345         return ob;
346       }
347     }
348
349     return null;
350   }
351
352   /**
353    * DOCUMENT ME!
354    *
355    * @param pickBox DOCUMENT ME!
356    */
357   public void pickNodes(Rectangle pickBox)
358   {
359     int width = getWidth();
360     int height = getHeight();
361
362     SequenceNode top = tree.getTopNode();
363
364     float wscale = (float) ( (width * .8) - (offx * 2)) / tree.getMaxHeight();
365
366     if (top.count == 0)
367     {
368       top.count = ( (SequenceNode) top.left()).count +
369           ( (SequenceNode) top.right()).count;
370     }
371
372     float chunk = (float) (height - (offy)) / top.count;
373
374     pickNode(pickBox, top, chunk, wscale, width, offx, offy);
375   }
376
377   /**
378    * DOCUMENT ME!
379    *
380    * @param pickBox DOCUMENT ME!
381    * @param node DOCUMENT ME!
382    * @param chunk DOCUMENT ME!
383    * @param scale DOCUMENT ME!
384    * @param width DOCUMENT ME!
385    * @param offx DOCUMENT ME!
386    * @param offy DOCUMENT ME!
387    */
388   public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
389                        float scale, int width, int offx, int offy)
390   {
391     if (node == null)
392     {
393       return;
394     }
395
396     if ( (node.left() == null) && (node.right() == null))
397     {
398       float height = node.height;
399       float dist = node.dist;
400
401       int xstart = (int) ( (height - dist) * scale) + offx;
402       int xend = (int) (height * scale) + offx;
403
404       int ypos = (int) (node.ycount * chunk) + offy;
405
406       if (pickBox.contains(new Point(xend, ypos)))
407       {
408         if (node.element() instanceof SequenceI)
409         {
410           SequenceI seq = (SequenceI) node.element();
411           SequenceGroup sg = av.getSelectionGroup();
412
413           if (sg != null)
414           {
415             sg.addOrRemove(seq, true);
416           }
417         }
418       }
419     }
420     else
421     {
422       pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
423                offx, offy);
424       pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
425                offx, offy);
426     }
427   }
428
429   /**
430    * DOCUMENT ME!
431    *
432    * @param node DOCUMENT ME!
433    * @param c DOCUMENT ME!
434    */
435   public void setColor(SequenceNode node, Color c)
436   {
437     if (node == null)
438     {
439       return;
440     }
441
442     if ( (node.left() == null) && (node.right() == null)) // TODO: internal node
443     {
444       node.color = c;
445
446       if (node.element() instanceof SequenceI)
447       {
448         AlignmentPanel[] aps = getAssociatedPanels();
449         for (int a = 0; a < aps.length; a++)
450         {
451           aps[a].av.setSequenceColour( (SequenceI) node.element(), c);
452         }
453       }
454     }
455     else
456     {
457       node.color = c;
458       setColor( (SequenceNode) node.left(), c);
459       setColor( (SequenceNode) node.right(), c);
460     }
461   }
462
463   /**
464    * DOCUMENT ME!
465    */
466   void startPrinting()
467   {
468     Thread thread = new Thread(this);
469     thread.start();
470   }
471
472   // put printing in a thread to avoid painting problems
473   public void run()
474   {
475     PrinterJob printJob = PrinterJob.getPrinterJob();
476     PageFormat pf = printJob.pageDialog(printJob.defaultPage());
477
478     printJob.setPrintable(this, pf);
479
480     if (printJob.printDialog())
481     {
482       try
483       {
484         printJob.print();
485       }
486       catch (Exception PrintException)
487       {
488         PrintException.printStackTrace();
489       }
490     }
491   }
492
493   /**
494    * DOCUMENT ME!
495    *
496    * @param pg DOCUMENT ME!
497    * @param pf DOCUMENT ME!
498    * @param pi DOCUMENT ME!
499    *
500    * @return DOCUMENT ME!
501    *
502    * @throws PrinterException DOCUMENT ME!
503    */
504   public int print(Graphics pg, PageFormat pf, int pi)
505       throws PrinterException
506   {
507     pg.setFont(font);
508     pg.translate( (int) pf.getImageableX(), (int) pf.getImageableY());
509
510     int pwidth = (int) pf.getImageableWidth();
511     int pheight = (int) pf.getImageableHeight();
512
513     int noPages = getHeight() / pheight;
514
515     if (pi > noPages)
516     {
517       return Printable.NO_SUCH_PAGE;
518     }
519
520     if (pwidth > getWidth())
521     {
522       pwidth = getWidth();
523     }
524
525     if (fitToWindow)
526     {
527       if (pheight > getHeight())
528       {
529         pheight = getHeight();
530       }
531
532       noPages = 0;
533     }
534     else
535     {
536       FontMetrics fm = pg.getFontMetrics(font);
537       int height = fm.getHeight() * nameHash.size();
538       pg.translate(0, -pi * pheight);
539       pg.setClip(0, pi * pheight, pwidth, (pi * pheight) + pheight);
540
541       // translate number of pages,
542       // height is screen size as this is the
543       // non overlapping text size
544       pheight = height;
545     }
546
547     draw(pg, pwidth, pheight);
548
549     return Printable.PAGE_EXISTS;
550   }
551
552   /**
553    * DOCUMENT ME!
554    *
555    * @param g DOCUMENT ME!
556    */
557   public void paintComponent(Graphics g)
558   {
559     super.paintComponent(g);
560     g.setFont(font);
561
562     if (tree == null)
563     {
564       g.drawString("Calculating tree....", 20, getHeight() / 2);
565     }
566     else
567     {
568       fm = g.getFontMetrics(font);
569
570       if (nameHash.size() == 0)
571       {
572         repaint();
573       }
574
575       if (fitToWindow ||
576           (!fitToWindow &&
577            (scrollPane.getHeight() > ( (fm.getHeight() * nameHash.size()) +
578                                       offy))))
579       {
580         draw(g, scrollPane.getWidth(), scrollPane.getHeight());
581         setPreferredSize(null);
582       }
583       else
584       {
585         setPreferredSize(new Dimension(scrollPane.getWidth(),
586                                        fm.getHeight() * nameHash.size()));
587         draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size());
588       }
589
590       scrollPane.revalidate();
591     }
592   }
593
594   /**
595    * DOCUMENT ME!
596    *
597    * @param fontSize DOCUMENT ME!
598    */
599   public void setFont(Font font)
600   {
601     this.font = font;
602     repaint();
603   }
604
605   /**
606    * DOCUMENT ME!
607    *
608    * @param g1 DOCUMENT ME!
609    * @param width DOCUMENT ME!
610    * @param height DOCUMENT ME!
611    */
612   public void draw(Graphics g1, int width, int height)
613   {
614     Graphics2D g2 = (Graphics2D) g1;
615     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
616                         RenderingHints.VALUE_ANTIALIAS_ON);
617     g2.setColor(Color.white);
618     g2.fillRect(0, 0, width, height);
619
620     g2.setFont(font);
621
622     offy = font.getSize() + 10;
623
624     fm = g2.getFontMetrics(font);
625
626     labelLength = fm.stringWidth(longestName) + 20; //20 allows for scrollbar
627
628     float wscale = (float) (width - labelLength - (offx * 2)) /
629         tree.getMaxHeight();
630
631     SequenceNode top = tree.getTopNode();
632
633     if (top.count == 0)
634     {
635       top.count = ( (SequenceNode) top.left()).count +
636           ( (SequenceNode) top.right()).count;
637     }
638
639     float chunk = (float) (height - (offy)) / top.count;
640
641     drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);
642
643     if (threshold != 0)
644     {
645       if (av.getCurrentTree() == tree)
646       {
647         g2.setColor(Color.red);
648       }
649       else
650       {
651         g2.setColor(Color.gray);
652       }
653
654       int x = (int) ( (threshold * (float) (getWidth() - labelLength -
655                                             (2 * offx))) + offx);
656
657       g2.drawLine(x, 0, x, getHeight());
658     }
659   }
660
661   /**
662    * DOCUMENT ME!
663    *
664    * @param e DOCUMENT ME!
665    */
666   public void mouseReleased(MouseEvent e)
667   {
668   }
669
670   /**
671    * DOCUMENT ME!
672    *
673    * @param e DOCUMENT ME!
674    */
675   public void mouseEntered(MouseEvent e)
676   {
677   }
678
679   /**
680    * DOCUMENT ME!
681    *
682    * @param e DOCUMENT ME!
683    */
684   public void mouseExited(MouseEvent e)
685   {
686   }
687
688   /**
689    * DOCUMENT ME!
690    *
691    * @param e DOCUMENT ME!
692    */
693   public void mouseClicked(MouseEvent evt)
694   {
695     if (highlightNode != null)
696     {
697       if (SwingUtilities.isRightMouseButton(evt))
698       {
699         Color col = JColorChooser.showDialog(this, "Select Sub-Tree Colour",
700                                              highlightNode.color);
701         if (col!=null)
702         {
703           setColor(highlightNode, col);
704         }
705       }
706       else
707       if (evt.getClickCount() > 1)
708       {
709         tree.swapNodes(highlightNode);
710         tree.reCount(tree.getTopNode());
711         tree.findHeight(tree.getTopNode());
712       }
713       else
714       {
715         Vector leaves = new Vector();
716         tree.findLeaves(highlightNode, leaves);
717
718         for (int i = 0; i < leaves.size(); i++)
719         {
720           SequenceI seq =
721               (SequenceI) ( (SequenceNode) leaves.elementAt(i)).element();
722           treeSelectionChanged(seq);
723         }
724       }
725
726       PaintRefresher.Refresh(tp, av.getSequenceSetId());
727       repaint();
728     }
729   }
730
731   public void mouseMoved(MouseEvent evt)
732   {
733     av.setCurrentTree(tree);
734
735     Object ob = findElement(evt.getX(), evt.getY());
736
737     if (ob instanceof SequenceNode)
738     {
739       highlightNode = (SequenceNode) ob;
740       this.setToolTipText(
741           "<html>Left click to select leaves"
742           + "<br>Double-click to invert leaves"
743           + "<br>Right click to change colour");
744       repaint();
745
746     }
747     else
748     {
749       if (highlightNode != null)
750       {
751         highlightNode = null;
752         setToolTipText(null);
753         repaint();
754       }
755     }
756   }
757
758   public void mouseDragged(MouseEvent ect)
759   {}
760
761   /**
762    * DOCUMENT ME!
763    *
764    * @param e DOCUMENT ME!
765    */
766   public void mousePressed(MouseEvent e)
767   {
768     av.setCurrentTree(tree);
769
770     int x = e.getX();
771     int y = e.getY();
772
773     Object ob = findElement(x, y);
774
775     if (ob instanceof SequenceI)
776     {
777       treeSelectionChanged( (Sequence) ob);
778       PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
779       repaint();
780       return;
781     }
782     else if (! (ob instanceof SequenceNode))
783     {
784       // Find threshold
785       if (tree.getMaxHeight() != 0)
786       {
787         threshold = (float) (x - offx) / (float) (getWidth() -
788                                                   labelLength - (2 * offx));
789
790         tree.getGroups().removeAllElements();
791         tree.groupNodes(tree.getTopNode(), threshold);
792         setColor(tree.getTopNode(), Color.black);
793
794         AlignmentPanel[] aps = getAssociatedPanels();
795
796         for (int a = 0; a < aps.length; a++)
797         {
798           aps[a].av.setSelectionGroup(null);
799           aps[a].av.alignment.deleteAllGroups();
800           aps[a].av.sequenceColours = null;
801         }
802         colourGroups();
803       }
804
805       PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
806       repaint();
807     }
808
809   }
810
811   void colourGroups()
812   {
813     for (int i = 0; i < tree.getGroups().size(); i++)
814     {
815       Color col = new Color( (int) (Math.random() * 255),
816                             (int) (Math.random() * 255),
817                             (int) (Math.random() * 255));
818       setColor( (SequenceNode) tree.getGroups().elementAt(i),
819                col.brighter());
820
821       Vector l = tree.findLeaves( (SequenceNode) tree.getGroups()
822                                  .elementAt(i),
823                                  new Vector());
824
825       Vector sequences = new Vector();
826
827       for (int j = 0; j < l.size(); j++)
828       {
829         SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
830
831         if (!sequences.contains(s1))
832         {
833           sequences.addElement(s1);
834         }
835       }
836
837       ColourSchemeI cs = null;
838
839       if (av.getGlobalColourScheme() != null)
840       {
841         if (av.getGlobalColourScheme() instanceof UserColourScheme)
842         {
843           cs = new UserColourScheme(
844               ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
845
846         }
847         else
848         {
849           cs = ColourSchemeProperty.getColour(sequences,
850                                               av.alignment.getWidth(),
851                                               ColourSchemeProperty.
852                                               getColourName(
853                                                   av.getGlobalColourScheme()));
854         }
855
856         cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
857                         av.getIgnoreGapsConsensus());
858       }
859
860       SequenceGroup sg = new SequenceGroup(sequences,
861                                            null, cs, true, true, false,
862                                            0,
863                                            av.alignment.getWidth() - 1);
864
865       sg.setName("JTreeGroup:" + sg.hashCode());
866
867       AlignmentPanel[] aps = getAssociatedPanels();
868       for (int a = 0; a < aps.length; a++)
869       {
870         if (aps[a].av.getGlobalColourScheme() != null
871             && aps[a].av.getGlobalColourScheme().conservationApplied())
872         {
873           Conservation c = new Conservation("Group",
874                                             ResidueProperties.propHash, 3,
875                                             sg.getSequences(null),
876                                             sg.getStartRes(), sg.getEndRes());
877
878           c.calculate();
879           c.verdict(false, aps[a].av.ConsPercGaps);
880           sg.cs.setConservation(c);
881         }
882
883         aps[a].av.alignment.addGroup(sg);
884       }
885     }
886
887   }
888
889   /**
890    * DOCUMENT ME!
891    *
892    * @param state DOCUMENT ME!
893    */
894   public void setShowDistances(boolean state)
895   {
896     this.showDistances = state;
897     repaint();
898   }
899
900   /**
901    * DOCUMENT ME!
902    *
903    * @param state DOCUMENT ME!
904    */
905   public void setShowBootstrap(boolean state)
906   {
907     this.showBootstrap = state;
908     repaint();
909   }
910
911   /**
912    * DOCUMENT ME!
913    *
914    * @param state DOCUMENT ME!
915    */
916   public void setMarkPlaceholders(boolean state)
917   {
918     this.markPlaceholders = state;
919     repaint();
920   }
921
922   AlignmentPanel[] getAssociatedPanels()
923   {
924     if (applyToAllViews)
925     {
926       return PaintRefresher.getAssociatedPanels(av.getSequenceSetId());
927     }
928     else
929     {
930       return new AlignmentPanel[]
931           {
932           ap};
933     }
934   }
935 }