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