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