Merge branch 'develop' into features/JAL-2094_colourInterface
[jalview.git] / src / jalview / appletgui / 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.appletgui;
22
23 import jalview.api.ColorI;
24 import jalview.api.FeatureRenderer;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.schemes.Colour;
28 import jalview.schemes.ColourSchemeI;
29
30 import java.awt.Color;
31 import java.awt.Font;
32 import java.awt.FontMetrics;
33 import java.awt.Graphics;
34
35 public class SequenceRenderer implements jalview.api.SequenceRenderer
36 {
37   final static int CHAR_TO_UPPER = 'A' - 'a';
38
39   AlignViewport av;
40
41   FontMetrics fm;
42
43   boolean renderGaps = true;
44
45   SequenceGroup currentSequenceGroup = null;
46
47   SequenceGroup[] allGroups = null;
48
49   Color resBoxColour;
50
51   Graphics graphics;
52
53   boolean forOverview = false;
54
55   public SequenceRenderer(AlignViewport av)
56   {
57     this.av = av;
58   }
59
60   /**
61    * DOCUMENT ME!
62    * 
63    * @param b
64    *          DOCUMENT ME!
65    */
66   public void prepare(Graphics g, boolean renderGaps)
67   {
68     graphics = g;
69     fm = g.getFontMetrics();
70
71     this.renderGaps = renderGaps;
72   }
73
74   @Override
75   public ColorI getResidueBoxColour(SequenceI seq, int i)
76   {
77     allGroups = av.getAlignment().findAllGroups(seq);
78
79     if (inCurrentSequenceGroup(i))
80     {
81       if (currentSequenceGroup.getDisplayBoxes())
82       {
83         getBoxColour(currentSequenceGroup.cs, seq, i);
84       }
85     }
86     else if (av.getShowBoxes())
87     {
88       getBoxColour(av.getGlobalColourScheme(), seq, i);
89     }
90
91     return new Colour(resBoxColour);
92   }
93
94   /**
95    * Get the residue colour at the given sequence position - as determined by
96    * the sequence group colour (if any), else the colour scheme, possibly
97    * overridden by a feature colour.
98    * 
99    * @param seq
100    * @param position
101    * @param fr
102    * @return
103    */
104   @Override
105   public ColorI getResidueColour(final SequenceI seq, int position,
106           FeatureRenderer fr)
107   {
108     // TODO replace 8 or so code duplications with calls to this method
109     // (refactored as needed)
110     ColorI col = getResidueBoxColour(seq, position);
111
112     if (fr != null)
113     {
114       col = fr.findFeatureColour(col, seq, position);
115     }
116     return col;
117   }
118
119   void getBoxColour(ColourSchemeI cs, SequenceI seq, int i)
120   {
121     if (cs != null)
122     {
123       resBoxColour = cs.findColour(seq.getCharAt(i), i, seq);
124     }
125     else if (forOverview
126             && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
127     {
128       resBoxColour = Color.lightGray;
129     }
130     else
131     {
132       resBoxColour = Color.white;
133     }
134
135   }
136
137   public Color findSequenceColour(SequenceI seq, int i)
138   {
139     allGroups = av.getAlignment().findAllGroups(seq);
140     drawBoxes(seq, i, i, 0);
141     return resBoxColour;
142   }
143
144   public void drawSequence(SequenceI seq, SequenceGroup[] sg, int start,
145           int end, int y1)
146   {
147     if (seq == null)
148     {
149       return;
150     }
151
152     allGroups = sg;
153
154     drawBoxes(seq, start, end, y1);
155
156     if (av.validCharWidth)
157     {
158       drawText(seq, start, end, y1);
159     }
160   }
161
162   public void drawBoxes(SequenceI seq, int start, int end, int y1)
163   {
164     int i = start;
165     int length = seq.getLength();
166
167     int curStart = -1;
168     int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(), avCharHeight = av
169             .getCharHeight();
170
171     Color tempColour = null;
172     while (i <= end)
173     {
174       resBoxColour = Color.white;
175       if (i < length)
176       {
177         if (inCurrentSequenceGroup(i))
178         {
179           if (currentSequenceGroup.getDisplayBoxes())
180           {
181             getBoxColour(currentSequenceGroup.cs, seq, i);
182           }
183         }
184         else if (av.getShowBoxes())
185         {
186           getBoxColour(av.getGlobalColourScheme(), seq, i);
187         }
188       }
189
190       if (resBoxColour != tempColour)
191       {
192         if (tempColour != null)
193         {
194           graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
195                   avCharHeight);
196         }
197         graphics.setColor(resBoxColour);
198
199         curStart = i;
200         curWidth = avCharWidth;
201         tempColour = resBoxColour;
202
203       }
204       else
205       {
206         curWidth += avCharWidth;
207       }
208
209       i++;
210     }
211
212     graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
213             avCharHeight);
214   }
215
216   public void drawText(SequenceI seq, int start, int end, int y1)
217   {
218     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
219     Font boldFont = null;
220     boolean bold = false;
221     if (av.isUpperCasebold())
222     {
223       boldFont = new Font(av.getFont().getName(), Font.BOLD, avCharHeight);
224
225       graphics.setFont(av.getFont());
226     }
227
228     y1 += avCharHeight - avCharHeight / 5; // height/5 replaces pady
229
230     int charOffset = 0;
231
232     // Need to find the sequence position here.
233     if (end + 1 >= seq.getLength())
234     {
235       end = seq.getLength() - 1;
236     }
237
238     char s = ' ';
239     boolean srep = av.isDisplayReferenceSeq();
240     for (int i = start; i <= end; i++)
241     {
242       graphics.setColor(Color.black);
243
244       s = seq.getCharAt(i);
245       if (!renderGaps && jalview.util.Comparison.isGap(s))
246       {
247         continue;
248       }
249
250       if (inCurrentSequenceGroup(i))
251       {
252         if (!currentSequenceGroup.getDisplayText())
253         {
254           continue;
255         }
256
257         if (currentSequenceGroup.getColourText())
258         {
259           getBoxColour(currentSequenceGroup.cs, seq, i);
260           graphics.setColor(resBoxColour.darker());
261         }
262         if (currentSequenceGroup.getShowNonconserved())
263         {
264           s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
265         }
266       }
267       else
268       {
269         if (!av.getShowText())
270         {
271           continue;
272         }
273
274         if (av.getColourText())
275         {
276           getBoxColour(av.getGlobalColourScheme(), seq, i);
277           if (av.getShowBoxes())
278           {
279             graphics.setColor(resBoxColour.darker());
280           }
281           else
282           {
283             graphics.setColor(resBoxColour);
284           }
285         }
286         if (av.getShowUnconserved())
287         {
288           s = getDisplayChar(srep, i, s, '.', null);
289
290         }
291       }
292
293       if (av.isUpperCasebold())
294       {
295         fm = graphics.getFontMetrics();
296         if ('A' <= s && s <= 'Z')
297         {
298           if (!bold)
299           {
300
301             graphics.setFont(boldFont);
302           }
303           bold = true;
304         }
305         else if (bold)
306         {
307           graphics.setFont(av.font);
308           bold = false;
309         }
310
311       }
312
313       charOffset = (avCharWidth - fm.charWidth(s)) / 2;
314       graphics.drawString(String.valueOf(s), charOffset + avCharWidth
315               * (i - start), y1);
316     }
317
318   }
319
320   /**
321    * Returns 'conservedChar' to represent the given position if the sequence
322    * character at that position is equal to the consensus (ignoring case), else
323    * returns the sequence character
324    * 
325    * @param usesrep
326    * @param position
327    * @param sequenceChar
328    * @param conservedChar
329    * @return
330    */
331   private char getDisplayChar(final boolean usesrep, int position,
332           char sequenceChar, char conservedChar, SequenceGroup currentGroup)
333   {
334     // TODO - use currentSequenceGroup rather than alignment
335     // currentSequenceGroup.getConsensus()
336     char conschar = (usesrep) ? (currentGroup == null
337             || position < currentGroup.getStartRes()
338             || position > currentGroup.getEndRes() ? av.getAlignment()
339             .getSeqrep().getCharAt(position)
340             : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
341                     .getCharAt(position) : av.getAlignment().getSeqrep()
342                     .getCharAt(position)))
343             : (currentGroup != null && currentGroup.getConsensus() != null
344                     && position >= currentGroup.getStartRes()
345                     && position <= currentGroup.getEndRes() && currentGroup
346                     .getConsensus().annotations.length > position) ? currentGroup
347                     .getConsensus().annotations[position].displayCharacter
348                     .charAt(0)
349                     : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
350                             .charAt(0);
351     if (!jalview.util.Comparison.isGap(conschar)
352             && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar))
353     {
354       sequenceChar = conservedChar;
355     }
356     return sequenceChar;
357   }
358
359   boolean inCurrentSequenceGroup(int res)
360   {
361     if (allGroups == null)
362     {
363       return false;
364     }
365
366     for (int i = 0; i < allGroups.length; i++)
367     {
368       if (allGroups[i].getStartRes() <= res
369               && allGroups[i].getEndRes() >= res)
370       {
371         currentSequenceGroup = allGroups[i];
372         return true;
373       }
374     }
375
376     return false;
377   }
378
379   public void drawHighlightedText(SequenceI seq, int start, int end,
380           int x1, int y1)
381   {
382     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
383     int pady = avCharHeight / 5;
384     int charOffset = 0;
385     graphics.setColor(Color.black);
386     graphics.fillRect(x1, y1, avCharWidth * (end - start + 1), avCharHeight);
387     graphics.setColor(Color.white);
388
389     char s = '~';
390     // Need to find the sequence position here.
391     if (av.validCharWidth)
392     {
393       for (int i = start; i <= end; i++)
394       {
395         if (i < seq.getLength())
396         {
397           s = seq.getCharAt(i);
398         }
399
400         charOffset = (avCharWidth - fm.charWidth(s)) / 2;
401         graphics.drawString(String.valueOf(s), charOffset + x1
402                 + avCharWidth * (i - start), y1 + avCharHeight - pady);
403       }
404     }
405   }
406
407   public void drawCursor(SequenceI seq, int res, int x1, int y1)
408   {
409     int pady = av.getCharHeight() / 5;
410     int charOffset = 0;
411     graphics.setColor(Color.black);
412     graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
413     graphics.setColor(Color.white);
414
415     graphics.setColor(Color.white);
416
417     char s = seq.getCharAt(res);
418     if (av.validCharWidth)
419     {
420
421       charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
422       graphics.drawString(String.valueOf(s), charOffset + x1,
423               (y1 + av.getCharHeight()) - pady);
424     }
425   }
426
427 }