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