618ec8a10a8b2b474e6cf36ed90161afc17755ef
[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, sr, ss, 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, av.startRes,\r
270                 av.startSeq, 0);\r
271     }\r
272 \r
273     g.drawImage(img, 0, 0, this);\r
274 \r
275     if (pdbCanvas != null)\r
276     {\r
277       pdbCanvas.updateSeqColours();\r
278     }\r
279   }\r
280 \r
281   int LABEL_WEST, LABEL_EAST;\r
282   public int getWrappedCanvasWidth(int cwidth)\r
283   {\r
284       FontMetrics fm = getFontMetrics(av.getFont());\r
285 \r
286       LABEL_EAST = 0;\r
287       LABEL_WEST = 0;\r
288 \r
289       if (av.scaleRightWrapped)\r
290       {\r
291           LABEL_EAST = fm.stringWidth(getMask());\r
292       }\r
293 \r
294       if (av.scaleLeftWrapped)\r
295       {\r
296           LABEL_WEST = fm.stringWidth(getMask());\r
297       }\r
298 \r
299       return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
300   }\r
301 \r
302 \r
303   /**\r
304    * Generates a string of zeroes.\r
305    * @return String\r
306    */\r
307   String getMask()\r
308   {\r
309     String mask = "00";\r
310     for (int i = av.alignment.getWidth(); i > 0; i /= 10)\r
311     {\r
312       mask += "0";\r
313     }\r
314     return mask;\r
315     }\r
316 \r
317   public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,\r
318                                int startRes)\r
319   {\r
320     AlignmentI al = av.getAlignment();\r
321 \r
322     FontMetrics fm = getFontMetrics(av.getFont());\r
323 \r
324     int LABEL_EAST = 0;\r
325 \r
326     if (av.scaleRightWrapped)\r
327     {\r
328         LABEL_EAST = fm.stringWidth(getMask());\r
329     }\r
330 \r
331     int LABEL_WEST = 0;\r
332 \r
333     if (av.scaleLeftWrapped)\r
334     {\r
335         LABEL_WEST = fm.stringWidth(getMask());\r
336     }\r
337 \r
338     int hgap = av.charHeight;\r
339     if(av.scaleAboveWrapped)\r
340       hgap += av.charHeight;\r
341 \r
342     int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;\r
343     int cHeight = av.getAlignment().getHeight() * av.charHeight;\r
344 \r
345     av.setWrappedWidth(cWidth);\r
346 \r
347     av.endRes = av.startRes + cWidth;\r
348 \r
349 \r
350     int endx = (startRes + cWidth) - 1;\r
351     int ypos = hgap;\r
352 \r
353 \r
354     while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))\r
355     {\r
356         g.setColor(Color.black);\r
357 \r
358         if (av.scaleLeftWrapped)\r
359         {\r
360             drawWestScale(g, startRes, endx, ypos);\r
361         }\r
362 \r
363         if (av.scaleRightWrapped)\r
364         {\r
365             g.translate(canvasWidth - LABEL_EAST, 0);\r
366             drawEastScale(g, startRes, endx, ypos);\r
367             g.translate(-(canvasWidth - LABEL_EAST), 0);\r
368         }\r
369 \r
370         g.translate(LABEL_WEST, 0);\r
371 \r
372         if (av.scaleAboveWrapped)\r
373         {\r
374             drawNorthScale(g, startRes, endx, ypos);\r
375         }\r
376 \r
377 \r
378         if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())\r
379         {\r
380           endx = av.vconsensus.size() - 2;\r
381         }\r
382 \r
383 \r
384         if(g.getClip()==null)\r
385           g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);\r
386 \r
387         drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);\r
388          g.setClip(null);\r
389 \r
390 \r
391         if(av.showAnnotation)\r
392         {\r
393           g.translate(0, cHeight + ypos+4);\r
394           if(annotations==null)\r
395             annotations = new AnnotationPanel(av);\r
396 \r
397           annotations.drawComponent( g, startRes, endx + 1);\r
398           g.translate(0, -cHeight - ypos-4);\r
399         }\r
400         g.translate(-LABEL_WEST, 0);\r
401 \r
402         ypos += cHeight+getAnnotationHeight()+hgap;\r
403 \r
404 \r
405         startRes += cWidth;\r
406         endx = (startRes + cWidth) - 1;\r
407 \r
408         if (endx > al.getWidth())\r
409         {\r
410             endx = al.getWidth();\r
411         }\r
412         }\r
413 \r
414   }\r
415 \r
416   AnnotationPanel annotations;\r
417   int getAnnotationHeight()\r
418   {\r
419     if(!av.showAnnotation)\r
420       return 0;\r
421 \r
422     if(annotations==null)\r
423       annotations = new AnnotationPanel(av);\r
424 \r
425     return annotations.adjustPanelHeight();\r
426     }\r
427 \r
428   synchronized public void drawPanel(Graphics g, int x1, int x2, int y1, int y2,\r
429                                      int startx, int starty, int offset)\r
430   {\r
431 \r
432     g.setFont(av.getFont());\r
433     sr.renderGaps(av.renderGaps);\r
434 \r
435     SequenceI nextSeq;\r
436     /// First draw the sequences\r
437   /////////////////////////////\r
438   for (int i = y1; i < y2; i++)\r
439   {\r
440     nextSeq = av.alignment.getSequenceAt(i);\r
441 \r
442     sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq), x1, x2,\r
443                     (x1 - startx) * av.charWidth,\r
444                     offset +\r
445                     (i - starty) * av.charHeight,\r
446                     av.charWidth, av.charHeight);\r
447 \r
448     if (av.showSequenceFeatures)\r
449     {\r
450       fr.drawSequence(g, nextSeq, x1, x2,\r
451                       (x1 - startx) * av.charWidth,\r
452                       offset +\r
453                       (i - starty) * av.charHeight,\r
454                       av.charWidth, av.charHeight);\r
455     }\r
456     /// Highlight search Results once all sequences have been drawn\r
457    //////////////////////////////////////////////////////////\r
458    if (searchResults != null)\r
459    {\r
460      int[] visibleResults = searchResults.getResults(nextSeq, x1, x2);\r
461      if (visibleResults != null)\r
462        for (int r = 0; r < visibleResults.length; r += 2)\r
463        {\r
464          sr.drawHighlightedText(nextSeq, visibleResults[r],\r
465                                 visibleResults[r + 1],\r
466                                 (visibleResults[r] - startx) * av.charWidth,\r
467                                 offset + ( (i - starty) * av.charHeight),\r
468                                 av.charWidth, av.charHeight);\r
469        }\r
470    }\r
471   }\r
472 \r
473     /////////////////////////////////////\r
474 \r
475     // Now outline any areas if necessary\r
476     /////////////////////////////////////\r
477     SequenceGroup group = av.getSelectionGroup();\r
478     java.util.Vector groups = av.alignment.getGroups();\r
479 \r
480     int sx = -1, sy = -1, ex = -1;\r
481     int groupIndex = -1;\r
482     if (group == null && groups.size() > 0)\r
483     {\r
484       group = (SequenceGroup) groups.elementAt(0);\r
485       groupIndex = 0;\r
486     }\r
487     if (group != null)\r
488     {\r
489         do\r
490         {\r
491             int oldY = -1;\r
492             int i = 0;\r
493             boolean inGroup = false;\r
494             int top = -1;\r
495             int bottom = -1;\r
496 \r
497             for (i = y1; i < y2; i++)\r
498             {\r
499                 sx = (group.getStartRes() - startx) * av.charWidth;\r
500                 sy = offset + ((i - starty) * av.charHeight);\r
501                 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -\r
502                     1;\r
503 \r
504                 if(sx+ex<0 || sx>imgWidth)\r
505                 {\r
506                   continue;\r
507                 }\r
508 \r
509                 if ( (sx <= (x2-x1)*av.charWidth) &&\r
510                         group.sequences.contains(av.alignment.getSequenceAt(\r
511                                 i)))\r
512                 {\r
513 \r
514                     if (bottom == -1)\r
515                    {\r
516                      if(i == y2-1 || // Dont check for i+1 if on the bottom row\r
517                       !group.sequences.contains(av.alignment.getSequenceAt(i+1 )))\r
518 \r
519                         bottom = sy + av.charHeight;\r
520                     }\r
521 \r
522                     if (!inGroup)\r
523                     {\r
524                         if (((top == -1) && (i == 0)) ||\r
525                                 !group.sequences.contains(\r
526                                     av.alignment.getSequenceAt(i - 1)))\r
527                         {\r
528                             top = sy;\r
529                         }\r
530 \r
531                         oldY = sy;\r
532                         inGroup = true;\r
533 \r
534                         if (group == av.getSelectionGroup())\r
535                         {\r
536 \r
537                             g.setColor(Color.red);\r
538                         }\r
539                         else\r
540                         {\r
541                             g.setColor(group.getOutlineColour());\r
542                         }\r
543                     }\r
544                 }\r
545                 else\r
546                 {\r
547                   if (inGroup)\r
548                   {\r
549                     if (sx >= 0 && sx < imgWidth)\r
550                       g.drawLine(sx, oldY, sx, sy);\r
551 \r
552                     if (sx + ex < imgWidth)\r
553                       g.drawLine(sx + ex, oldY, sx + ex, sy);\r
554 \r
555                     if (sx < 0)\r
556                     {\r
557                       ex += sx;\r
558                       sx = 0;\r
559                     }\r
560 \r
561                     if (sx + ex > imgWidth)\r
562                       ex = imgWidth;\r
563 \r
564                     else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)\r
565                       ex = (x2 - x1 + 1) * av.charWidth;\r
566 \r
567                     if (top != -1)\r
568                     {\r
569                       g.drawLine(sx, top, sx + ex, top);\r
570                       top = -1;\r
571                     }\r
572 \r
573                     if (bottom != -1)\r
574                     {\r
575                       g.drawLine(sx, bottom, sx + ex, bottom);\r
576                       bottom = -1;\r
577                     }\r
578 \r
579                     inGroup = false;\r
580                     }\r
581                 }\r
582             }\r
583 \r
584             if (inGroup)\r
585             {\r
586               sy = offset + ( (i - starty) * av.charHeight);\r
587               if (sx >= 0 && sx < imgWidth)\r
588                 g.drawLine(sx, oldY, sx, sy);\r
589 \r
590               if (sx + ex < imgWidth)\r
591                 g.drawLine(sx + ex, oldY, sx + ex, sy);\r
592 \r
593               if (sx < 0)\r
594               {\r
595                 ex += sx;\r
596                 sx = 0;\r
597               }\r
598 \r
599               if (sx + ex > imgWidth)\r
600                 ex = imgWidth;\r
601               else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)\r
602                 ex = (x2 - x1 + 1) * av.charWidth;\r
603 \r
604               if (top != -1)\r
605               {\r
606                 g.drawLine(sx, top, sx + ex, top);\r
607                 top = -1;\r
608               }\r
609 \r
610               if (bottom != -1)\r
611               {\r
612                 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);\r
613                 bottom = -1;\r
614               }\r
615 \r
616                 inGroup = false;\r
617             }\r
618 \r
619             groupIndex++;\r
620 \r
621             if (groupIndex >= groups.size())\r
622             {\r
623                 break;\r
624             }\r
625 \r
626             group = (SequenceGroup) groups.elementAt(groupIndex);\r
627         }\r
628         while (groupIndex < groups.size());\r
629     }\r
630   }\r
631 \r
632   public void highlightSearchResults(SearchResults results)\r
633   {\r
634     searchResults = results;\r
635 \r
636     repaint();\r
637   }\r
638 \r
639 }\r