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