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