JAL-2371 CollectionColourScheme wraps ColourSchemeI
[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.FeatureRenderer;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.datamodel.SequenceI;
26 import jalview.schemes.CollectionColourSchemeI;
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.getViewportColourScheme(), 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 fr
100    * @return
101    */
102   @Override
103   public Color getResidueColour(final SequenceI seq, int position,
104           FeatureRenderer fr)
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 (fr != null)
111     {
112       col = fr.findFeatureColour(col, seq, position);
113     }
114     return col;
115   }
116
117   void getBoxColour(CollectionColourSchemeI collectionColourSchemeI,
118           SequenceI seq, int i)
119   {
120     if (collectionColourSchemeI != null)
121     {
122       resBoxColour = collectionColourSchemeI.findColour(seq.getCharAt(i),
123               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.getGroupColourScheme(), seq,
182                     i);
183           }
184         }
185         else if (av.getShowBoxes())
186         {
187           getBoxColour(av.getViewportColourScheme(), seq, i);
188         }
189       }
190
191       if (resBoxColour != tempColour)
192       {
193         if (tempColour != null)
194         {
195           graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
196                   avCharHeight);
197         }
198         graphics.setColor(resBoxColour);
199
200         curStart = i;
201         curWidth = avCharWidth;
202         tempColour = resBoxColour;
203
204       }
205       else
206       {
207         curWidth += avCharWidth;
208       }
209
210       i++;
211     }
212
213     graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth,
214             avCharHeight);
215   }
216
217   public void drawText(SequenceI seq, int start, int end, int y1)
218   {
219     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
220     Font boldFont = null;
221     boolean bold = false;
222     if (av.isUpperCasebold())
223     {
224       boldFont = new Font(av.getFont().getName(), Font.BOLD, avCharHeight);
225
226       graphics.setFont(av.getFont());
227     }
228
229     y1 += avCharHeight - avCharHeight / 5; // height/5 replaces pady
230
231     int charOffset = 0;
232
233     // Need to find the sequence position here.
234     if (end + 1 >= seq.getLength())
235     {
236       end = seq.getLength() - 1;
237     }
238
239     char s = ' ';
240     boolean srep = av.isDisplayReferenceSeq();
241     for (int i = start; i <= end; i++)
242     {
243       graphics.setColor(Color.black);
244
245       s = seq.getCharAt(i);
246       if (!renderGaps && jalview.util.Comparison.isGap(s))
247       {
248         continue;
249       }
250
251       if (inCurrentSequenceGroup(i))
252       {
253         if (!currentSequenceGroup.getDisplayText())
254         {
255           continue;
256         }
257
258         if (currentSequenceGroup.getColourText())
259         {
260           getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i);
261           graphics.setColor(resBoxColour.darker());
262         }
263         if (currentSequenceGroup.getShowNonconserved())
264         {
265           s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
266         }
267       }
268       else
269       {
270         if (!av.getShowText())
271         {
272           continue;
273         }
274
275         if (av.getColourText())
276         {
277           getBoxColour(av.getViewportColourScheme(), seq, i);
278           if (av.getShowBoxes())
279           {
280             graphics.setColor(resBoxColour.darker());
281           }
282           else
283           {
284             graphics.setColor(resBoxColour);
285           }
286         }
287         if (av.getShowUnconserved())
288         {
289           s = getDisplayChar(srep, i, s, '.', null);
290
291         }
292       }
293
294       if (av.isUpperCasebold())
295       {
296         fm = graphics.getFontMetrics();
297         if ('A' <= s && s <= 'Z')
298         {
299           if (!bold)
300           {
301
302             graphics.setFont(boldFont);
303           }
304           bold = true;
305         }
306         else if (bold)
307         {
308           graphics.setFont(av.font);
309           bold = false;
310         }
311
312       }
313
314       charOffset = (avCharWidth - fm.charWidth(s)) / 2;
315       graphics.drawString(String.valueOf(s), charOffset + avCharWidth
316               * (i - start), y1);
317     }
318
319   }
320
321   /**
322    * Returns 'conservedChar' to represent the given position if the sequence
323    * character at that position is equal to the consensus (ignoring case), else
324    * returns the sequence character
325    * 
326    * @param usesrep
327    * @param position
328    * @param sequenceChar
329    * @param conservedChar
330    * @return
331    */
332   private char getDisplayChar(final boolean usesrep, int position,
333           char sequenceChar, char conservedChar, SequenceGroup currentGroup)
334   {
335     // TODO - use currentSequenceGroup rather than alignment
336     // currentSequenceGroup.getConsensus()
337     char conschar = (usesrep) ? (currentGroup == null
338             || position < currentGroup.getStartRes()
339             || position > currentGroup.getEndRes() ? av.getAlignment()
340             .getSeqrep().getCharAt(position)
341             : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
342                     .getCharAt(position) : av.getAlignment().getSeqrep()
343                     .getCharAt(position)))
344             : (currentGroup != null && currentGroup.getConsensus() != null
345                     && position >= currentGroup.getStartRes()
346                     && position <= currentGroup.getEndRes() && currentGroup
347                     .getConsensus().annotations.length > position) ? currentGroup
348                     .getConsensus().annotations[position].displayCharacter
349                     .charAt(0)
350                     : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
351                             .charAt(0);
352     if (!jalview.util.Comparison.isGap(conschar)
353             && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar))
354     {
355       sequenceChar = conservedChar;
356     }
357     return sequenceChar;
358   }
359
360   boolean inCurrentSequenceGroup(int res)
361   {
362     if (allGroups == null)
363     {
364       return false;
365     }
366
367     for (int i = 0; i < allGroups.length; i++)
368     {
369       if (allGroups[i].getStartRes() <= res
370               && allGroups[i].getEndRes() >= res)
371       {
372         currentSequenceGroup = allGroups[i];
373         return true;
374       }
375     }
376
377     return false;
378   }
379
380   public void drawHighlightedText(SequenceI seq, int start, int end,
381           int x1, int y1)
382   {
383     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
384     int pady = avCharHeight / 5;
385     int charOffset = 0;
386     graphics.setColor(Color.black);
387     graphics.fillRect(x1, y1, avCharWidth * (end - start + 1), avCharHeight);
388     graphics.setColor(Color.white);
389
390     char s = '~';
391     // Need to find the sequence position here.
392     if (av.validCharWidth)
393     {
394       for (int i = start; i <= end; i++)
395       {
396         if (i < seq.getLength())
397         {
398           s = seq.getCharAt(i);
399         }
400
401         charOffset = (avCharWidth - fm.charWidth(s)) / 2;
402         graphics.drawString(String.valueOf(s), charOffset + x1
403                 + avCharWidth * (i - start), y1 + avCharHeight - pady);
404       }
405     }
406   }
407
408   public void drawCursor(SequenceI seq, int res, int x1, int y1)
409   {
410     int pady = av.getCharHeight() / 5;
411     int charOffset = 0;
412     graphics.setColor(Color.black);
413     graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
414     graphics.setColor(Color.white);
415
416     graphics.setColor(Color.white);
417
418     char s = seq.getCharAt(res);
419     if (av.validCharWidth)
420     {
421
422       charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
423       graphics.drawString(String.valueOf(s), charOffset + x1,
424               (y1 + av.getCharHeight()) - pady);
425     }
426   }
427
428 }