JAL-1640 updates to use get/setter on viewportI: efficiencies - temp variables create...
[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.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
85             .getCharWidth() == dwidth);
86
87     this.renderGaps = renderGaps;
88   }
89
90   @Override
91   public Color getResidueBoxColour(SequenceI seq, int i)
92   {
93     allGroups = av.getAlignment().findAllGroups(seq);
94
95     if (inCurrentSequenceGroup(i))
96     {
97       if (currentSequenceGroup.getDisplayBoxes())
98       {
99         getBoxColour(currentSequenceGroup.cs, seq, i);
100       }
101     }
102     else if (av.getShowBoxes())
103     {
104       getBoxColour(av.getGlobalColourScheme(), seq, i);
105     }
106
107     return resBoxColour;
108   }
109
110   /**
111    * Get the residue colour at the given sequence position - as determined by
112    * the sequence group colour (if any), else the colour scheme, possibly
113    * overridden by a feature colour.
114    * 
115    * @param seq
116    * @param position
117    * @param fr
118    * @return
119    */
120   @Override
121   public Color getResidueColour(final SequenceI seq, int position,
122           FeatureRenderer fr)
123   {
124     // TODO replace 8 or so code duplications with calls to this method
125     // (refactored as needed)
126     Color col = getResidueBoxColour(seq, position);
127
128     if (fr != null)
129     {
130       col = fr.findFeatureColour(col, seq, position);
131     }
132     return col;
133   }
134
135   /**
136    * DOCUMENT ME!
137    * 
138    * @param cs
139    *          DOCUMENT ME!
140    * @param seq
141    *          DOCUMENT ME!
142    * @param i
143    *          DOCUMENT ME!
144    */
145   void getBoxColour(ColourSchemeI cs, SequenceI seq, int i)
146   {
147     if (cs != null)
148     {
149       resBoxColour = cs.findColour(seq.getCharAt(i), i, seq);
150     }
151     else if (forOverview
152             && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
153     {
154       resBoxColour = Color.lightGray;
155     }
156     else
157     {
158       resBoxColour = Color.white;
159     }
160   }
161
162   /**
163    * DOCUMENT ME!
164    * 
165    * @param g
166    *          DOCUMENT ME!
167    * @param seq
168    *          DOCUMENT ME!
169    * @param sg
170    *          DOCUMENT ME!
171    * @param start
172    *          DOCUMENT ME!
173    * @param end
174    *          DOCUMENT ME!
175    * @param x1
176    *          DOCUMENT ME!
177    * @param y1
178    *          DOCUMENT ME!
179    * @param width
180    *          DOCUMENT ME!
181    * @param height
182    *          DOCUMENT ME!
183    */
184   public void drawSequence(SequenceI seq, SequenceGroup[] sg, int start,
185           int end, int y1)
186   {
187     allGroups = sg;
188
189     drawBoxes(seq, start, end, y1);
190
191     if (av.validCharWidth)
192     {
193       drawText(seq, start, end, y1);
194     }
195   }
196
197   /**
198    * DOCUMENT ME!
199    * 
200    * @param seq
201    *          DOCUMENT ME!
202    * @param start
203    *          DOCUMENT ME!
204    * @param end
205    *          DOCUMENT ME!
206    * @param x1
207    *          DOCUMENT ME!
208    * @param y1
209    *          DOCUMENT ME!
210    * @param width
211    *          DOCUMENT ME!
212    * @param height
213    *          DOCUMENT ME!
214    */
215   public synchronized void drawBoxes(SequenceI seq, int start, int end,
216           int y1)
217   {
218     if (seq == null)
219      {
220       return; // fix for racecondition
221     }
222     int i = start;
223     int length = seq.getLength();
224
225     int curStart = -1;
226     int curWidth = av.getCharWidth(), avWidth = av.getCharWidth(), avHeight = av
227             .getCharHeight();
228
229     Color tempColour = null;
230
231     while (i <= end)
232     {
233       resBoxColour = Color.white;
234
235       if (i < length)
236       {
237         if (inCurrentSequenceGroup(i))
238         {
239           if (currentSequenceGroup.getDisplayBoxes())
240           {
241             getBoxColour(currentSequenceGroup.cs, seq, i);
242           }
243         }
244         else if (av.getShowBoxes())
245         {
246           getBoxColour(av.getGlobalColourScheme(), seq, i);
247         }
248
249       }
250
251       if (resBoxColour != tempColour)
252       {
253         if (tempColour != null)
254         {
255           graphics.fillRect(avWidth * (curStart - start), y1, curWidth,
256                   avHeight);
257         }
258
259         graphics.setColor(resBoxColour);
260
261         curStart = i;
262         curWidth = avWidth;
263         tempColour = resBoxColour;
264       }
265       else
266       {
267         curWidth += avWidth;
268       }
269
270       i++;
271     }
272
273     graphics.fillRect(avWidth * (curStart - start), y1, curWidth, avHeight);
274
275   }
276
277   /**
278    * DOCUMENT ME!
279    * 
280    * @param seq
281    *          DOCUMENT ME!
282    * @param start
283    *          DOCUMENT ME!
284    * @param end
285    *          DOCUMENT ME!
286    * @param x1
287    *          DOCUMENT ME!
288    * @param y1
289    *          DOCUMENT ME!
290    * @param width
291    *          DOCUMENT ME!
292    * @param height
293    *          DOCUMENT ME!
294    */
295   public void drawText(SequenceI seq, int start, int end, int y1)
296   {
297     y1 += av.getCharHeight() - av.getCharHeight() / 5; // height/5 replaces pady
298     int charOffset = 0;
299     char s;
300
301     if (end + 1 >= seq.getLength())
302     {
303       end = seq.getLength() - 1;
304     }
305     graphics.setColor(av.getTextColour());
306
307     if (monospacedFont && av.getShowText() && allGroups.length == 0
308             && !av.getColourText() && av.getThresholdTextColour() == 0)
309     {
310       if (av.isRenderGaps())
311       {
312         graphics.drawString(seq.getSequenceAsString(start, end + 1), 0, y1);
313       }
314       else
315       {
316         char gap = av.getGapCharacter();
317         graphics.drawString(seq.getSequenceAsString(start, end + 1)
318                 .replace(gap, ' '), 0, y1);
319       }
320     }
321     else
322     {
323       boolean srep = av.isDisplayReferenceSeq();
324       boolean getboxColour = false;
325       for (int i = start; i <= end; i++)
326       {
327         graphics.setColor(av.getTextColour());
328         getboxColour = false;
329         s = seq.getCharAt(i);
330         if (!renderGaps && jalview.util.Comparison.isGap(s))
331         {
332           continue;
333         }
334
335         if (inCurrentSequenceGroup(i))
336         {
337           if (!currentSequenceGroup.getDisplayText())
338           {
339             continue;
340           }
341
342           if (currentSequenceGroup.thresholdTextColour > 0
343                   || currentSequenceGroup.getColourText())
344           {
345             getboxColour = true;
346             getBoxColour(currentSequenceGroup.cs, seq, i);
347
348             if (currentSequenceGroup.getColourText())
349             {
350               graphics.setColor(resBoxColour.darker());
351             }
352
353             if (currentSequenceGroup.thresholdTextColour > 0)
354             {
355               if (resBoxColour.getRed() + resBoxColour.getBlue()
356                       + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
357               {
358                 graphics.setColor(currentSequenceGroup.textColour2);
359               }
360             }
361           }
362           else
363           {
364             graphics.setColor(currentSequenceGroup.textColour);
365           }
366           if (currentSequenceGroup.getShowNonconserved()) // todo optimize
367           {
368             // todo - use sequence group consensus
369             s = getDisplayChar(srep, i, s,
370                     '.');
371
372           }
373
374         }
375         else
376         {
377           if (!av.getShowText())
378           {
379             continue;
380           }
381
382           if (av.getColourText())
383           {
384             getboxColour = true;
385             getBoxColour(av.getGlobalColourScheme(), seq, i);
386
387             if (av.getShowBoxes())
388             {
389               graphics.setColor(resBoxColour.darker());
390             }
391             else
392             {
393               graphics.setColor(resBoxColour);
394             }
395           }
396
397           if (av.getThresholdTextColour() > 0)
398           {
399             if (!getboxColour)
400             {
401               getBoxColour(av.getGlobalColourScheme(), seq, i);
402             }
403
404             if (resBoxColour.getRed() + resBoxColour.getBlue()
405                     + resBoxColour.getGreen() < av.getThresholdTextColour())
406             {
407               graphics.setColor(av.getTextColour2());
408             }
409           }
410           if (av.getShowUnconserved())
411           {
412             s = getDisplayChar(srep, i, s,
413                     '.');
414
415           }
416
417         }
418
419         charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
420         graphics.drawString(String.valueOf(s),
421                 charOffset + av.getCharWidth()
422                 * (i - start), y1);
423
424       }
425     }
426   }
427
428   private char getDisplayChar(final boolean usesrep, int position,
429           char s, char c)
430   {
431     // TODO - use currentSequenceGroup rather than alignemnt 
432     // currentSequenceGroup.getConsensus()
433     char conschar = (usesrep) ? av.getAlignment().getSeqrep().getCharAt(position) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
434             .charAt(0);
435     if (conschar != '-' && s == conschar)
436     {
437       s = c;
438     }
439     return s;
440   }
441
442   /**
443    * DOCUMENT ME!
444    * 
445    * @param res
446    *          DOCUMENT ME!
447    * 
448    * @return DOCUMENT ME!
449    */
450   boolean inCurrentSequenceGroup(int res)
451   {
452     if (allGroups == null)
453     {
454       return false;
455     }
456
457     for (int i = 0; i < allGroups.length; i++)
458     {
459       if ((allGroups[i].getStartRes() <= res)
460               && (allGroups[i].getEndRes() >= res))
461       {
462         currentSequenceGroup = allGroups[i];
463
464         return true;
465       }
466     }
467
468     return false;
469   }
470
471   /**
472    * DOCUMENT ME!
473    * 
474    * @param seq
475    *          DOCUMENT ME!
476    * @param start
477    *          DOCUMENT ME!
478    * @param end
479    *          DOCUMENT ME!
480    * @param x1
481    *          DOCUMENT ME!
482    * @param y1
483    *          DOCUMENT ME!
484    * @param width
485    *          DOCUMENT ME!
486    * @param height
487    *          DOCUMENT ME!
488    */
489   public void drawHighlightedText(SequenceI seq, int start, int end,
490           int x1, int y1)
491   {
492     int pady = av.getCharHeight() / 5;
493     int charOffset = 0;
494     graphics.setColor(Color.BLACK);
495     graphics.fillRect(x1, y1, av.getCharWidth() * (end - start + 1),
496             av.getCharHeight());
497     graphics.setColor(Color.white);
498
499     char s = '~';
500
501     // Need to find the sequence position here.
502     if (av.validCharWidth)
503     {
504       for (int i = start; i <= end; i++)
505       {
506         if (i < seq.getLength())
507         {
508           s = seq.getCharAt(i);
509         }
510
511         charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
512         graphics.drawString(String.valueOf(s),
513                 charOffset + x1 + (av.getCharWidth() * (i - start)),
514                 (y1 + av.getCharHeight()) - pady);
515       }
516     }
517   }
518
519   public void drawCursor(SequenceI seq, int res, int x1, int y1)
520   {
521     int pady = av.getCharHeight() / 5;
522     int charOffset = 0;
523     graphics.setColor(Color.black);
524     graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
525
526     if (av.validCharWidth)
527     {
528       graphics.setColor(Color.white);
529
530       char s = seq.getCharAt(res);
531
532       charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
533       graphics.drawString(String.valueOf(s), charOffset + x1,
534               (y1 + av.getCharHeight()) - pady);
535     }
536
537   }
538 }