After merge
[jalview.git] / src / jalview / gui / SeqCanvas.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.datamodel.*;\r
22 \r
23 import java.awt.*;\r
24 import java.awt.image.*;\r
25 \r
26 import javax.swing.*;\r
27 import jalview.schemes.*;\r
28 \r
29 \r
30 /**\r
31  * DOCUMENT ME!\r
32  *\r
33  * @author $author$\r
34  * @version $Revision$\r
35  */\r
36 public class SeqCanvas extends JComponent\r
37 {\r
38     FeatureRenderer fr;\r
39     SequenceRenderer sr;\r
40     BufferedImage img;\r
41     Graphics2D gg;\r
42     int imgWidth;\r
43     int imgHeight;\r
44     AlignViewport av;\r
45     boolean displaySearch = false;\r
46     int[] searchResults = null;\r
47     boolean fastPaint = false;\r
48     int LABEL_WEST;\r
49     int LABEL_EAST;\r
50 \r
51     /**\r
52      * Creates a new SeqCanvas object.\r
53      *\r
54      * @param av DOCUMENT ME!\r
55      */\r
56     public SeqCanvas(AlignViewport av)\r
57     {\r
58         this.av = av;\r
59         fr = new FeatureRenderer(av);\r
60         sr = new SequenceRenderer(av);\r
61         setLayout(new BorderLayout());\r
62         PaintRefresher.Register(this, av.alignment);\r
63         setBackground(Color.white);\r
64     }\r
65 \r
66     MCview.PDBCanvas pdbCanvas;\r
67     public SequenceRenderer getSequenceRenderer()\r
68     {\r
69       return sr;\r
70     }\r
71 \r
72     public FeatureRenderer getFeatureRenderer()\r
73     {\r
74       return fr;\r
75     }\r
76 \r
77     public void setPDBCanvas(MCview.PDBCanvas pc)\r
78     {\r
79       pdbCanvas = pc;\r
80     }\r
81 \r
82     public AlignViewport getViewport()\r
83     {\r
84       return av;\r
85     }\r
86 \r
87 \r
88     /**\r
89      * DOCUMENT ME!\r
90      *\r
91      * @param g DOCUMENT ME!\r
92      * @param startx DOCUMENT ME!\r
93      * @param endx DOCUMENT ME!\r
94      * @param ypos DOCUMENT ME!\r
95      */\r
96     void drawNorthScale(Graphics g, int startx, int endx, int ypos)\r
97     {\r
98         int scalestartx = startx - (startx % 10) + 10;\r
99 \r
100         g.setColor(Color.black);\r
101 \r
102         // NORTH SCALE\r
103         for (int i = scalestartx; i < endx; i += 10)\r
104         {\r
105             String string = String.valueOf(i);\r
106             g.drawString(string, (i - startx - 1) * av.charWidth,\r
107                 ypos - (av.charHeight / 2));\r
108 \r
109             g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),\r
110                 (ypos + 2) - (av.charHeight / 2),\r
111                 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -\r
112                 2);\r
113         }\r
114     }\r
115 \r
116     /**\r
117      * DOCUMENT ME!\r
118      *\r
119      * @param g DOCUMENT ME!\r
120      * @param startx DOCUMENT ME!\r
121      * @param endx DOCUMENT ME!\r
122      * @param ypos DOCUMENT ME!\r
123      */\r
124     void drawWestScale(Graphics g, int startx, int endx, int ypos)\r
125     {\r
126         FontMetrics fm = getFontMetrics(av.getFont());\r
127         ypos += av.charHeight;\r
128 \r
129         // EAST SCALE\r
130         for (int i = 0; i < av.alignment.getHeight(); i++)\r
131         {\r
132             SequenceI seq = av.alignment.getSequenceAt(i);\r
133             int index = startx;\r
134             int value = -1;\r
135 \r
136             while (index < endx)\r
137             {\r
138                 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
139                 {\r
140                     index++;\r
141 \r
142                     continue;\r
143                 }\r
144 \r
145                 value = av.alignment.getSequenceAt(i).findPosition(index);\r
146 \r
147                 break;\r
148             }\r
149 \r
150             if (value != -1)\r
151             {\r
152                 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;\r
153                 g.drawString(value + "", x,\r
154                     (ypos + (i * av.charHeight)) - (av.charHeight / 5));\r
155             }\r
156         }\r
157     }\r
158 \r
159     /**\r
160      * DOCUMENT ME!\r
161      *\r
162      * @param g DOCUMENT ME!\r
163      * @param startx DOCUMENT ME!\r
164      * @param endx DOCUMENT ME!\r
165      * @param ypos DOCUMENT ME!\r
166      */\r
167     void drawEastScale(Graphics g, int startx, int endx, int ypos)\r
168     {\r
169         ypos += av.charHeight;\r
170 \r
171         // EAST SCALE\r
172         for (int i = 0; i < av.alignment.getHeight(); i++)\r
173         {\r
174             SequenceI seq = av.alignment.getSequenceAt(i);\r
175             int index = endx;\r
176             int value = -1;\r
177 \r
178             while (index > startx)\r
179             {\r
180                 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
181                 {\r
182                     index--;\r
183 \r
184                     continue;\r
185                 }\r
186 \r
187                 value = av.alignment.getSequenceAt(i).findPosition(index);\r
188 \r
189                 break;\r
190             }\r
191 \r
192             if (value != -1)\r
193             {\r
194                 g.drawString(String.valueOf(value), av.charWidth/2,\r
195                     (ypos + (i * av.charHeight)) - (av.charHeight / 5));\r
196             }\r
197         }\r
198     }\r
199 \r
200     /**\r
201      * DOCUMENT ME!\r
202      *\r
203      * @param horizontal DOCUMENT ME!\r
204      * @param vertical DOCUMENT ME!\r
205      */\r
206     public void fastPaint(int horizontal, int vertical)\r
207     {\r
208         if (gg == null)\r
209         {\r
210             return;\r
211         }\r
212 \r
213         gg.copyArea(horizontal * av.charWidth,\r
214                     vertical * av.charHeight,\r
215                     imgWidth,\r
216                     imgHeight,\r
217                     -horizontal * av.charWidth,\r
218                     -vertical * av.charHeight);\r
219 \r
220         int sr = av.startRes;\r
221         int er = av.endRes;\r
222         int ss = av.startSeq;\r
223         int es = av.endSeq;\r
224         int transX = 0;\r
225         int transY = 0;\r
226 \r
227         if (horizontal > 0) // scrollbar pulled right, image to the left\r
228         {\r
229             er ++;\r
230             transX = (er - sr - horizontal) * av.charWidth;\r
231             sr = er - horizontal;\r
232         }\r
233         else if (horizontal < 0)\r
234         {\r
235             er = sr - horizontal-1;\r
236         }\r
237         else if (vertical > 0) // scroll down\r
238         {\r
239             ss = es - vertical;\r
240 \r
241             if (ss < av.startSeq)\r
242             { // ie scrolling too fast, more than a page at a time\r
243                 ss = av.startSeq;\r
244             }\r
245             else\r
246             {\r
247                 transY = imgHeight - (vertical * av.charHeight);\r
248             }\r
249         }\r
250         else if (vertical < 0)\r
251         {\r
252             es = ss - vertical;\r
253 \r
254             if (es > av.endSeq)\r
255             {\r
256                 es = av.endSeq;\r
257             }\r
258         }\r
259 \r
260         gg.translate(transX, transY);\r
261         drawPanel(gg, sr, er, ss, es, sr, ss, 0);\r
262         gg.translate(-transX, -transY);\r
263 \r
264         fastPaint = true;\r
265         repaint();\r
266     }\r
267 \r
268     /**\r
269      * Definitions of startx and endx (hopefully):\r
270      * SMJS This is what I'm working towards!\r
271      *   startx is the first residue (starting at 0) to display.\r
272      *   endx   is the last residue to display (starting at 0).\r
273      *   starty is the first sequence to display (starting at 0).\r
274      *   endy   is the last sequence to display (starting at 0).\r
275      * NOTE 1: The av limits are set in setFont in this class and\r
276      * in the adjustment listener in SeqPanel when the scrollbars move.\r
277      */\r
278 \r
279     // Set this to false to force a full panel paint\r
280     public void paintComponent(Graphics g)\r
281     {\r
282         super.paintComponent(g);\r
283 \r
284         sr.renderGaps(av.renderGaps);\r
285 \r
286         if ( img != null && (fastPaint\r
287              || (getVisibleRect().width != g.getClipBounds().width)\r
288              || (getVisibleRect().height != g.getClipBounds().height)))\r
289         {\r
290             g.drawImage(img, 0, 0, this);\r
291             fastPaint = false;\r
292             return;\r
293         }\r
294 \r
295 \r
296         // this draws the whole of the alignment\r
297         imgWidth = getWidth();\r
298         imgHeight = getHeight();\r
299 \r
300         imgWidth -= (imgWidth % av.charWidth);\r
301         imgHeight -= (imgHeight % av.charHeight);\r
302 \r
303         if ((imgWidth < 1) || (imgHeight < 1))\r
304         {\r
305             return;\r
306         }\r
307 \r
308         img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);\r
309         gg = (Graphics2D) img.getGraphics();\r
310         gg.setFont(av.getFont());\r
311         gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
312           RenderingHints.VALUE_ANTIALIAS_ON);\r
313 \r
314         gg.setColor(Color.white);\r
315         gg.fillRect(0, 0, imgWidth, imgHeight);\r
316 \r
317 \r
318         if (av.getWrapAlignment())\r
319         {\r
320             drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);\r
321         }\r
322         else\r
323         {\r
324             drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,\r
325                 av.startRes, av.startSeq, 0);\r
326         }\r
327 \r
328         g.drawImage(img, 0, 0, this);\r
329 \r
330         if (pdbCanvas != null)\r
331         {\r
332          pdbCanvas.updateSeqColours();\r
333         }\r
334 \r
335     }\r
336 \r
337     /**\r
338      * DOCUMENT ME!\r
339      *\r
340      * @param cwidth DOCUMENT ME!\r
341      *\r
342      * @return DOCUMENT ME!\r
343      */\r
344     public int getWrappedCanvasWidth(int cwidth)\r
345     {\r
346         FontMetrics fm = getFontMetrics(av.getFont());\r
347 \r
348         LABEL_EAST = 0;\r
349         LABEL_WEST = 0;\r
350 \r
351         if (av.scaleRightWrapped)\r
352         {\r
353             LABEL_EAST = fm.stringWidth(getMask()+"0");\r
354         }\r
355 \r
356         if (av.scaleLeftWrapped)\r
357         {\r
358             LABEL_WEST = fm.stringWidth(getMask());\r
359         }\r
360 \r
361         return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
362     }\r
363 \r
364 \r
365     /**\r
366      * Generates a string of zeroes.\r
367      * @return String\r
368      */\r
369     String getMask()\r
370     {\r
371       String mask = "0";\r
372       for (int i = av.alignment.getWidth(); i > 0; i /= 10)\r
373       {\r
374         mask += "0";\r
375       }\r
376       return mask;\r
377     }\r
378 \r
379     /**\r
380      * DOCUMENT ME!\r
381      *\r
382      * @param g DOCUMENT ME!\r
383      * @param canvasWidth DOCUMENT ME!\r
384      * @param canvasHeight DOCUMENT ME!\r
385      * @param startRes DOCUMENT ME!\r
386      */\r
387     public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,\r
388         int startRes)\r
389     {\r
390 \r
391         AlignmentI al = av.getAlignment();\r
392 \r
393         FontMetrics fm = getFontMetrics(av.getFont());\r
394 \r
395         int LABEL_EAST = 0;\r
396 \r
397         if (av.scaleRightWrapped)\r
398         {\r
399             LABEL_EAST = fm.stringWidth(getMask()+"0");\r
400         }\r
401 \r
402         int LABEL_WEST = 0;\r
403 \r
404         if (av.scaleLeftWrapped)\r
405         {\r
406             LABEL_WEST = fm.stringWidth(getMask());\r
407         }\r
408 \r
409         int hgap = av.charHeight;\r
410         if(av.scaleAboveWrapped)\r
411           hgap += av.charHeight;\r
412 \r
413         int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
414         int cHeight = av.getAlignment().getHeight() * av.charHeight;\r
415 \r
416         av.setWrappedWidth(cWidth);\r
417 \r
418         av.endRes = av.startRes + cWidth;\r
419 \r
420 \r
421         int endx = (startRes + cWidth) - 1;\r
422         int ypos = hgap;\r
423 \r
424 \r
425         while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))\r
426         {\r
427             g.setFont(av.getFont());\r
428             g.setColor(Color.black);\r
429 \r
430             if (av.scaleLeftWrapped)\r
431             {\r
432                 drawWestScale(g, startRes, endx, ypos);\r
433             }\r
434 \r
435             if (av.scaleRightWrapped)\r
436             {\r
437                 g.translate(canvasWidth - LABEL_EAST, 0);\r
438                 drawEastScale(g, startRes, endx, ypos);\r
439                 g.translate(-(canvasWidth - LABEL_EAST), 0);\r
440             }\r
441 \r
442             g.translate(LABEL_WEST, 0);\r
443 \r
444             if (av.scaleAboveWrapped)\r
445             {\r
446                 drawNorthScale(g, startRes, endx, ypos);\r
447             }\r
448 \r
449             // When printing we have an extra clipped region,\r
450             // the Printable page which we need to account for here\r
451             Shape clip = g.getClip();\r
452 \r
453             if (clip == null)\r
454             {\r
455                 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);\r
456             }\r
457             else\r
458             {\r
459                 g.setClip(0, (int) clip.getBounds().getY(),\r
460                     cWidth * av.charWidth, (int) clip.getBounds().getHeight());\r
461             }\r
462 \r
463             if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())\r
464             {\r
465               endx = av.vconsensus.size() - 2;\r
466             }\r
467 \r
468 \r
469             drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);\r
470 \r
471             if(av.showAnnotation)\r
472             {\r
473               g.translate(0, cHeight + ypos + 3);\r
474               if(annotations==null)\r
475                 annotations = new AnnotationPanel(av);\r
476 \r
477               annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);\r
478               g.translate(0, -cHeight - ypos);\r
479             }\r
480             g.setClip(clip);\r
481             g.translate(-LABEL_WEST, 0);\r
482 \r
483             ypos += cHeight+getAnnotationHeight()+hgap;\r
484             if(av.showAnnotation)\r
485               ypos -= 3;\r
486 \r
487             startRes += cWidth;\r
488             endx = (startRes + cWidth) - 1;\r
489 \r
490             if (endx > al.getWidth())\r
491             {\r
492                 endx = al.getWidth();\r
493             }\r
494         }\r
495     }\r
496 \r
497     AnnotationPanel annotations;\r
498     int getAnnotationHeight()\r
499     {\r
500       if(!av.showAnnotation)\r
501         return 0;\r
502 \r
503       if(annotations==null)\r
504         annotations = new AnnotationPanel(av);\r
505 \r
506       return annotations.adjustPanelHeight();\r
507     }\r
508 \r
509     /**\r
510      * DOCUMENT ME!\r
511      *\r
512      * @param g1 DOCUMENT ME!\r
513      * @param x1 DOCUMENT ME!\r
514      * @param x2 DOCUMENT ME!\r
515      * @param y1 DOCUMENT ME!\r
516      * @param y2 DOCUMENT ME!\r
517      * @param startx DOCUMENT ME!\r
518      * @param starty DOCUMENT ME!\r
519      * @param offset DOCUMENT ME!\r
520      */\r
521 \r
522     float aaRatio = 2f/3f;\r
523     public void increaseAARatio()\r
524     {\r
525       aaRatio += .025;\r
526       if(aaRatio>1)\r
527         aaRatio = 1;\r
528 \r
529       repaint();\r
530     }\r
531 \r
532     public void decreaseAARation()\r
533     {\r
534       aaRatio -= .025;\r
535       if(aaRatio<0)\r
536         aaRatio = 0;\r
537 \r
538       repaint();\r
539     }\r
540 \r
541 \r
542     synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,\r
543         int y2, int startx, int starty, int offset)\r
544     {\r
545       Graphics2D g = (Graphics2D) g1;\r
546       g.setFont(av.getFont());\r
547 \r
548       SequenceI nextSeq, dna = null;\r
549       int aaHeight = av.charHeight, dnaHeight = av.charHeight;\r
550       Font dnafont = null, aafont = av.getFont();\r
551 \r
552  /*     if (av.getShowTranslation())\r
553       {\r
554         aaHeight = (int) (av.getCharHeight() * aaRatio);\r
555         dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));\r
556         java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();\r
557         transform.scale(1f / 3f, 1);\r
558         dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),\r
559                                 dnaHeight);\r
560         dnafont = dnafont.deriveFont(transform);\r
561 \r
562         aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),\r
563                                aaHeight);\r
564         transform = new java.awt.geom.AffineTransform();\r
565         transform.scale(1 / aaRatio, 1);\r
566         aafont = aafont.deriveFont(transform);\r
567       }\r
568 */\r
569 \r
570       ColourSchemeI cs = av.getGlobalColourScheme();\r
571 \r
572         /// First draw the sequences\r
573         /////////////////////////////\r
574         for (int i = y1; i < y2; i++)\r
575         {\r
576             nextSeq = av.alignment.getSequenceAt(i);\r
577             g.setFont(aafont);\r
578          /*   if(av.getShowTranslation())\r
579             {\r
580               dna = nextSeq;\r
581               StringBuffer sb = new StringBuffer();\r
582               for(int r = 0; r<nextSeq.getLength(); r+=3)\r
583               {\r
584                 String codon = nextSeq.getSequence(r, r+3);\r
585                 codon = codon.replace('U', 'T');\r
586                 String res = ResidueProperties.codonTranslate(codon);\r
587                 if(res !=null )\r
588                 {\r
589                   if (res.equals("STOP"))\r
590                     sb.append("X");\r
591                   else\r
592                     sb.append(res);\r
593                 }\r
594               }\r
595               nextSeq = new Sequence("d", sb.toString());\r
596             }*/\r
597 \r
598             sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),\r
599                             x1, x2, (x1 - startx) * av.charWidth,\r
600                             offset + ( (i - starty) * av.charHeight), av.charWidth,\r
601                             aaHeight);\r
602 \r
603          /*   if(av.getShowTranslation())\r
604             {\r
605               av.setGlobalColourScheme(new NucleotideColourScheme());\r
606               g.setFont(dnafont);\r
607               sr.drawSequence(g, dna, null,\r
608                               x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,\r
609                               offset + ( (i - starty) * av.charHeight) + aaHeight,\r
610                               av.charWidth / 3,\r
611                               dnaHeight);\r
612               av.setGlobalColourScheme(cs);\r
613             }*/\r
614 \r
615             if (av.showSequenceFeatures)\r
616             {\r
617                 fr.drawSequence(g, nextSeq, x1, x2,\r
618                     (x1 - startx) * av.charWidth,\r
619                     offset + ((i - starty) * av.charHeight), av.charWidth,\r
620                     av.charHeight);\r
621             }\r
622         }\r
623 \r
624         //\r
625         /////////////////////////////////////\r
626         // Now outline any areas if necessary\r
627         /////////////////////////////////////\r
628         SequenceGroup group = av.getSelectionGroup();\r
629 \r
630         int sx = -1;\r
631         int sy = -1;\r
632         int ex = -1;\r
633         int groupIndex = -1;\r
634 \r
635         if ((group == null) && (av.alignment.getGroups().size() > 0))\r
636         {\r
637             group = (SequenceGroup) av.alignment.getGroups().elementAt(0);\r
638             groupIndex = 0;\r
639         }\r
640 \r
641         if (group != null)\r
642         {\r
643             do\r
644             {\r
645                 int oldY = -1;\r
646                 int i = 0;\r
647                 boolean inGroup = false;\r
648                 int top = -1;\r
649                 int bottom = -1;\r
650 \r
651                 for (i = y1; i < y2; i++)\r
652                 {\r
653                     sx = (group.getStartRes() - startx) * av.charWidth;\r
654                     sy = offset + ((i - starty) * av.charHeight);\r
655                     ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -\r
656                         1;\r
657 \r
658                     if(sx+ex<0 || sx>imgWidth)\r
659                     {\r
660                       continue;\r
661                     }\r
662 \r
663                     if ( (sx <= (x2-x1)*av.charWidth) &&\r
664                             group.sequences.contains(av.alignment.getSequenceAt(\r
665                                     i)))\r
666                     {\r
667                         if ((bottom == -1) &&\r
668                                 !group.sequences.contains(\r
669                                     av.alignment.getSequenceAt(i + 1)))\r
670                         {\r
671                             bottom = sy + av.charHeight;\r
672                         }\r
673 \r
674                         if (!inGroup)\r
675                         {\r
676                             if (((top == -1) && (i == 0)) ||\r
677                                     !group.sequences.contains(\r
678                                         av.alignment.getSequenceAt(i - 1)))\r
679                             {\r
680                                 top = sy;\r
681                             }\r
682 \r
683                             oldY = sy;\r
684                             inGroup = true;\r
685 \r
686                             if (group == av.getSelectionGroup())\r
687                             {\r
688                                 g.setStroke(new BasicStroke(1,\r
689                                         BasicStroke.CAP_BUTT,\r
690                                         BasicStroke.JOIN_ROUND, 3f,\r
691                                         new float[] { 5f, 3f }, 0f));\r
692                                 g.setColor(Color.RED);\r
693                             }\r
694                             else\r
695                             {\r
696                                 g.setStroke(new BasicStroke());\r
697                                 g.setColor(group.getOutlineColour());\r
698                             }\r
699                         }\r
700                     }\r
701                     else\r
702                     {\r
703                       if (inGroup)\r
704                       {\r
705                         if (sx >= 0 && sx < imgWidth)\r
706                           g.drawLine(sx, oldY, sx, sy);\r
707 \r
708                         if (sx + ex < imgWidth)\r
709                           g.drawLine(sx + ex, oldY, sx + ex, sy);\r
710 \r
711                         if (sx < 0)\r
712                         {\r
713                           ex += sx;\r
714                           sx = 0;\r
715                         }\r
716 \r
717                         if (sx + ex > imgWidth)\r
718                           ex = imgWidth;\r
719 \r
720                         else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)\r
721                           ex = (x2 - x1 + 1) * av.charWidth;\r
722 \r
723                         if (top != -1)\r
724                         {\r
725                           g.drawLine(sx, top, sx + ex, top);\r
726                           top = -1;\r
727                         }\r
728 \r
729                         if (bottom != -1)\r
730                         {\r
731                           g.drawLine(sx, bottom, sx + ex, bottom);\r
732                           bottom = -1;\r
733                         }\r
734 \r
735                         inGroup = false;\r
736                         }\r
737                     }\r
738                 }\r
739 \r
740                 if (inGroup)\r
741                 {\r
742                   sy = offset + ( (i - starty) * av.charHeight);\r
743                   if (sx >= 0 && sx < imgWidth)\r
744                     g.drawLine(sx, oldY, sx, sy);\r
745 \r
746                   if (sx + ex < imgWidth)\r
747                     g.drawLine(sx + ex, oldY, sx + ex, sy);\r
748 \r
749                   if (sx < 0)\r
750                   {\r
751                     ex += sx;\r
752                     sx = 0;\r
753                   }\r
754 \r
755                   if (sx + ex > imgWidth)\r
756                     ex = imgWidth;\r
757                   else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)\r
758                     ex = (x2 - x1 + 1) * av.charWidth;\r
759 \r
760                   if (top != -1)\r
761                   {\r
762                     g.drawLine(sx, top, sx + ex, top);\r
763                     top = -1;\r
764                   }\r
765 \r
766                   if (bottom != -1)\r
767                   {\r
768                     g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);\r
769                     bottom = -1;\r
770                   }\r
771 \r
772                     inGroup = false;\r
773                 }\r
774 \r
775                 groupIndex++;\r
776 \r
777                 if (groupIndex >= av.alignment.getGroups().size())\r
778                 {\r
779                     break;\r
780                 }\r
781 \r
782                 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);\r
783             }\r
784             while (groupIndex < av.alignment.getGroups().size());\r
785         }\r
786 \r
787         /// Highlight search Results once all sequences have been drawn\r
788         //////////////////////////////////////////////////////////\r
789         if (displaySearch)\r
790         {\r
791             for (int r = 0; r < searchResults.length; r += 3)\r
792             {\r
793                 int searchSeq = searchResults[r];\r
794 \r
795                 if ((searchSeq >= y1) && (searchSeq < y2))\r
796                 {\r
797                     SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);\r
798 \r
799                     int searchStart = seq.findIndex(searchResults[r + 1]) - 1;\r
800                     int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;\r
801 \r
802                     SequenceRenderer ssr = (SequenceRenderer) sr;\r
803 \r
804                     if (searchStart < x1)\r
805                     {\r
806                         searchStart = x1;\r
807                     }\r
808 \r
809                     if (searchEnd > x2)\r
810                     {\r
811                         searchEnd = x2;\r
812                     }\r
813 \r
814                     ssr.drawHighlightedText(seq, searchStart, searchEnd,\r
815                         (searchStart - startx) * av.charWidth,\r
816                         offset + ((searchSeq - starty) * av.charHeight),\r
817                         av.charWidth, av.charHeight);\r
818                 }\r
819             }\r
820         }\r
821     }\r
822 \r
823     /**\r
824      * DOCUMENT ME!\r
825      *\r
826      * @param results DOCUMENT ME!\r
827      */\r
828     public void highlightSearchResults(int[] results)\r
829     {\r
830         // results are in the order sequence, startRes, endRes\r
831         if (results == null)\r
832         {\r
833             displaySearch = false;\r
834         }\r
835         else\r
836         {\r
837             displaySearch = true;\r
838         }\r
839 \r
840         searchResults = results;\r
841 \r
842         repaint();\r
843     }\r
844 }\r