updated to jalview 2.1 and begun ArchiveClient/VamsasClient/VamsasStore updates.
[jalview.git] / src / jalview / appletgui / SeqCanvas.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19
20 package jalview.appletgui;
21
22 import java.awt.*;
23
24 import jalview.datamodel.*;
25
26 public class SeqCanvas
27     extends Panel
28 {
29   FeatureRenderer fr;
30   SequenceRenderer sr;
31   Image img;
32   Graphics gg;
33   int imgWidth;
34   int imgHeight;
35
36   AlignViewport av;
37
38   SearchResults searchResults = null;
39
40   boolean fastPaint = false;
41
42
43   int cursorX = 0;
44   int cursorY = 0;
45
46
47   public SeqCanvas(AlignViewport av)
48   {
49     this.av = av;
50     fr = new FeatureRenderer(av);
51     sr = new SequenceRenderer(av);
52     PaintRefresher.Register(this, av.alignment);
53   }
54
55   public AlignViewport getViewport()
56   {
57     return av;
58   }
59
60   public FeatureRenderer getFeatureRenderer()
61   {
62     return fr;
63   }
64
65   MCview.AppletPDBCanvas pdbCanvas;
66   public SequenceRenderer getSequenceRenderer()
67   {
68     return sr;
69   }
70
71   public void setPDBCanvas(MCview.AppletPDBCanvas pc)
72   {
73     pdbCanvas = pc;
74   }
75
76
77   void drawNorthScale(Graphics g, int startx, int endx, int ypos)
78   {
79     int scalestartx = startx - startx % 10 + 10;
80
81     g.setColor(Color.black);
82
83     // NORTH SCALE
84     for (int i = scalestartx; i < endx; i += 10)
85     {
86       int value = i;
87       if(av.hasHiddenColumns)
88           value = av.getColumnSelection().adjustForHiddenColumns(value);
89
90       g.drawString( String.valueOf(value), (i - startx - 1) * av.charWidth,
91           ypos - (av.charHeight / 2));
92
93       g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
94           (ypos + 2) - (av.charHeight / 2),
95           ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
96                 2);
97     }
98   }
99
100   void drawWestScale(Graphics g, int startx, int endx, int ypos)
101   {
102     FontMetrics fm = getFontMetrics(av.getFont());
103     ypos += av.charHeight;
104     if (av.hasHiddenColumns)
105     {
106       startx = av.getColumnSelection().adjustForHiddenColumns(startx);
107       endx = av.getColumnSelection().adjustForHiddenColumns(endx);
108     }
109
110     int maxwidth = av.alignment.getWidth();
111     if (av.hasHiddenColumns)
112         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
113
114       // WEST SCALE
115       for (int i = 0; i < av.alignment.getHeight(); i++)
116       {
117         SequenceI seq = av.alignment.getSequenceAt(i);
118         int index = startx;
119         int value = -1;
120
121         while (index < endx)
122         {
123           if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
124           {
125             index++;
126
127             continue;
128           }
129
130           value = av.alignment.getSequenceAt(i).findPosition(index);
131
132           break;
133         }
134
135         if (value != -1)
136         {
137           int x = LABEL_WEST - fm.stringWidth(String.valueOf(value)) -
138               av.charWidth / 2;
139           g.drawString(value + "", x,
140                        (ypos + (i * av.charHeight)) - (av.charHeight / 5));
141         }
142       }
143     }
144   void drawEastScale(Graphics g, int startx, int endx, int ypos)
145   {
146         ypos += av.charHeight;
147
148         if(av.hasHiddenColumns)
149                 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
150
151         SequenceI seq;
152         // EAST SCALE
153         for (int i = 0; i < av.alignment.getHeight(); i++)
154         {
155             seq = av.alignment.getSequenceAt(i);
156             int index = endx;
157             int value = -1;
158
159             while (index > startx)
160             {
161                 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
162                 {
163                     index--;
164
165                     continue;
166                 }
167
168                 value = seq.findPosition(index);
169
170                 break;
171             }
172
173             if (value != -1)
174             {
175                 g.drawString(String.valueOf(value), 0,
176                     (ypos + (i * av.charHeight)) - (av.charHeight / 5));
177             }
178         }
179     }
180
181   int lastsr=0;
182   void fastPaint(int horizontal, int vertical)
183   {
184     if ( fastPaint || gg == null)
185     {
186       return;
187     }
188
189
190     // Its possible on certain browsers that the call to fastpaint
191     // is faster than it can paint, so this check here catches
192     // this possibility
193     if(lastsr + horizontal != av.startRes)
194     {
195       horizontal = av.startRes - lastsr;
196     }
197
198     lastsr = av.startRes;
199
200     fastPaint = true;
201     gg.copyArea(horizontal * av.charWidth,
202                 vertical * av.charHeight,
203                 imgWidth - horizontal * av.charWidth,
204                 imgHeight - vertical * av.charHeight,
205                 -horizontal * av.charWidth,
206                 -vertical * av.charHeight);
207
208
209
210     int sr = av.startRes, er = av.endRes, ss = av.startSeq, es = av.endSeq,
211         transX = 0, transY = 0;
212
213     if (horizontal > 0) // scrollbar pulled right, image to the left
214     {
215       transX = (er - sr - horizontal) * av.charWidth;
216       sr = er - horizontal;
217     }
218     else if (horizontal < 0)
219     {
220       er = sr - horizontal;
221     }
222
223     else if (vertical > 0) // scroll down
224     {
225       ss = es - vertical;
226       if (ss < av.startSeq) // ie scrolling too fast, more than a page at a time
227       {
228         ss = av.startSeq;
229       }
230       else
231       {
232         transY = imgHeight - vertical * av.charHeight;
233       }
234     }
235     else if (vertical < 0)
236     {
237       es = ss - vertical;
238       if (es > av.endSeq)
239       {
240         es = av.endSeq;
241       }
242     }
243
244     gg.translate(transX, transY);
245
246     drawPanel(gg, sr, er, ss, es, 0);
247     gg.translate( -transX, -transY);
248
249     repaint();
250
251   }
252
253   /**
254    * Definitions of startx and endx (hopefully):
255    * SMJS This is what I'm working towards!
256    *   startx is the first residue (starting at 0) to display.
257    *   endx   is the last residue to display (starting at 0).
258    *   starty is the first sequence to display (starting at 0).
259    *   endy   is the last sequence to display (starting at 0).
260    * NOTE 1: The av limits are set in setFont in this class and
261    * in the adjustment listener in SeqPanel when the scrollbars move.
262    */
263   public void update(Graphics g)
264   {
265     paint(g);
266   }
267
268   public void paint(Graphics g)
269   {
270
271     if (img != null && (fastPaint
272                         || (getSize().width != g.getClipBounds().width)
273                         || (getSize().height != g.getClipBounds().height)))
274     {
275       g.drawImage(img, 0, 0, this);
276       fastPaint = false;
277       return;
278     }
279
280     if (fastPaint)
281     {
282       g.drawImage(img, 0, 0, this);
283       fastPaint = false;
284       return;
285     }
286
287     // this draws the whole of the alignment
288     imgWidth = this.getSize().width;
289     imgHeight = this.getSize().height;
290
291     imgWidth -= imgWidth % av.charWidth;
292     imgHeight -= imgHeight % av.charHeight;
293
294     if (imgWidth < 1 || imgHeight < 1)
295     {
296       return;
297     }
298
299     if (img == null || imgWidth != img.getWidth(this) ||
300         imgHeight != img.getHeight(this))
301     {
302       img = createImage(imgWidth, imgHeight);
303       gg = img.getGraphics();
304       gg.setFont(av.getFont());
305     }
306
307     gg.setColor(Color.white);
308     gg.fillRect(0, 0, imgWidth, imgHeight);
309
310
311     if (av.getWrapAlignment())
312     {
313       drawWrappedPanel(gg, imgWidth, imgHeight, av.startRes);
314     }
315     else
316     {
317       drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
318     }
319
320     g.drawImage(img, 0, 0, this);
321
322     if (pdbCanvas != null)
323     {
324       pdbCanvas.updateSeqColours();
325     }
326   }
327
328   int LABEL_WEST, LABEL_EAST;
329   public int getWrappedCanvasWidth(int cwidth)
330   {
331       cwidth -= cwidth % av.charWidth;
332
333       FontMetrics fm = getFontMetrics(av.getFont());
334
335       LABEL_EAST = 0;
336       LABEL_WEST = 0;
337
338       if (av.scaleRightWrapped)
339       {
340           LABEL_EAST = fm.stringWidth(getMask());
341       }
342
343       if (av.scaleLeftWrapped)
344       {
345           LABEL_WEST = fm.stringWidth(getMask());
346       }
347
348       return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
349   }
350
351
352   /**
353    * Generates a string of zeroes.
354    * @return String
355    */
356   String getMask()
357   {
358     String mask = "00";
359     for (int i = av.alignment.getWidth(); i > 0; i /= 10)
360     {
361       mask += "0";
362     }
363     return mask;
364     }
365
366   public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
367                                int startRes)
368   {
369     AlignmentI al = av.getAlignment();
370
371     FontMetrics fm = getFontMetrics(av.getFont());
372
373
374     if (av.scaleRightWrapped)
375     {
376         LABEL_EAST = fm.stringWidth(getMask());
377     }
378
379     if (av.scaleLeftWrapped)
380     {
381         LABEL_WEST = fm.stringWidth(getMask());
382     }
383
384     int hgap = av.charHeight;
385     if(av.scaleAboveWrapped)
386       hgap += av.charHeight;
387
388     int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
389     int cHeight = av.getAlignment().getHeight() * av.charHeight;
390
391     av.setWrappedWidth(cWidth);
392
393     av.endRes = av.startRes + cWidth;
394
395
396     int endx;
397     int ypos = hgap;
398
399     int maxwidth = av.alignment.getWidth();
400
401     if(av.hasHiddenColumns)
402           maxwidth = av.getColumnSelection().findColumnPosition(maxwidth)-1;
403
404     while ((ypos <= canvasHeight) && (startRes < maxwidth))
405     {
406       endx = startRes + cWidth -1;
407
408       if (endx > maxwidth)
409       {
410         endx = maxwidth;
411       }
412
413         g.setColor(Color.black);
414
415         if (av.scaleLeftWrapped)
416         {
417             drawWestScale(g, startRes, endx, ypos);
418         }
419
420         if (av.scaleRightWrapped)
421         {
422             g.translate(canvasWidth - LABEL_EAST, 0);
423             drawEastScale(g, startRes, endx, ypos);
424             g.translate(-(canvasWidth - LABEL_EAST), 0);
425         }
426
427         g.translate(LABEL_WEST, 0);
428
429         if (av.scaleAboveWrapped)
430         {
431           drawNorthScale(g, startRes, endx, ypos);
432         }
433         if (av.hasHiddenColumns && av.showHiddenMarkers)
434         {
435           g.setColor(Color.blue);
436           int res;
437           for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
438                i++)
439           {
440             res = av.getColumnSelection().findHiddenRegionPosition(i) -
441                 startRes;
442
443             if (res < 0 || res > endx - startRes)
444               continue;
445
446             gg.fillPolygon(new int[]
447                            {res * av.charWidth - av.charHeight / 4,
448                            res * av.charWidth + av.charHeight / 4,
449                            res * av.charWidth},
450                            new int[]
451                            {
452                            ypos - (av.charHeight / 2),
453                            ypos - (av.charHeight / 2),
454                            ypos - (av.charHeight / 2) + 8
455             }, 3);
456
457           }
458         }
459
460         if(g.getClip()==null)
461           g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
462
463         drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
464          g.setClip(null);
465
466
467         if(av.showAnnotation)
468         {
469           g.translate(0, cHeight + ypos+4);
470           if(annotations==null)
471             annotations = new AnnotationPanel(av);
472
473           annotations.drawComponent( g, startRes, endx+1 );
474           g.translate(0, -cHeight - ypos-4);
475         }
476         g.translate(-LABEL_WEST, 0);
477
478         ypos += cHeight+getAnnotationHeight()+hgap;
479
480
481         startRes += cWidth;
482         }
483
484   }
485
486   AnnotationPanel annotations;
487   int getAnnotationHeight()
488   {
489     if(!av.showAnnotation)
490       return 0;
491
492     if(annotations==null)
493       annotations = new AnnotationPanel(av);
494
495     return annotations.adjustPanelHeight();
496     }
497
498     void drawPanel(Graphics g1, int startRes, int endRes,
499                     int startSeq, int endSeq, int offset)
500     {
501       if(!av.hasHiddenColumns)
502       {
503         draw(g1, startRes, endRes, startSeq, endSeq, offset);
504       }
505       else
506       {
507         java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
508
509         int screenY = 0;
510         int blockStart = startRes;
511         int blockEnd = endRes;
512
513         for (int i = 0; i < regions.size(); i++)
514         {
515           int[] region = (int[]) regions.elementAt(i);
516           int hideStart = region[0];
517           int hideEnd = region[1];
518
519           if (hideStart <= blockStart)
520           {
521             blockStart += (hideEnd - hideStart) + 1;
522             continue;
523           }
524
525           blockEnd = hideStart - 1;
526
527           g1.translate(screenY * av.charWidth, 0);
528
529           draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
530
531           if(av.getShowHiddenMarkers())
532           {
533             g1.setColor(Color.blue);
534             g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
535                         startSeq + offset,
536                         (blockEnd - blockStart + 1) * av.charWidth - 1,
537                         startSeq + (endSeq - startSeq) * av.charHeight + offset);
538           }
539
540           g1.translate( -screenY * av.charWidth, 0);
541           screenY += blockEnd - blockStart + 1;
542           blockStart = hideEnd + 1;
543         }
544
545         if (screenY <= (endRes - startRes))
546         {
547           blockEnd = blockStart + (endRes - startRes) - screenY;
548           g1.translate(screenY * av.charWidth, 0);
549           draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
550
551           g1.translate( -screenY * av.charWidth, 0);
552         }
553       }
554
555     }
556
557
558
559
560     //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
561     // int x1, int x2, int y1, int y2, int startx, int starty,
562     void draw(Graphics g,
563                    int startRes, int endRes,
564                    int startSeq, int endSeq,
565                    int offset)
566    {
567       g.setFont(av.getFont());
568       sr.prepare(g, av.renderGaps);
569
570       SequenceI nextSeq;
571
572         /// First draw the sequences
573         /////////////////////////////
574         for (int i = startSeq; i < endSeq; i++)
575         {
576             nextSeq = av.alignment.getSequenceAt(i);
577
578             sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
579                             startRes, endRes,
580                             offset + ( (i - startSeq) * av.charHeight));
581
582             if (av.showSequenceFeatures)
583             {
584                 fr.drawSequence(g, nextSeq, startRes, endRes,
585                     offset + ((i - startSeq) * av.charHeight));
586             }
587
588             /// Highlight search Results once all sequences have been drawn
589             //////////////////////////////////////////////////////////
590             if (searchResults != null)
591             {
592               int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
593               if (visibleResults != null)
594                 for (int r = 0; r < visibleResults.length; r += 2)
595                 {
596                   sr.drawHighlightedText(nextSeq, visibleResults[r],
597                                          visibleResults[r + 1],
598                                          (visibleResults[r] - startRes) * av.charWidth,
599                                          offset + ( (i - startSeq) * av.charHeight));
600                 }
601             }
602
603             if(av.cursorMode && cursorY==i
604                && cursorX>=startRes && cursorX<=endRes)
605             {
606               sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
607                             offset + ( (i - startSeq) * av.charHeight));
608             }
609           }
610
611           if(av.getSelectionGroup()!=null || av.alignment.getGroups().size()>0)
612             drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
613
614    }
615
616    void drawGroupsBoundaries(Graphics g,
617                    int startRes, int endRes,
618                    int startSeq, int endSeq,
619                    int offset)
620    {
621      //
622      /////////////////////////////////////
623      // Now outline any areas if necessary
624      /////////////////////////////////////
625      SequenceGroup group = av.getSelectionGroup();
626
627      int sx = -1;
628      int sy = -1;
629      int ex = -1;
630      int groupIndex = -1;
631
632      if ( (group == null) && (av.alignment.getGroups().size() > 0))
633      {
634        group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
635        groupIndex = 0;
636      }
637
638      if (group != null)
639      {
640        do
641        {
642          int oldY = -1;
643          int i = 0;
644          boolean inGroup = false;
645          int top = -1;
646          int bottom = -1;
647          int alHeight = av.alignment.getHeight()-1;
648
649          for (i = startSeq; i < endSeq; i++)
650          {
651            sx = (group.getStartRes() - startRes) * av.charWidth;
652            sy = offset + ( (i - startSeq) * av.charHeight);
653            ex = ( ( (group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
654                1;
655
656            if (sx + ex < 0 || sx > imgWidth)
657            {
658              continue;
659            }
660
661            if ( (sx <= (endRes - startRes) * av.charWidth) &&
662                group.getSequences(false).
663                contains(av.alignment.getSequenceAt(i)))
664            {
665              if ( (bottom == -1) &&
666              (i >= alHeight ||
667                  !group.getSequences(false).contains(
668                      av.alignment.getSequenceAt(i + 1))))
669              {
670                bottom = sy + av.charHeight;
671              }
672
673              if (!inGroup)
674              {
675                if ( ( (top == -1) && (i == 0)) ||
676                    !group.getSequences(false).contains(
677                        av.alignment.getSequenceAt(i - 1)))
678                {
679                  top = sy;
680                }
681
682                oldY = sy;
683                inGroup = true;
684
685                if (group == av.getSelectionGroup())
686                {
687                  g.setColor(Color.red);
688                }
689                else
690                {
691                  g.setColor(group.getOutlineColour());
692                }
693              }
694            }
695            else
696            {
697              if (inGroup)
698              {
699                if (sx >= 0 && sx < imgWidth)
700                  g.drawLine(sx, oldY, sx, sy);
701
702                if (sx + ex < imgWidth)
703                  g.drawLine(sx + ex, oldY, sx + ex, sy);
704
705                if (sx < 0)
706                {
707                  ex += sx;
708                  sx = 0;
709                }
710
711                if (sx + ex > imgWidth)
712                  ex = imgWidth;
713
714                else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
715                  ex = (endRes - startRes + 1) * av.charWidth;
716
717                if (top != -1)
718                {
719                  g.drawLine(sx, top, sx + ex, top);
720                  top = -1;
721                }
722
723                if (bottom != -1)
724                {
725                  g.drawLine(sx, bottom, sx + ex, bottom);
726                  bottom = -1;
727                }
728
729                inGroup = false;
730              }
731            }
732          }
733
734          if (inGroup)
735          {
736            sy = offset + ( (i - startSeq) * av.charHeight);
737            if (sx >= 0 && sx < imgWidth)
738              g.drawLine(sx, oldY, sx, sy);
739
740            if (sx + ex < imgWidth)
741              g.drawLine(sx + ex, oldY, sx + ex, sy);
742
743            if (sx < 0)
744            {
745              ex += sx;
746              sx = 0;
747            }
748
749            if (sx + ex > imgWidth)
750              ex = imgWidth;
751            else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
752              ex = (endRes - startRes + 1) * av.charWidth;
753
754            if (top != -1)
755            {
756              g.drawLine(sx, top, sx + ex, top);
757              top = -1;
758            }
759
760            if (bottom != -1)
761            {
762              g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
763              bottom = -1;
764            }
765
766            inGroup = false;
767          }
768
769          groupIndex++;
770
771          if (groupIndex >= av.alignment.getGroups().size())
772          {
773            break;
774          }
775
776          group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
777        }
778        while (groupIndex < av.alignment.getGroups().size());
779
780      }
781    }
782
783   public void highlightSearchResults(SearchResults results)
784   {
785     searchResults = results;
786
787     repaint();
788   }
789
790 }