JAL-1683 replace year/version strings with tokens in source
[jalview.git] / src / jalview / gui / SequenceRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.api.FeatureRenderer;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.schemes.ColourSchemeI;
28
29 import java.awt.Color;
30 import java.awt.FontMetrics;
31 import java.awt.Graphics;
32
33 /**
34  * DOCUMENT ME!
35  * 
36  * @author $author$
37  * @version $Revision$
38  */
39 public class SequenceRenderer implements jalview.api.SequenceRenderer
40 {
41   AlignViewport av;
42
43   FontMetrics fm;
44
45   boolean renderGaps = true;
46
47   SequenceGroup currentSequenceGroup = null;
48
49   SequenceGroup[] allGroups = null;
50
51   Color resBoxColour;
52
53   Graphics graphics;
54
55   boolean monospacedFont;
56
57   boolean forOverview = false;
58
59   /**
60    * Creates a new SequenceRenderer object.
61    * 
62    * @param av
63    *          DOCUMENT ME!
64    */
65   public SequenceRenderer(AlignViewport av)
66   {
67     this.av = av;
68   }
69
70   /**
71    * DOCUMENT ME!
72    * 
73    * @param b
74    *          DOCUMENT ME!
75    */
76   public void prepare(Graphics g, boolean renderGaps)
77   {
78     graphics = g;
79     fm = g.getFontMetrics();
80
81     // If EPS graphics, stringWidth will be a double, not an int
82     double dwidth = fm.getStringBounds("M", g).getWidth();
83
84     monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && av.charWidth == dwidth);
85
86     this.renderGaps = renderGaps;
87   }
88
89   @Override
90   public Color getResidueBoxColour(SequenceI seq, int i)
91   {
92     allGroups = av.getAlignment().findAllGroups(seq);
93
94     if (inCurrentSequenceGroup(i))
95     {
96       if (currentSequenceGroup.getDisplayBoxes())
97       {
98         getBoxColour(currentSequenceGroup.cs, seq, i);
99       }
100     }
101     else if (av.getShowBoxes())
102     {
103       getBoxColour(av.getGlobalColourScheme(), seq, i);
104     }
105
106     return resBoxColour;
107   }
108
109   /**
110    * Get the residue colour at the given sequence position - as determined by
111    * the sequence group colour (if any), else the colour scheme, possibly
112    * overridden by a feature colour.
113    * 
114    * @param seq
115    * @param position
116    * @param fr
117    * @return
118    */
119   @Override
120   public Color getResidueColour(final SequenceI seq, int position,
121           FeatureRenderer fr)
122   {
123     // TODO replace 8 or so code duplications with calls to this method
124     // (refactored as needed)
125     Color col = getResidueBoxColour(seq, position);
126
127     if (fr != null)
128     {
129       col = fr.findFeatureColour(col, seq, position);
130     }
131     return col;
132   }
133
134   /**
135    * DOCUMENT ME!
136    * 
137    * @param cs
138    *          DOCUMENT ME!
139    * @param seq
140    *          DOCUMENT ME!
141    * @param i
142    *          DOCUMENT ME!
143    */
144   void getBoxColour(ColourSchemeI cs, SequenceI seq, int i)
145   {
146     if (cs != null)
147     {
148       resBoxColour = cs.findColour(seq.getCharAt(i), i, seq);
149     }
150     else if (forOverview
151             && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
152     {
153       resBoxColour = Color.lightGray;
154     }
155     else
156     {
157       resBoxColour = Color.white;
158     }
159   }
160
161   /**
162    * DOCUMENT ME!
163    * 
164    * @param g
165    *          DOCUMENT ME!
166    * @param seq
167    *          DOCUMENT ME!
168    * @param sg
169    *          DOCUMENT ME!
170    * @param start
171    *          DOCUMENT ME!
172    * @param end
173    *          DOCUMENT ME!
174    * @param x1
175    *          DOCUMENT ME!
176    * @param y1
177    *          DOCUMENT ME!
178    * @param width
179    *          DOCUMENT ME!
180    * @param height
181    *          DOCUMENT ME!
182    */
183   public void drawSequence(SequenceI seq, SequenceGroup[] sg, int start,
184           int end, int y1)
185   {
186     allGroups = sg;
187
188     drawBoxes(seq, start, end, y1);
189
190     if (av.validCharWidth)
191     {
192       drawText(seq, start, end, y1);
193     }
194   }
195
196   /**
197    * DOCUMENT ME!
198    * 
199    * @param seq
200    *          DOCUMENT ME!
201    * @param start
202    *          DOCUMENT ME!
203    * @param end
204    *          DOCUMENT ME!
205    * @param x1
206    *          DOCUMENT ME!
207    * @param y1
208    *          DOCUMENT ME!
209    * @param width
210    *          DOCUMENT ME!
211    * @param height
212    *          DOCUMENT ME!
213    */
214   public synchronized void drawBoxes(SequenceI seq, int start, int end,
215           int y1)
216   {
217     if (seq == null)
218      {
219       return; // fix for racecondition
220     }
221     int i = start;
222     int length = seq.getLength();
223
224     int curStart = -1;
225     int curWidth = av.charWidth;
226
227     Color tempColour = null;
228
229     while (i <= end)
230     {
231       resBoxColour = Color.white;
232
233       if (i < length)
234       {
235         if (inCurrentSequenceGroup(i))
236         {
237           if (currentSequenceGroup.getDisplayBoxes())
238           {
239             getBoxColour(currentSequenceGroup.cs, seq, i);
240           }
241         }
242         else if (av.getShowBoxes())
243         {
244           getBoxColour(av.getGlobalColourScheme(), seq, i);
245         }
246
247       }
248
249       if (resBoxColour != tempColour)
250       {
251         if (tempColour != null)
252         {
253           graphics.fillRect(av.charWidth * (curStart - start), y1,
254                   curWidth, av.charHeight);
255         }
256
257         graphics.setColor(resBoxColour);
258
259         curStart = i;
260         curWidth = av.charWidth;
261         tempColour = resBoxColour;
262       }
263       else
264       {
265         curWidth += av.charWidth;
266       }
267
268       i++;
269     }
270
271     graphics.fillRect(av.charWidth * (curStart - start), y1, curWidth,
272             av.charHeight);
273
274   }
275
276   /**
277    * DOCUMENT ME!
278    * 
279    * @param seq
280    *          DOCUMENT ME!
281    * @param start
282    *          DOCUMENT ME!
283    * @param end
284    *          DOCUMENT ME!
285    * @param x1
286    *          DOCUMENT ME!
287    * @param y1
288    *          DOCUMENT ME!
289    * @param width
290    *          DOCUMENT ME!
291    * @param height
292    *          DOCUMENT ME!
293    */
294   public void drawText(SequenceI seq, int start, int end, int y1)
295   {
296     y1 += av.charHeight - av.charHeight / 5; // height/5 replaces pady
297     int charOffset = 0;
298     char s;
299
300     if (end + 1 >= seq.getLength())
301     {
302       end = seq.getLength() - 1;
303     }
304     graphics.setColor(av.textColour);
305
306     if (monospacedFont && av.showText && allGroups.length == 0
307             && !av.getColourText() && av.thresholdTextColour == 0)
308     {
309       if (av.renderGaps)
310       {
311         graphics.drawString(seq.getSequenceAsString(start, end + 1), 0, y1);
312       }
313       else
314       {
315         char gap = av.getGapCharacter();
316         graphics.drawString(seq.getSequenceAsString(start, end + 1)
317                 .replace(gap, ' '), 0, y1);
318       }
319     }
320     else
321     {
322       boolean srep = av.isDisplayReferenceSeq();
323       boolean getboxColour = false;
324       for (int i = start; i <= end; i++)
325       {
326         graphics.setColor(av.textColour);
327         getboxColour = false;
328         s = seq.getCharAt(i);
329         if (!renderGaps && jalview.util.Comparison.isGap(s))
330         {
331           continue;
332         }
333
334         if (inCurrentSequenceGroup(i))
335         {
336           if (!currentSequenceGroup.getDisplayText())
337           {
338             continue;
339           }
340
341           if (currentSequenceGroup.thresholdTextColour > 0
342                   || currentSequenceGroup.getColourText())
343           {
344             getboxColour = true;
345             getBoxColour(currentSequenceGroup.cs, seq, i);
346
347             if (currentSequenceGroup.getColourText())
348             {
349               graphics.setColor(resBoxColour.darker());
350             }
351
352             if (currentSequenceGroup.thresholdTextColour > 0)
353             {
354               if (resBoxColour.getRed() + resBoxColour.getBlue()
355                       + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
356               {
357                 graphics.setColor(currentSequenceGroup.textColour2);
358               }
359             }
360           }
361           else
362           {
363             graphics.setColor(currentSequenceGroup.textColour);
364           }
365           if (currentSequenceGroup.getShowNonconserved()) // todo optimize
366           {
367             // todo - use sequence group consensus
368             s = getDisplayChar(srep, i, s,
369                     '.');
370
371           }
372
373         }
374         else
375         {
376           if (!av.getShowText())
377           {
378             continue;
379           }
380
381           if (av.getColourText())
382           {
383             getboxColour = true;
384             getBoxColour(av.getGlobalColourScheme(), seq, i);
385
386             if (av.getShowBoxes())
387             {
388               graphics.setColor(resBoxColour.darker());
389             }
390             else
391             {
392               graphics.setColor(resBoxColour);
393             }
394           }
395
396           if (av.thresholdTextColour > 0)
397           {
398             if (!getboxColour)
399             {
400               getBoxColour(av.getGlobalColourScheme(), seq, i);
401             }
402
403             if (resBoxColour.getRed() + resBoxColour.getBlue()
404                     + resBoxColour.getGreen() < av.thresholdTextColour)
405             {
406               graphics.setColor(av.textColour2);
407             }
408           }
409           if (av.getShowUnconserved())
410           {
411             s = getDisplayChar(srep, i, s,
412                     '.');
413
414           }
415
416         }
417
418         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
419         graphics.drawString(String.valueOf(s), charOffset + av.charWidth
420                 * (i - start), y1);
421
422       }
423     }
424   }
425
426   private char getDisplayChar(final boolean usesrep, int position,
427           char s, char c)
428   {
429     // TODO - use currentSequenceGroup rather than alignemnt 
430     // currentSequenceGroup.getConsensus()
431     char conschar = (usesrep) ? av.getAlignment().getSeqrep().getCharAt(position) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
432             .charAt(0);
433     if (conschar != '-' && s == conschar)
434     {
435       s = c;
436     }
437     return s;
438   }
439
440   /**
441    * DOCUMENT ME!
442    * 
443    * @param res
444    *          DOCUMENT ME!
445    * 
446    * @return DOCUMENT ME!
447    */
448   boolean inCurrentSequenceGroup(int res)
449   {
450     if (allGroups == null)
451     {
452       return false;
453     }
454
455     for (int i = 0; i < allGroups.length; i++)
456     {
457       if ((allGroups[i].getStartRes() <= res)
458               && (allGroups[i].getEndRes() >= res))
459       {
460         currentSequenceGroup = allGroups[i];
461
462         return true;
463       }
464     }
465
466     return false;
467   }
468
469   /**
470    * DOCUMENT ME!
471    * 
472    * @param seq
473    *          DOCUMENT ME!
474    * @param start
475    *          DOCUMENT ME!
476    * @param end
477    *          DOCUMENT ME!
478    * @param x1
479    *          DOCUMENT ME!
480    * @param y1
481    *          DOCUMENT ME!
482    * @param width
483    *          DOCUMENT ME!
484    * @param height
485    *          DOCUMENT ME!
486    */
487   public void drawHighlightedText(SequenceI seq, int start, int end,
488           int x1, int y1)
489   {
490     int pady = av.charHeight / 5;
491     int charOffset = 0;
492     graphics.setColor(Color.BLACK);
493     graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
494             av.charHeight);
495     graphics.setColor(Color.white);
496
497     char s = '~';
498
499     // Need to find the sequence position here.
500     if (av.validCharWidth)
501     {
502       for (int i = start; i <= end; i++)
503       {
504         if (i < seq.getLength())
505         {
506           s = seq.getCharAt(i);
507         }
508
509         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
510         graphics.drawString(String.valueOf(s), charOffset + x1
511                 + (av.charWidth * (i - start)), (y1 + av.charHeight) - pady);
512       }
513     }
514   }
515
516   public void drawCursor(SequenceI seq, int res, int x1, int y1)
517   {
518     int pady = av.charHeight / 5;
519     int charOffset = 0;
520     graphics.setColor(Color.black);
521     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
522
523     if (av.validCharWidth)
524     {
525       graphics.setColor(Color.white);
526
527       char s = seq.getCharAt(res);
528
529       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
530       graphics.drawString(String.valueOf(s), charOffset + x1,
531               (y1 + av.charHeight) - pady);
532     }
533
534   }
535 }