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