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