Conservation colour scheme is no more
[jalview.git] / src / jalview / gui / TreeCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.gui;\r
20 \r
21 import jalview.analysis.*;\r
22 \r
23 import jalview.datamodel.*;\r
24 \r
25 import jalview.schemes.*;\r
26 \r
27 import jalview.util.*;\r
28 \r
29 import java.awt.*;\r
30 import java.awt.event.*;\r
31 import java.awt.print.*;\r
32 \r
33 import java.util.*;\r
34 \r
35 import javax.swing.*;\r
36 \r
37 \r
38 /**\r
39  * DOCUMENT ME!\r
40  *\r
41  * @author $author$\r
42  * @version $Revision$\r
43  */\r
44 public class TreeCanvas extends JPanel implements MouseListener, Runnable,\r
45     Printable\r
46 {\r
47     /** DOCUMENT ME!! */\r
48     public static final String PLACEHOLDER = " * ";\r
49     NJTree tree;\r
50     JScrollPane scrollPane;\r
51     AlignViewport av;\r
52     Font font;\r
53     int fontSize = 12;\r
54     boolean fitToWindow = true;\r
55     boolean showDistances = false;\r
56     boolean showBootstrap = false;\r
57     boolean markPlaceholders = false;\r
58     int offx = 20;\r
59     int offy = 20;\r
60     float threshold;\r
61     String longestName;\r
62     int labelLength = -1;\r
63 \r
64     //RubberbandRectangle rubberband;\r
65     Vector listeners;\r
66     Hashtable nameHash = new Hashtable();\r
67     Hashtable nodeHash = new Hashtable();\r
68 \r
69     /**\r
70      * Creates a new TreeCanvas object.\r
71      *\r
72      * @param av DOCUMENT ME!\r
73      * @param tree DOCUMENT ME!\r
74      * @param scroller DOCUMENT ME!\r
75      * @param label DOCUMENT ME!\r
76      */\r
77     public TreeCanvas(AlignViewport av, NJTree tree, JScrollPane scroller,\r
78         String label)\r
79     {\r
80         this.av = av;\r
81         this.tree = tree;\r
82         scrollPane = scroller;\r
83         addMouseListener(this);\r
84         tree.findHeight(tree.getTopNode());\r
85         longestName = label;\r
86 \r
87         PaintRefresher.Register(this, av.alignment);\r
88     }\r
89 \r
90     /**\r
91      * DOCUMENT ME!\r
92      *\r
93      * @param sequence DOCUMENT ME!\r
94      */\r
95     public void TreeSelectionChanged(Sequence sequence)\r
96     {\r
97         SequenceGroup selected = av.getSelectionGroup();\r
98 \r
99         if (selected == null)\r
100         {\r
101             selected = new SequenceGroup();\r
102             av.setSelectionGroup(selected);\r
103         }\r
104 \r
105         selected.setEndRes(av.alignment.getWidth());\r
106         selected.addOrRemove(sequence, true);\r
107 \r
108         PaintRefresher.Refresh(this, av.alignment);\r
109         repaint();\r
110     }\r
111 \r
112     /**\r
113      * DOCUMENT ME!\r
114      *\r
115      * @param tree DOCUMENT ME!\r
116      */\r
117     public void setTree(NJTree tree)\r
118     {\r
119         this.tree = tree;\r
120         tree.findHeight(tree.getTopNode());\r
121     }\r
122 \r
123     /**\r
124      * DOCUMENT ME!\r
125      *\r
126      * @param g DOCUMENT ME!\r
127      * @param node DOCUMENT ME!\r
128      * @param chunk DOCUMENT ME!\r
129      * @param scale DOCUMENT ME!\r
130      * @param width DOCUMENT ME!\r
131      * @param offx DOCUMENT ME!\r
132      * @param offy DOCUMENT ME!\r
133      */\r
134     public void drawNode(Graphics g, SequenceNode node, float chunk,\r
135         float scale, int width, int offx, int offy)\r
136     {\r
137         if (node == null)\r
138         {\r
139             return;\r
140         }\r
141 \r
142         if ((node.left() == null) && (node.right() == null))\r
143         {\r
144             // Drawing leaf node\r
145             float height = node.height;\r
146             float dist = node.dist;\r
147 \r
148             int xstart = (int) ((height - dist) * scale) + offx;\r
149             int xend = (int) (height * scale) + offx;\r
150 \r
151             int ypos = (int) (node.ycount * chunk) + offy;\r
152 \r
153             if (node.element() instanceof SequenceI)\r
154             {\r
155                 if (((SequenceI) ((SequenceNode) node).element()).getColor() == Color.white)\r
156                 {\r
157                     g.setColor(Color.black);\r
158                 }\r
159                 else\r
160                 {\r
161                     g.setColor(((SequenceI) ((SequenceNode) node).element()).getColor()\r
162                                 .darker());\r
163                 }\r
164             }\r
165             else\r
166             {\r
167                 g.setColor(Color.black);\r
168             }\r
169 \r
170             // Draw horizontal line\r
171             g.drawLine(xstart, ypos, xend, ypos);\r
172 \r
173             String nodeLabel = "";\r
174 \r
175             if (showDistances && (node.dist > 0))\r
176             {\r
177                 nodeLabel = new Format("%5.2f").form(node.dist);\r
178             }\r
179 \r
180             if (showBootstrap)\r
181             {\r
182                 if (showDistances)\r
183                 {\r
184                     nodeLabel = nodeLabel + " : ";\r
185                 }\r
186 \r
187                 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
188             }\r
189 \r
190             if (!nodeLabel.equals(""))\r
191             {\r
192                 g.drawString(nodeLabel, xstart, ypos - 10);\r
193             }\r
194 \r
195             String name = (markPlaceholders && node.isPlaceholder())\r
196                 ? (PLACEHOLDER + node.getName()) : node.getName();\r
197             FontMetrics fm = g.getFontMetrics(font);\r
198             int charWidth = fm.stringWidth(name) + 3;\r
199             int charHeight = fm.getHeight();\r
200 \r
201             Rectangle rect = new Rectangle(xend + 20, ypos - charHeight,\r
202                     charWidth, charHeight);\r
203 \r
204             nameHash.put((SequenceI) node.element(), rect);\r
205 \r
206             // Colour selected leaves differently\r
207             SequenceGroup selected = av.getSelectionGroup();\r
208 \r
209             if ((selected != null) &&\r
210                     selected.sequences.contains((SequenceI) node.element()))\r
211             {\r
212                 g.setColor(Color.gray);\r
213 \r
214                 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth,\r
215                     charHeight);\r
216                 g.setColor(Color.white);\r
217             }\r
218 \r
219             g.drawString(name, xend + 10, ypos);\r
220             g.setColor(Color.black);\r
221         }\r
222         else\r
223         {\r
224             drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,\r
225                 offy);\r
226             drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,\r
227                 offy);\r
228 \r
229             float height = node.height;\r
230             float dist = node.dist;\r
231 \r
232             int xstart = (int) ((height - dist) * scale) + offx;\r
233             int xend = (int) (height * scale) + offx;\r
234             int ypos = (int) (node.ycount * chunk) + offy;\r
235 \r
236             g.setColor(((SequenceNode) node).color.darker());\r
237 \r
238             // Draw horizontal line\r
239             g.drawLine(xstart, ypos, xend, ypos);\r
240             g.fillRect(xend - 2, ypos - 2, 4, 4);\r
241 \r
242             int ystart = (int) (((SequenceNode) node.left()).ycount * chunk) +\r
243                 offy;\r
244             int yend = (int) (((SequenceNode) node.right()).ycount * chunk) +\r
245                 offy;\r
246 \r
247             Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);\r
248             nodeHash.put(node, pos);\r
249 \r
250             g.drawLine((int) (height * scale) + offx, ystart,\r
251                 (int) (height * scale) + offx, yend);\r
252 \r
253             if (showDistances && (node.dist > 0))\r
254             {\r
255                 g.drawString(new Format("%5.2f").form(node.dist), xstart,\r
256                     ypos - 5);\r
257             }\r
258         }\r
259     }\r
260 \r
261     /**\r
262      * DOCUMENT ME!\r
263      *\r
264      * @param x DOCUMENT ME!\r
265      * @param y DOCUMENT ME!\r
266      *\r
267      * @return DOCUMENT ME!\r
268      */\r
269     public Object findElement(int x, int y)\r
270     {\r
271         Enumeration keys = nameHash.keys();\r
272 \r
273         while (keys.hasMoreElements())\r
274         {\r
275             Object ob = keys.nextElement();\r
276             Rectangle rect = (Rectangle) nameHash.get(ob);\r
277 \r
278             if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&\r
279                     (y <= (rect.y + rect.height)))\r
280             {\r
281                 return ob;\r
282             }\r
283         }\r
284 \r
285         keys = nodeHash.keys();\r
286 \r
287         while (keys.hasMoreElements())\r
288         {\r
289             Object ob = keys.nextElement();\r
290             Rectangle rect = (Rectangle) nodeHash.get(ob);\r
291 \r
292             if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&\r
293                     (y <= (rect.y + rect.height)))\r
294             {\r
295                 return ob;\r
296             }\r
297         }\r
298 \r
299         return null;\r
300     }\r
301 \r
302     /**\r
303      * DOCUMENT ME!\r
304      *\r
305      * @param pickBox DOCUMENT ME!\r
306      */\r
307     public void pickNodes(Rectangle pickBox)\r
308     {\r
309         int width = getWidth();\r
310         int height = getHeight();\r
311 \r
312         SequenceNode top = tree.getTopNode();\r
313 \r
314         float wscale = (float) ((width * .8) - (offx * 2)) / tree.getMaxHeight();\r
315 \r
316         if (top.count == 0)\r
317         {\r
318             top.count = ((SequenceNode) top.left()).count +\r
319                 ((SequenceNode) top.right()).count;\r
320         }\r
321 \r
322         float chunk = (float) (height - (offy * 2)) / top.count;\r
323 \r
324         pickNode(pickBox, top, chunk, wscale, width, offx, offy);\r
325     }\r
326 \r
327     /**\r
328      * DOCUMENT ME!\r
329      *\r
330      * @param pickBox DOCUMENT ME!\r
331      * @param node DOCUMENT ME!\r
332      * @param chunk DOCUMENT ME!\r
333      * @param scale DOCUMENT ME!\r
334      * @param width DOCUMENT ME!\r
335      * @param offx DOCUMENT ME!\r
336      * @param offy DOCUMENT ME!\r
337      */\r
338     public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,\r
339         float scale, int width, int offx, int offy)\r
340     {\r
341         if (node == null)\r
342         {\r
343             return;\r
344         }\r
345 \r
346         if ((node.left() == null) && (node.right() == null))\r
347         {\r
348             float height = node.height;\r
349             float dist = node.dist;\r
350 \r
351             int xstart = (int) ((height - dist) * scale) + offx;\r
352             int xend = (int) (height * scale) + offx;\r
353 \r
354             int ypos = (int) (node.ycount * chunk) + offy;\r
355 \r
356             if (pickBox.contains(new Point(xend, ypos)))\r
357             {\r
358                 if (node.element() instanceof SequenceI)\r
359                 {\r
360                     SequenceI seq = (SequenceI) node.element();\r
361                     SequenceGroup sg = av.getSelectionGroup();\r
362 \r
363                     if (sg != null)\r
364                     {\r
365                         sg.addOrRemove(seq, true);\r
366                     }\r
367                 }\r
368             }\r
369         }\r
370         else\r
371         {\r
372             pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,\r
373                 offx, offy);\r
374             pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,\r
375                 offx, offy);\r
376         }\r
377     }\r
378 \r
379     /**\r
380      * DOCUMENT ME!\r
381      *\r
382      * @param node DOCUMENT ME!\r
383      * @param c DOCUMENT ME!\r
384      */\r
385     public void setColor(SequenceNode node, Color c)\r
386     {\r
387         if (node == null)\r
388         {\r
389             return;\r
390         }\r
391 \r
392         if ((node.left() == null) && (node.right() == null))\r
393         {\r
394             node.color = c;\r
395 \r
396             if (node.element() instanceof SequenceI)\r
397             {\r
398                 ((SequenceI) node.element()).setColor(c);\r
399             }\r
400         }\r
401         else\r
402         {\r
403             node.color = c;\r
404             setColor((SequenceNode) node.left(), c);\r
405             setColor((SequenceNode) node.right(), c);\r
406         }\r
407     }\r
408 \r
409     /**\r
410      * DOCUMENT ME!\r
411      */\r
412     void startPrinting()\r
413     {\r
414         Thread thread = new Thread(this);\r
415         thread.start();\r
416     }\r
417 \r
418     // put printing in a thread to avoid painting problems\r
419     public void run()\r
420     {\r
421         PrinterJob printJob = PrinterJob.getPrinterJob();\r
422         PageFormat pf = printJob.pageDialog(printJob.defaultPage());\r
423 \r
424         printJob.setPrintable(this, pf);\r
425 \r
426         if (printJob.printDialog())\r
427         {\r
428             try\r
429             {\r
430                 printJob.print();\r
431             }\r
432             catch (Exception PrintException)\r
433             {\r
434                 PrintException.printStackTrace();\r
435             }\r
436         }\r
437     }\r
438 \r
439     /**\r
440      * DOCUMENT ME!\r
441      *\r
442      * @param pg DOCUMENT ME!\r
443      * @param pf DOCUMENT ME!\r
444      * @param pi DOCUMENT ME!\r
445      *\r
446      * @return DOCUMENT ME!\r
447      *\r
448      * @throws PrinterException DOCUMENT ME!\r
449      */\r
450     public int print(Graphics pg, PageFormat pf, int pi)\r
451         throws PrinterException\r
452     {\r
453         pg.setFont(font);\r
454         pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());\r
455 \r
456         int pwidth = (int) pf.getImageableWidth();\r
457         int pheight = (int) pf.getImageableHeight();\r
458 \r
459         int noPages = getHeight() / pheight;\r
460 \r
461         if (pi > noPages)\r
462         {\r
463             return Printable.NO_SUCH_PAGE;\r
464         }\r
465 \r
466         if (pwidth > getWidth())\r
467         {\r
468             pwidth = getWidth();\r
469         }\r
470 \r
471         if (fitToWindow)\r
472         {\r
473             if (pheight > getHeight())\r
474             {\r
475                 pheight = getHeight();\r
476             }\r
477 \r
478             noPages = 0;\r
479         }\r
480         else\r
481         {\r
482             FontMetrics fm = pg.getFontMetrics(font);\r
483             int height = fm.getHeight() * nameHash.size();\r
484             pg.translate(0, -pi * pheight);\r
485             pg.setClip(0, pi * pheight, pwidth, (pi * pheight) + pheight);\r
486 \r
487             // translate number of pages,\r
488             // height is screen size as this is the\r
489             // non overlapping text size\r
490             pheight = height;\r
491         }\r
492 \r
493         draw(pg, pwidth, pheight);\r
494 \r
495         return Printable.PAGE_EXISTS;\r
496     }\r
497 \r
498     /**\r
499      * DOCUMENT ME!\r
500      *\r
501      * @param g DOCUMENT ME!\r
502      */\r
503     public void paintComponent(Graphics g)\r
504     {\r
505         super.paintComponent(g);\r
506         font = new Font("Verdana", Font.PLAIN, fontSize);\r
507         g.setFont(font);\r
508 \r
509         FontMetrics fm = g.getFontMetrics(font);\r
510 \r
511         if (nameHash.size() == 0)\r
512         {\r
513             repaint();\r
514         }\r
515 \r
516         if (fitToWindow ||\r
517                 (!fitToWindow &&\r
518                 (scrollPane.getHeight() > ((fm.getHeight() * nameHash.size()) +\r
519                 offy))))\r
520         {\r
521             draw(g, scrollPane.getWidth(), scrollPane.getHeight());\r
522             setPreferredSize(null);\r
523         }\r
524         else\r
525         {\r
526             setPreferredSize(new Dimension(scrollPane.getWidth(),\r
527                     fm.getHeight() * nameHash.size()));\r
528             draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size());\r
529         }\r
530 \r
531         scrollPane.revalidate();\r
532     }\r
533 \r
534     /**\r
535      * DOCUMENT ME!\r
536      *\r
537      * @return DOCUMENT ME!\r
538      */\r
539     public int getFontSize()\r
540     {\r
541         return fontSize;\r
542     }\r
543 \r
544     /**\r
545      * DOCUMENT ME!\r
546      *\r
547      * @param fontSize DOCUMENT ME!\r
548      */\r
549     public void setFontSize(int fontSize)\r
550     {\r
551         this.fontSize = fontSize;\r
552         repaint();\r
553     }\r
554 \r
555     /**\r
556      * DOCUMENT ME!\r
557      *\r
558      * @param g1 DOCUMENT ME!\r
559      * @param width DOCUMENT ME!\r
560      * @param height DOCUMENT ME!\r
561      */\r
562     public void draw(Graphics g1, int width, int height)\r
563     {\r
564         Graphics2D g2 = (Graphics2D) g1;\r
565         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
566             RenderingHints.VALUE_ANTIALIAS_ON);\r
567         g2.setColor(Color.white);\r
568         g2.fillRect(0, 0, width, height);\r
569 \r
570         labelLength = g2.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar\r
571 \r
572         float wscale = (float) (width - labelLength - (offx * 2)) / tree.getMaxHeight();\r
573 \r
574         SequenceNode top = tree.getTopNode();\r
575 \r
576         if (top.count == 0)\r
577         {\r
578             top.count = ((SequenceNode) top.left()).count +\r
579                 ((SequenceNode) top.right()).count;\r
580         }\r
581 \r
582         float chunk = (float) (height - (offy * 2)) / top.count;\r
583 \r
584         drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);\r
585 \r
586         if (threshold != 0)\r
587         {\r
588             if (av.getCurrentTree() == tree)\r
589             {\r
590                 g2.setColor(Color.red);\r
591             }\r
592             else\r
593             {\r
594                 g2.setColor(Color.gray);\r
595             }\r
596 \r
597             int x = (int) ((threshold * (float) (getWidth() - labelLength -\r
598                 (2 * offx))) + offx);\r
599 \r
600             g2.drawLine(x, 0, x, getHeight());\r
601         }\r
602     }\r
603 \r
604     /**\r
605      * DOCUMENT ME!\r
606      *\r
607      * @param e DOCUMENT ME!\r
608      */\r
609     public void mouseReleased(MouseEvent e)\r
610     {\r
611     }\r
612 \r
613     /**\r
614      * DOCUMENT ME!\r
615      *\r
616      * @param e DOCUMENT ME!\r
617      */\r
618     public void mouseEntered(MouseEvent e)\r
619     {\r
620     }\r
621 \r
622     /**\r
623      * DOCUMENT ME!\r
624      *\r
625      * @param e DOCUMENT ME!\r
626      */\r
627     public void mouseExited(MouseEvent e)\r
628     {\r
629     }\r
630 \r
631     /**\r
632      * DOCUMENT ME!\r
633      *\r
634      * @param e DOCUMENT ME!\r
635      */\r
636     public void mouseClicked(MouseEvent e)\r
637     {\r
638     }\r
639 \r
640     /**\r
641      * DOCUMENT ME!\r
642      *\r
643      * @param e DOCUMENT ME!\r
644      */\r
645     public void mousePressed(MouseEvent e)\r
646     {\r
647         av.setCurrentTree(tree);\r
648 \r
649         int x = e.getX();\r
650         int y = e.getY();\r
651 \r
652         Object ob = findElement(x, y);\r
653 \r
654         if (ob instanceof SequenceI)\r
655         {\r
656             TreeSelectionChanged((Sequence) ob);\r
657             repaint();\r
658 \r
659             return;\r
660         }\r
661         else if (ob instanceof SequenceNode)\r
662         {\r
663             SequenceNode tmpnode = (SequenceNode) ob;\r
664             tree.swapNodes(tmpnode);\r
665             tree.reCount(tree.getTopNode());\r
666             tree.findHeight(tree.getTopNode());\r
667         }\r
668         else\r
669         {\r
670             // Find threshold\r
671             if (tree.getMaxHeight() != 0)\r
672             {\r
673                 threshold = (float) (x - offx) / (float) (getWidth() -\r
674                     labelLength - (2 * offx));\r
675 \r
676                 tree.getGroups().removeAllElements();\r
677                 tree.groupNodes(tree.getTopNode(), threshold);\r
678                 setColor(tree.getTopNode(), Color.black);\r
679 \r
680                 av.setSelectionGroup(null);\r
681                 av.alignment.deleteAllGroups();\r
682 \r
683                 for (int i = 0; i < tree.getGroups().size(); i++)\r
684                 {\r
685                     Color col = new Color((int) (Math.random() * 255),\r
686                             (int) (Math.random() * 255),\r
687                             (int) (Math.random() * 255));\r
688                     setColor((SequenceNode) tree.getGroups().elementAt(i),\r
689                         col.brighter());\r
690 \r
691                     Vector l = tree.findLeaves((SequenceNode) tree.getGroups()\r
692                                                                   .elementAt(i),\r
693                             new Vector());\r
694 \r
695                     Vector sequences = new Vector();\r
696 \r
697                     for (int j = 0; j < l.size(); j++)\r
698                     {\r
699                         SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j)).element();\r
700 \r
701                         if (!sequences.contains(s1))\r
702                         {\r
703                             sequences.addElement(s1);\r
704                         }\r
705                     }\r
706 \r
707                     ColourSchemeI cs = ColourSchemeProperty.getColour(sequences,\r
708                             av.alignment.getWidth(),\r
709                             ColourSchemeProperty.getColourName(\r
710                                 av.getGlobalColourScheme()));\r
711 \r
712                     SequenceGroup sg = new SequenceGroup(sequences,\r
713                             "TreeGroup", cs, true, true, false, 0,\r
714                             av.alignment.getWidth());\r
715 \r
716                     if (sg.cs != null)\r
717                     {\r
718                         sg.cs.setThreshold(25, av.getIgnoreGapsConsensus());\r
719                     }\r
720 \r
721                     if (av.getGlobalColourScheme().conservationApplied())\r
722                     {\r
723                         Conservation c = new Conservation("Group",\r
724                                 ResidueProperties.propHash, 3, sg.sequences,\r
725                                 sg.getStartRes(), sg.getEndRes());\r
726 \r
727                         c.calculate();\r
728                         c.verdict(false, av.ConsPercGaps);\r
729                         sg.cs.setConservation(c);\r
730                     }\r
731 \r
732                     av.alignment.addGroup(sg);\r
733                 }\r
734             }\r
735         }\r
736 \r
737         PaintRefresher.Refresh(this, av.alignment);\r
738         repaint();\r
739     }\r
740 \r
741     /**\r
742      * DOCUMENT ME!\r
743      *\r
744      * @param state DOCUMENT ME!\r
745      */\r
746     public void setShowDistances(boolean state)\r
747     {\r
748         this.showDistances = state;\r
749         repaint();\r
750     }\r
751 \r
752     /**\r
753      * DOCUMENT ME!\r
754      *\r
755      * @param state DOCUMENT ME!\r
756      */\r
757     public void setShowBootstrap(boolean state)\r
758     {\r
759         this.showBootstrap = state;\r
760         repaint();\r
761     }\r
762 \r
763     /**\r
764      * DOCUMENT ME!\r
765      *\r
766      * @param state DOCUMENT ME!\r
767      */\r
768     public void setMarkPlaceholders(boolean state)\r
769     {\r
770         this.markPlaceholders = state;\r
771         repaint();\r
772     }\r
773 }\r