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