ac5ff899b871c443fe80a41691f4cbb77fe86b50
[jalview.git] / src / jalview / appletgui / 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 \r
20 package jalview.appletgui;\r
21 \r
22 import java.awt.*;\r
23 \r
24 import jalview.datamodel.*;\r
25 \r
26 public class SeqCanvas\r
27     extends Panel\r
28 {\r
29   FeatureRenderer fr;\r
30   SequenceRenderer sr;\r
31   Image img;\r
32   Graphics gg;\r
33   int imgWidth;\r
34   int imgHeight;\r
35 \r
36   AlignViewport av;\r
37 \r
38   SearchResults searchResults = null;\r
39 \r
40   int chunkHeight;\r
41   int chunkWidth;\r
42 \r
43   boolean fastPaint = false;\r
44 \r
45 \r
46   public SeqCanvas(AlignViewport av)\r
47   {\r
48     this.av = av;\r
49     fr = new FeatureRenderer(av);\r
50     sr = new SequenceRenderer(av);\r
51     PaintRefresher.Register(this, av.alignment);\r
52   }\r
53 \r
54   public AlignViewport getViewport()\r
55   {\r
56     return av;\r
57   }\r
58 \r
59   public FeatureRenderer getFeatureRenderer()\r
60   {\r
61     return fr;\r
62   }\r
63 \r
64   MCview.AppletPDBCanvas pdbCanvas;\r
65   public SequenceRenderer getSequenceRenderer()\r
66   {\r
67     return sr;\r
68   }\r
69 \r
70   public void setPDBCanvas(MCview.AppletPDBCanvas pc)\r
71   {\r
72     pdbCanvas = pc;\r
73   }\r
74 \r
75 \r
76   void drawNorthScale(Graphics g, int startx, int endx, int ypos)\r
77   {\r
78     int scalestartx = startx - startx % 10 + 10;\r
79 \r
80     g.setColor(Color.black);\r
81 \r
82     // NORTH SCALE\r
83     for (int i = scalestartx; i < endx; i += 10)\r
84     {\r
85       String string = String.valueOf(i);\r
86       g.drawString(string, (i - startx - 1) * av.charWidth,\r
87                    ypos - av.charHeight / 2);\r
88 \r
89       g.drawLine( (i - startx - 1) * av.charWidth + av.charWidth / 2,\r
90                  ypos + 2 - av.charHeight / 2,\r
91                  (i - startx - 1) * av.charWidth + av.charWidth / 2, ypos - 2);\r
92 \r
93     }\r
94   }\r
95 \r
96   void drawWestScale(Graphics g, int startx, int endx, int ypos)\r
97   {\r
98     FontMetrics fm = getFontMetrics(av.getFont());\r
99     ypos += av.charHeight;\r
100     // EAST SCALE\r
101     for (int i = 0; i < av.alignment.getHeight(); i++)\r
102     {\r
103       SequenceI seq = av.alignment.getSequenceAt(i);\r
104       int index = startx;\r
105       int value = -1;\r
106       while (index < endx)\r
107       {\r
108         if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
109         {\r
110           index++;\r
111           continue;\r
112         }\r
113 \r
114         value = av.alignment.getSequenceAt(i).findPosition(index);\r
115         break;\r
116       }\r
117       if (value != -1)\r
118       {\r
119         int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;\r
120         g.drawString(value + "", x,\r
121                      ypos + i * av.charHeight - av.charHeight / 5);\r
122       }\r
123     }\r
124   }\r
125 \r
126   void drawEastScale(Graphics g, int startx, int endx, int ypos)\r
127   {\r
128     ypos += av.charHeight;\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 = endx;\r
134       int value = -1;\r
135       while (index > startx)\r
136       {\r
137         if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
138         {\r
139           index--;\r
140           continue;\r
141         }\r
142 \r
143         value = av.alignment.getSequenceAt(i).findPosition(index);\r
144         break;\r
145       }\r
146       if (value != -1)\r
147       {\r
148         g.drawString(value + "", av.charWidth/2,\r
149                      ypos + i * av.charHeight - av.charHeight / 5);\r
150       }\r
151     }\r
152 \r
153   }\r
154 \r
155   public void fastPaint(int horizontal, int vertical)\r
156   {\r
157     if (fastPaint || gg == null)\r
158     {\r
159       repaint();\r
160       return;\r
161     }\r
162 \r
163     fastPaint = true;\r
164     gg.copyArea(horizontal * av.charWidth,\r
165                 vertical * av.charHeight,\r
166                 imgWidth,\r
167                 imgHeight,\r
168                 -horizontal * av.charWidth,\r
169                     -vertical * av.charHeight);\r
170 \r
171     int sr = av.startRes, er = av.endRes, ss = av.startSeq, es = av.endSeq,\r
172         transX = 0, transY = 0;\r
173 \r
174     if (horizontal > 0) // scrollbar pulled right, image to the left\r
175     {\r
176       transX = (er - sr - horizontal) * av.charWidth;\r
177       sr = er - horizontal;\r
178     }\r
179     else if (horizontal < 0)\r
180     {\r
181       er = sr - horizontal;\r
182     }\r
183 \r
184     else if (vertical > 0) // scroll down\r
185     {\r
186       ss = es - vertical;\r
187       if (ss < av.startSeq) // ie scrolling too fast, more than a page at a time\r
188       {\r
189         ss = av.startSeq;\r
190       }\r
191       else\r
192       {\r
193         transY = imgHeight - vertical * av.charHeight;\r
194       }\r
195     }\r
196     else if (vertical < 0)\r
197     {\r
198       es = ss - vertical;\r
199       if (es > av.endSeq)\r
200       {\r
201         es = av.endSeq;\r
202       }\r
203     }\r
204 \r
205     gg.translate(transX, transY);\r
206 \r
207     drawPanel(gg, sr, er, ss, es, 0);\r
208     gg.translate( -transX, -transY);\r
209 \r
210     repaint();\r
211 \r
212   }\r
213 \r
214   /**\r
215    * Definitions of startx and endx (hopefully):\r
216    * SMJS This is what I'm working towards!\r
217    *   startx is the first residue (starting at 0) to display.\r
218    *   endx   is the last residue to display (starting at 0).\r
219    *   starty is the first sequence to display (starting at 0).\r
220    *   endy   is the last sequence to display (starting at 0).\r
221    * NOTE 1: The av limits are set in setFont in this class and\r
222    * in the adjustment listener in SeqPanel when the scrollbars move.\r
223    */\r
224   public void update(Graphics g)\r
225   {\r
226     paint(g);\r
227   }\r
228 \r
229   public void paint(Graphics g)\r
230   {\r
231 \r
232     if (fastPaint)\r
233     {\r
234       g.drawImage(img, 0, 0, this);\r
235       fastPaint = false;\r
236       return;\r
237     }\r
238 \r
239     // this draws the whole of the alignment\r
240     imgWidth = this.getSize().width;\r
241     imgHeight = this.getSize().height;\r
242 \r
243     imgWidth -= imgWidth % av.charWidth;\r
244     imgHeight -= imgHeight % av.charHeight;\r
245 \r
246     if (imgWidth < 1 || imgHeight < 1)\r
247     {\r
248       return;\r
249     }\r
250 \r
251     if (img == null || imgWidth != img.getWidth(this) ||\r
252         imgHeight != img.getHeight(this))\r
253     {\r
254       img = createImage(imgWidth, imgHeight);\r
255       gg = img.getGraphics();\r
256       gg.setFont(av.getFont());\r
257     }\r
258 \r
259     gg.setColor(Color.white);\r
260     gg.fillRect(0, 0, imgWidth, imgHeight);\r
261 \r
262 \r
263     if (av.getWrapAlignment())\r
264     {\r
265       drawWrappedPanel(gg, imgWidth, imgHeight, av.startRes);\r
266     }\r
267     else\r
268     {\r
269       drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);\r
270     }\r
271 \r
272     g.drawImage(img, 0, 0, this);\r
273 \r
274     if (pdbCanvas != null)\r
275     {\r
276       pdbCanvas.updateSeqColours();\r
277     }\r
278   }\r
279 \r
280   int LABEL_WEST, LABEL_EAST;\r
281   public int getWrappedCanvasWidth(int cwidth)\r
282   {\r
283       FontMetrics fm = getFontMetrics(av.getFont());\r
284 \r
285       LABEL_EAST = 0;\r
286       LABEL_WEST = 0;\r
287 \r
288       if (av.scaleRightWrapped)\r
289       {\r
290           LABEL_EAST = fm.stringWidth(getMask());\r
291       }\r
292 \r
293       if (av.scaleLeftWrapped)\r
294       {\r
295           LABEL_WEST = fm.stringWidth(getMask());\r
296       }\r
297 \r
298       return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
299   }\r
300 \r
301 \r
302   /**\r
303    * Generates a string of zeroes.\r
304    * @return String\r
305    */\r
306   String getMask()\r
307   {\r
308     String mask = "00";\r
309     for (int i = av.alignment.getWidth(); i > 0; i /= 10)\r
310     {\r
311       mask += "0";\r
312     }\r
313     return mask;\r
314     }\r
315 \r
316   public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,\r
317                                int startRes)\r
318   {\r
319     AlignmentI al = av.getAlignment();\r
320 \r
321     FontMetrics fm = getFontMetrics(av.getFont());\r
322 \r
323     int LABEL_EAST = 0;\r
324 \r
325     if (av.scaleRightWrapped)\r
326     {\r
327         LABEL_EAST = fm.stringWidth(getMask());\r
328     }\r
329 \r
330     int LABEL_WEST = 0;\r
331 \r
332     if (av.scaleLeftWrapped)\r
333     {\r
334         LABEL_WEST = fm.stringWidth(getMask());\r
335     }\r
336 \r
337     int hgap = av.charHeight;\r
338     if(av.scaleAboveWrapped)\r
339       hgap += av.charHeight;\r
340 \r
341     int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
342     int cHeight = av.getAlignment().getHeight() * av.charHeight;\r
343 \r
344     av.setWrappedWidth(cWidth);\r
345 \r
346     av.endRes = av.startRes + cWidth;\r
347 \r
348 \r
349     int endx = (startRes + cWidth) - 1;\r
350     int ypos = hgap;\r
351 \r
352 \r
353     while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))\r
354     {\r
355         g.setColor(Color.black);\r
356 \r
357         if (av.scaleLeftWrapped)\r
358         {\r
359             drawWestScale(g, startRes, endx, ypos);\r
360         }\r
361 \r
362         if (av.scaleRightWrapped)\r
363         {\r
364             g.translate(canvasWidth - LABEL_EAST, 0);\r
365             drawEastScale(g, startRes, endx, ypos);\r
366             g.translate(-(canvasWidth - LABEL_EAST), 0);\r
367         }\r
368 \r
369         g.translate(LABEL_WEST, 0);\r
370 \r
371         if (av.scaleAboveWrapped)\r
372         {\r
373             drawNorthScale(g, startRes, endx, ypos);\r
374         }\r
375 \r
376 \r
377         if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())\r
378         {\r
379           endx = av.vconsensus.size() - 2;\r
380         }\r
381 \r
382 \r
383         if(g.getClip()==null)\r
384           g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);\r
385 \r
386         drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);\r
387          g.setClip(null);\r
388 \r
389 \r
390         if(av.showAnnotation)\r
391         {\r
392           g.translate(0, cHeight + ypos+4);\r
393           if(annotations==null)\r
394             annotations = new AnnotationPanel(av);\r
395 \r
396           annotations.drawComponent( g, startRes, endx + 1);\r
397           g.translate(0, -cHeight - ypos-4);\r
398         }\r
399         g.translate(-LABEL_WEST, 0);\r
400 \r
401         ypos += cHeight+getAnnotationHeight()+hgap;\r
402 \r
403 \r
404         startRes += cWidth;\r
405         endx = (startRes + cWidth) - 1;\r
406 \r
407         if (endx > al.getWidth())\r
408         {\r
409             endx = al.getWidth();\r
410         }\r
411         }\r
412 \r
413   }\r
414 \r
415   AnnotationPanel annotations;\r
416   int getAnnotationHeight()\r
417   {\r
418     if(!av.showAnnotation)\r
419       return 0;\r
420 \r
421     if(annotations==null)\r
422       annotations = new AnnotationPanel(av);\r
423 \r
424     return annotations.adjustPanelHeight();\r
425     }\r
426 \r
427   synchronized public void drawPanel(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset)\r
428   {\r
429 \r
430     g.setFont(av.getFont());\r
431     sr.renderGaps(av.renderGaps);\r
432 \r
433     SequenceI nextSeq;\r
434     /// First draw the sequences\r
435   /////////////////////////////\r
436   for (int i = startSeq; i < endSeq; i++)\r
437   {\r
438     nextSeq = av.alignment.getSequenceAt(i);\r
439 \r
440     sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq), startRes, endRes,\r
441                     (startRes - startRes) * av.charWidth,\r
442                     offset +\r
443                     (i - startSeq) * av.charHeight,\r
444                     av.charWidth, av.charHeight);\r
445 \r
446     if (av.showSequenceFeatures)\r
447     {\r
448       fr.drawSequence(g, nextSeq, startRes, endRes,\r
449                       offset +(i - startSeq) * av.charHeight,\r
450                       av.charWidth, av.charHeight);\r
451     }\r
452     /// Highlight search Results once all sequences have been drawn\r
453    //////////////////////////////////////////////////////////\r
454    if (searchResults != null)\r
455    {\r
456      int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);\r
457      if (visibleResults != null)\r
458        for (int r = 0; r < visibleResults.length; r += 2)\r
459        {\r
460          sr.drawHighlightedText(nextSeq, visibleResults[r],\r
461                                 visibleResults[r + 1],\r
462                                 (visibleResults[r] - startRes) * av.charWidth,\r
463                                 offset + ( (i - startSeq) * av.charHeight),\r
464                                 av.charWidth, av.charHeight);\r
465        }\r
466    }\r
467   }\r
468 \r
469     /////////////////////////////////////\r
470 \r
471     // Now outline any areas if necessary\r
472     /////////////////////////////////////\r
473     SequenceGroup group = av.getSelectionGroup();\r
474     java.util.Vector groups = av.alignment.getGroups();\r
475 \r
476     int sx = -1, sy = -1, ex = -1;\r
477     int groupIndex = -1;\r
478     if (group == null && groups.size() > 0)\r
479     {\r
480       group = (SequenceGroup) groups.elementAt(0);\r
481       groupIndex = 0;\r
482     }\r
483     if (group != null)\r
484     {\r
485         do\r
486         {\r
487             int oldY = -1;\r
488             int i = 0;\r
489             boolean inGroup = false;\r
490             int top = -1;\r
491             int bottom = -1;\r
492 \r
493             for (i = startSeq; i < endSeq; i++)\r
494             {\r
495                 sx = (group.getStartRes() - startRes) * av.charWidth;\r
496                 sy = offset + ((i - startSeq) * av.charHeight);\r
497                 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -\r
498                     1;\r
499 \r
500                 if(sx+ex<0 || sx>imgWidth)\r
501                 {\r
502                   continue;\r
503                 }\r
504 \r
505                 if ( (sx <= (endRes-startRes)*av.charWidth) &&\r
506                         group.sequences.contains(av.alignment.getSequenceAt(\r
507                                 i)))\r
508                 {\r
509 \r
510                     if (bottom == -1)\r
511                    {\r
512                      if(i == endSeq-1 || // Dont check for i+1 if on the bottom row\r
513                       !group.sequences.contains(av.alignment.getSequenceAt(i+1 )))\r
514 \r
515                         bottom = sy + av.charHeight;\r
516                     }\r
517 \r
518                     if (!inGroup)\r
519                     {\r
520                         if (((top == -1) && (i == 0)) ||\r
521                                 !group.sequences.contains(\r
522                                     av.alignment.getSequenceAt(i - 1)))\r
523                         {\r
524                             top = sy;\r
525                         }\r
526 \r
527                         oldY = sy;\r
528                         inGroup = true;\r
529 \r
530                         if (group == av.getSelectionGroup())\r
531                         {\r
532 \r
533                             g.setColor(Color.red);\r
534                         }\r
535                         else\r
536                         {\r
537                             g.setColor(group.getOutlineColour());\r
538                         }\r
539                     }\r
540                 }\r
541                 else\r
542                 {\r
543                   if (inGroup)\r
544                   {\r
545                     if (sx >= 0 && sx < imgWidth)\r
546                       g.drawLine(sx, oldY, sx, sy);\r
547 \r
548                     if (sx + ex < imgWidth)\r
549                       g.drawLine(sx + ex, oldY, sx + ex, sy);\r
550 \r
551                     if (sx < 0)\r
552                     {\r
553                       ex += sx;\r
554                       sx = 0;\r
555                     }\r
556 \r
557                     if (sx + ex > imgWidth)\r
558                       ex = imgWidth;\r
559 \r
560                     else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)\r
561                       ex = (endRes - startRes + 1) * av.charWidth;\r
562 \r
563                     if (top != -1)\r
564                     {\r
565                       g.drawLine(sx, top, sx + ex, top);\r
566                       top = -1;\r
567                     }\r
568 \r
569                     if (bottom != -1)\r
570                     {\r
571                       g.drawLine(sx, bottom, sx + ex, bottom);\r
572                       bottom = -1;\r
573                     }\r
574 \r
575                     inGroup = false;\r
576                     }\r
577                 }\r
578             }\r
579 \r
580             if (inGroup)\r
581             {\r
582               sy = offset + ( (i - startSeq) * av.charHeight);\r
583               if (sx >= 0 && sx < imgWidth)\r
584                 g.drawLine(sx, oldY, sx, sy);\r
585 \r
586               if (sx + ex < imgWidth)\r
587                 g.drawLine(sx + ex, oldY, sx + ex, sy);\r
588 \r
589               if (sx < 0)\r
590               {\r
591                 ex += sx;\r
592                 sx = 0;\r
593               }\r
594 \r
595               if (sx + ex > imgWidth)\r
596                 ex = imgWidth;\r
597               else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)\r
598                 ex = (endRes - startRes + 1) * av.charWidth;\r
599 \r
600               if (top != -1)\r
601               {\r
602                 g.drawLine(sx, top, sx + ex, top);\r
603                 top = -1;\r
604               }\r
605 \r
606               if (bottom != -1)\r
607               {\r
608                 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);\r
609                 bottom = -1;\r
610               }\r
611 \r
612                 inGroup = false;\r
613             }\r
614 \r
615             groupIndex++;\r
616 \r
617             if (groupIndex >= groups.size())\r
618             {\r
619                 break;\r
620             }\r
621 \r
622             group = (SequenceGroup) groups.elementAt(groupIndex);\r
623         }\r
624         while (groupIndex < groups.size());\r
625     }\r
626   }\r
627 \r
628   public void highlightSearchResults(SearchResults results)\r
629   {\r
630     searchResults = results;\r
631 \r
632     repaint();\r
633   }\r
634 \r
635 }\r