Merge branch 'Release_2_8_1_Branch' into JAL-1372_referenceseq
[jalview.git] / src / jalview / gui / SequenceRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.gui;
20
21 import jalview.datamodel.AlignmentAnnotation;
22 import jalview.datamodel.SequenceGroup;
23 import jalview.datamodel.SequenceI;
24 import jalview.schemes.ColourSchemeI;
25
26 import java.awt.Color;
27 import java.awt.FontMetrics;
28 import java.awt.Graphics;
29
30 /**
31  * DOCUMENT ME!
32  * 
33  * @author $author$
34  * @version $Revision$
35  */
36 public class SequenceRenderer implements jalview.api.SequenceRenderer
37 {
38   AlignViewport av;
39
40   FontMetrics fm;
41
42   boolean renderGaps = true;
43
44   SequenceGroup currentSequenceGroup = null;
45
46   SequenceGroup[] allGroups = null;
47
48   Color resBoxColour;
49
50   Graphics graphics;
51
52   boolean monospacedFont;
53
54   boolean forOverview = false;
55
56   /**
57    * Creates a new SequenceRenderer object.
58    * 
59    * @param av
60    *          DOCUMENT ME!
61    */
62   public SequenceRenderer(AlignViewport av)
63   {
64     this.av = av;
65   }
66
67   /**
68    * DOCUMENT ME!
69    * 
70    * @param b
71    *          DOCUMENT ME!
72    */
73   public void prepare(Graphics g, boolean renderGaps)
74   {
75     graphics = g;
76     fm = g.getFontMetrics();
77
78     // If EPS graphics, stringWidth will be a double, not an int
79     double dwidth = fm.getStringBounds("M", g).getWidth();
80
81     monospacedFont = (dwidth == fm.getStringBounds("|", g).getWidth() && (float) av.charWidth == dwidth);
82
83     this.renderGaps = renderGaps;
84   }
85
86   public Color getResidueBoxColour(SequenceI seq, int i)
87   {
88     allGroups = av.getAlignment().findAllGroups(seq);
89
90     if (inCurrentSequenceGroup(i))
91     {
92       if (currentSequenceGroup.getDisplayBoxes())
93       {
94         getBoxColour(currentSequenceGroup.cs, seq, i);
95       }
96     }
97     else if (av.getShowBoxes())
98     {
99       getBoxColour(av.getGlobalColourScheme(), seq, i);
100     }
101
102     return resBoxColour;
103   }
104
105   /**
106    * DOCUMENT ME!
107    * 
108    * @param cs
109    *          DOCUMENT ME!
110    * @param seq
111    *          DOCUMENT ME!
112    * @param i
113    *          DOCUMENT ME!
114    */
115   void getBoxColour(ColourSchemeI cs, SequenceI seq, int i)
116   {
117     if (cs != null)
118     {
119       resBoxColour = cs.findColour(seq.getCharAt(i), i, seq);
120     }
121     else if (forOverview
122             && !jalview.util.Comparison.isGap(seq.getCharAt(i)))
123     {
124       resBoxColour = Color.lightGray;
125     }
126     else
127     {
128       resBoxColour = Color.white;
129     }
130   }
131
132   /**
133    * DOCUMENT ME!
134    * 
135    * @param g
136    *          DOCUMENT ME!
137    * @param seq
138    *          DOCUMENT ME!
139    * @param sg
140    *          DOCUMENT ME!
141    * @param start
142    *          DOCUMENT ME!
143    * @param end
144    *          DOCUMENT ME!
145    * @param x1
146    *          DOCUMENT ME!
147    * @param y1
148    *          DOCUMENT ME!
149    * @param width
150    *          DOCUMENT ME!
151    * @param height
152    *          DOCUMENT ME!
153    */
154   public void drawSequence(SequenceI seq, SequenceGroup[] sg, int start,
155           int end, int y1)
156   {
157     allGroups = sg;
158
159     drawBoxes(seq, start, end, y1);
160
161     if (av.validCharWidth)
162     {
163       drawText(seq, start, end, y1);
164     }
165   }
166
167   /**
168    * DOCUMENT ME!
169    * 
170    * @param seq
171    *          DOCUMENT ME!
172    * @param start
173    *          DOCUMENT ME!
174    * @param end
175    *          DOCUMENT ME!
176    * @param x1
177    *          DOCUMENT ME!
178    * @param y1
179    *          DOCUMENT ME!
180    * @param width
181    *          DOCUMENT ME!
182    * @param height
183    *          DOCUMENT ME!
184    */
185   public synchronized void drawBoxes(SequenceI seq, int start, int end,
186           int y1)
187   {
188     if (seq == null)
189       return; // fix for racecondition
190     int i = start;
191     int length = seq.getLength();
192
193     int curStart = -1;
194     int curWidth = av.charWidth;
195
196     Color tempColour = null;
197
198     while (i <= end)
199     {
200       resBoxColour = Color.white;
201
202       if (i < length)
203       {
204         if (inCurrentSequenceGroup(i))
205         {
206           if (currentSequenceGroup.getDisplayBoxes())
207           {
208             getBoxColour(currentSequenceGroup.cs, seq, i);
209           }
210         }
211         else if (av.getShowBoxes())
212         {
213           getBoxColour(av.getGlobalColourScheme(), seq, i);
214         }
215
216       }
217
218       if (resBoxColour != tempColour)
219       {
220         if (tempColour != null)
221         {
222           graphics.fillRect(av.charWidth * (curStart - start), y1,
223                   curWidth, av.charHeight);
224         }
225
226         graphics.setColor(resBoxColour);
227
228         curStart = i;
229         curWidth = av.charWidth;
230         tempColour = resBoxColour;
231       }
232       else
233       {
234         curWidth += av.charWidth;
235       }
236
237       i++;
238     }
239
240     graphics.fillRect(av.charWidth * (curStart - start), y1, curWidth,
241             av.charHeight);
242
243   }
244
245   /**
246    * DOCUMENT ME!
247    * 
248    * @param seq
249    *          DOCUMENT ME!
250    * @param start
251    *          DOCUMENT ME!
252    * @param end
253    *          DOCUMENT ME!
254    * @param x1
255    *          DOCUMENT ME!
256    * @param y1
257    *          DOCUMENT ME!
258    * @param width
259    *          DOCUMENT ME!
260    * @param height
261    *          DOCUMENT ME!
262    */
263   public void drawText(SequenceI seq, int start, int end, int y1)
264   {
265     y1 += av.charHeight - av.charHeight / 5; // height/5 replaces pady
266     int charOffset = 0;
267     char s;
268
269     if (end + 1 >= seq.getLength())
270     {
271       end = seq.getLength() - 1;
272     }
273     graphics.setColor(av.textColour);
274
275     if (monospacedFont && av.showText && allGroups.length == 0
276             && !av.getColourText() && av.thresholdTextColour == 0)
277     {
278       if (av.renderGaps)
279       {
280         graphics.drawString(seq.getSequenceAsString(start, end + 1), 0, y1);
281       }
282       else
283       {
284         char gap = av.getGapCharacter();
285         graphics.drawString(seq.getSequenceAsString(start, end + 1)
286                 .replace(gap, ' '), 0, y1);
287       }
288     }
289     else
290     {
291       boolean srep = av.isDisplayReferenceSeq();
292       boolean getboxColour = false;
293       for (int i = start; i <= end; i++)
294       {
295         graphics.setColor(av.textColour);
296         getboxColour = false;
297         s = seq.getCharAt(i);
298         if (!renderGaps && jalview.util.Comparison.isGap(s))
299         {
300           continue;
301         }
302
303         if (inCurrentSequenceGroup(i))
304         {
305           if (!currentSequenceGroup.getDisplayText())
306           {
307             continue;
308           }
309
310           if (currentSequenceGroup.thresholdTextColour > 0
311                   || currentSequenceGroup.getColourText())
312           {
313             getboxColour = true;
314             getBoxColour(currentSequenceGroup.cs, seq, i);
315
316             if (currentSequenceGroup.getColourText())
317             {
318               graphics.setColor(resBoxColour.darker());
319             }
320
321             if (currentSequenceGroup.thresholdTextColour > 0)
322             {
323               if (resBoxColour.getRed() + resBoxColour.getBlue()
324                       + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
325               {
326                 graphics.setColor(currentSequenceGroup.textColour2);
327               }
328             }
329           }
330           else
331           {
332             graphics.setColor(currentSequenceGroup.textColour);
333           }
334           if (currentSequenceGroup.getShowNonconserved()) // todo optimize
335           {
336             // todo - use sequence group consensus
337             s = getDisplayChar(srep, i, s,
338                     '.');
339
340           }
341
342         }
343         else
344         {
345           if (!av.getShowText())
346           {
347             continue;
348           }
349
350           if (av.getColourText())
351           {
352             getboxColour = true;
353             getBoxColour(av.getGlobalColourScheme(), seq, i);
354
355             if (av.getShowBoxes())
356             {
357               graphics.setColor(resBoxColour.darker());
358             }
359             else
360             {
361               graphics.setColor(resBoxColour);
362             }
363           }
364
365           if (av.thresholdTextColour > 0)
366           {
367             if (!getboxColour)
368             {
369               getBoxColour(av.getGlobalColourScheme(), seq, i);
370             }
371
372             if (resBoxColour.getRed() + resBoxColour.getBlue()
373                     + resBoxColour.getGreen() < av.thresholdTextColour)
374             {
375               graphics.setColor(av.textColour2);
376             }
377           }
378           if (av.getShowUnconserved())
379           {
380             s = getDisplayChar(srep, i, s,
381                     '.');
382
383           }
384
385         }
386
387         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
388         graphics.drawString(String.valueOf(s), charOffset + av.charWidth
389                 * (i - start), y1);
390
391       }
392     }
393   }
394
395   private char getDisplayChar(final boolean usesrep, int position,
396           char s, char c)
397   {
398     // TODO - use currentSequenceGroup rather than alignemnt 
399     // currentSequenceGroup.getConsensus()
400     char conschar = (usesrep) ? av.getAlignment().getSeqrep().getCharAt(position) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
401             .charAt(0);
402     if (conschar != '-' && s == conschar)
403     {
404       s = c;
405     }
406     return s;
407   }
408
409   /**
410    * DOCUMENT ME!
411    * 
412    * @param res
413    *          DOCUMENT ME!
414    * 
415    * @return DOCUMENT ME!
416    */
417   boolean inCurrentSequenceGroup(int res)
418   {
419     if (allGroups == null)
420     {
421       return false;
422     }
423
424     for (int i = 0; i < allGroups.length; i++)
425     {
426       if ((allGroups[i].getStartRes() <= res)
427               && (allGroups[i].getEndRes() >= res))
428       {
429         currentSequenceGroup = allGroups[i];
430
431         return true;
432       }
433     }
434
435     return false;
436   }
437
438   /**
439    * DOCUMENT ME!
440    * 
441    * @param seq
442    *          DOCUMENT ME!
443    * @param start
444    *          DOCUMENT ME!
445    * @param end
446    *          DOCUMENT ME!
447    * @param x1
448    *          DOCUMENT ME!
449    * @param y1
450    *          DOCUMENT ME!
451    * @param width
452    *          DOCUMENT ME!
453    * @param height
454    *          DOCUMENT ME!
455    */
456   public void drawHighlightedText(SequenceI seq, int start, int end,
457           int x1, int y1)
458   {
459     int pady = av.charHeight / 5;
460     int charOffset = 0;
461     graphics.setColor(Color.BLACK);
462     graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
463             av.charHeight);
464     graphics.setColor(Color.white);
465
466     char s = '~';
467
468     // Need to find the sequence position here.
469     if (av.validCharWidth)
470     {
471       for (int i = start; i <= end; i++)
472       {
473         if (i < seq.getLength())
474         {
475           s = seq.getCharAt(i);
476         }
477
478         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
479         graphics.drawString(String.valueOf(s), charOffset + x1
480                 + (av.charWidth * (i - start)), (y1 + av.charHeight) - pady);
481       }
482     }
483   }
484
485   public void drawCursor(SequenceI seq, int res, int x1, int y1)
486   {
487     int pady = av.charHeight / 5;
488     int charOffset = 0;
489     graphics.setColor(Color.black);
490     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
491
492     if (av.validCharWidth)
493     {
494       graphics.setColor(Color.white);
495
496       char s = seq.getCharAt(res);
497
498       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
499       graphics.drawString(String.valueOf(s), charOffset + x1,
500               (y1 + av.charHeight) - pady);
501     }
502
503   }
504 }