JAL-1640 JAL-1551 formatting/imports/remove dead code
[jalview.git] / src / jalview / gui / SequenceRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.SequenceGroup;
25 import jalview.datamodel.SequenceI;
26 import jalview.schemes.ColourSchemeI;
27
28 import java.awt.Color;
29 import java.awt.FontMetrics;
30 import java.awt.Graphics;
31
32 /**
33  * DOCUMENT ME!
34  * 
35  * @author $author$
36  * @version $Revision$
37  */
38 public class SequenceRenderer implements jalview.api.SequenceRenderer
39 {
40   AlignViewport av;
41
42   FontMetrics fm;
43
44   boolean renderGaps = true;
45
46   SequenceGroup currentSequenceGroup = null;
47
48   SequenceGroup[] allGroups = null;
49
50   Color resBoxColour;
51
52   Graphics graphics;
53
54   boolean monospacedFont;
55
56   boolean forOverview = false;
57
58   /**
59    * Creates a new SequenceRenderer object.
60    * 
61    * @param av
62    *          DOCUMENT ME!
63    */
64   public SequenceRenderer(AlignViewport av)
65   {
66     this.av = av;
67   }
68
69   /**
70    * DOCUMENT ME!
71    * 
72    * @param b
73    *          DOCUMENT ME!
74    */
75   public void prepare(Graphics g, boolean renderGaps)
76   {
77     graphics = g;
78     fm = g.getFontMetrics();
79
80     // If EPS graphics, stringWidth will be a double, not an int
81     double dwidth = fm.getStringBounds("M", g).getWidth();
82
83     monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && av
84             .getCharWidth() == 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.getCharWidth(), avWidth = av.getCharWidth(), avHeight = av
226             .getCharHeight();
227
228     Color tempColour = null;
229
230     while (i <= end)
231     {
232       resBoxColour = Color.white;
233
234       if (i < length)
235       {
236         if (inCurrentSequenceGroup(i))
237         {
238           if (currentSequenceGroup.getDisplayBoxes())
239           {
240             getBoxColour(currentSequenceGroup.cs, seq, i);
241           }
242         }
243         else if (av.getShowBoxes())
244         {
245           getBoxColour(av.getGlobalColourScheme(), seq, i);
246         }
247
248       }
249
250       if (resBoxColour != tempColour)
251       {
252         if (tempColour != null)
253         {
254           graphics.fillRect(avWidth * (curStart - start), y1, curWidth,
255                   avHeight);
256         }
257
258         graphics.setColor(resBoxColour);
259
260         curStart = i;
261         curWidth = avWidth;
262         tempColour = resBoxColour;
263       }
264       else
265       {
266         curWidth += avWidth;
267       }
268
269       i++;
270     }
271
272     graphics.fillRect(avWidth * (curStart - start), y1, curWidth, avHeight);
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.getCharHeight() - av.getCharHeight() / 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.getTextColour());
305
306     if (monospacedFont && av.getShowText() && allGroups.length == 0
307             && !av.getColourText() && av.getThresholdTextColour() == 0)
308     {
309       if (av.isRenderGaps())
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.getTextColour());
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.getThresholdTextColour() > 0)
397           {
398             if (!getboxColour)
399             {
400               getBoxColour(av.getGlobalColourScheme(), seq, i);
401             }
402
403             if (resBoxColour.getRed() + resBoxColour.getBlue()
404                     + resBoxColour.getGreen() < av.getThresholdTextColour())
405             {
406               graphics.setColor(av.getTextColour2());
407             }
408           }
409           if (av.getShowUnconserved())
410           {
411             s = getDisplayChar(srep, i, s,
412                     '.');
413
414           }
415
416         }
417
418         charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
419         graphics.drawString(String.valueOf(s),
420                 charOffset + av.getCharWidth()
421                 * (i - start), y1);
422
423       }
424     }
425   }
426
427   private char getDisplayChar(final boolean usesrep, int position,
428           char s, char c)
429   {
430     // TODO - use currentSequenceGroup rather than alignemnt 
431     // currentSequenceGroup.getConsensus()
432     char conschar = (usesrep) ? av.getAlignment().getSeqrep().getCharAt(position) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
433             .charAt(0);
434     if (conschar != '-' && s == conschar)
435     {
436       s = c;
437     }
438     return s;
439   }
440
441   /**
442    * DOCUMENT ME!
443    * 
444    * @param res
445    *          DOCUMENT ME!
446    * 
447    * @return DOCUMENT ME!
448    */
449   boolean inCurrentSequenceGroup(int res)
450   {
451     if (allGroups == null)
452     {
453       return false;
454     }
455
456     for (int i = 0; i < allGroups.length; i++)
457     {
458       if ((allGroups[i].getStartRes() <= res)
459               && (allGroups[i].getEndRes() >= res))
460       {
461         currentSequenceGroup = allGroups[i];
462
463         return true;
464       }
465     }
466
467     return false;
468   }
469
470   /**
471    * DOCUMENT ME!
472    * 
473    * @param seq
474    *          DOCUMENT ME!
475    * @param start
476    *          DOCUMENT ME!
477    * @param end
478    *          DOCUMENT ME!
479    * @param x1
480    *          DOCUMENT ME!
481    * @param y1
482    *          DOCUMENT ME!
483    * @param width
484    *          DOCUMENT ME!
485    * @param height
486    *          DOCUMENT ME!
487    */
488   public void drawHighlightedText(SequenceI seq, int start, int end,
489           int x1, int y1)
490   {
491     int pady = av.getCharHeight() / 5;
492     int charOffset = 0;
493     graphics.setColor(Color.BLACK);
494     graphics.fillRect(x1, y1, av.getCharWidth() * (end - start + 1),
495             av.getCharHeight());
496     graphics.setColor(Color.white);
497
498     char s = '~';
499
500     // Need to find the sequence position here.
501     if (av.validCharWidth)
502     {
503       for (int i = start; i <= end; i++)
504       {
505         if (i < seq.getLength())
506         {
507           s = seq.getCharAt(i);
508         }
509
510         charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
511         graphics.drawString(String.valueOf(s),
512                 charOffset + x1 + (av.getCharWidth() * (i - start)),
513                 (y1 + av.getCharHeight()) - pady);
514       }
515     }
516   }
517
518   public void drawCursor(SequenceI seq, int res, int x1, int y1)
519   {
520     int pady = av.getCharHeight() / 5;
521     int charOffset = 0;
522     graphics.setColor(Color.black);
523     graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
524
525     if (av.validCharWidth)
526     {
527       graphics.setColor(Color.white);
528
529       char s = seq.getCharAt(res);
530
531       charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
532       graphics.drawString(String.valueOf(s), charOffset + x1,
533               (y1 + av.getCharHeight()) - pady);
534     }
535
536   }
537 }