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