JAL-1432 updated copyright notices
[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 getboxColour = false;
292       for (int i = start; i <= end; i++)
293       {
294         graphics.setColor(av.textColour);
295         getboxColour = false;
296         s = seq.getCharAt(i);
297         if (!renderGaps && jalview.util.Comparison.isGap(s))
298         {
299           continue;
300         }
301
302         if (inCurrentSequenceGroup(i))
303         {
304           if (!currentSequenceGroup.getDisplayText())
305           {
306             continue;
307           }
308
309           if (currentSequenceGroup.thresholdTextColour > 0
310                   || currentSequenceGroup.getColourText())
311           {
312             getboxColour = true;
313             getBoxColour(currentSequenceGroup.cs, seq, i);
314
315             if (currentSequenceGroup.getColourText())
316             {
317               graphics.setColor(resBoxColour.darker());
318             }
319
320             if (currentSequenceGroup.thresholdTextColour > 0)
321             {
322               if (resBoxColour.getRed() + resBoxColour.getBlue()
323                       + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
324               {
325                 graphics.setColor(currentSequenceGroup.textColour2);
326               }
327             }
328           }
329           else
330           {
331             graphics.setColor(currentSequenceGroup.textColour);
332           }
333           if (currentSequenceGroup.getShowNonconserved()) // todo optimize
334           {
335             // todo - use sequence group consensus
336             s = getDisplayChar(av.getAlignmentConsensusAnnotation(), i, s,
337                     '.');
338
339           }
340
341         }
342         else
343         {
344           if (!av.getShowText())
345           {
346             continue;
347           }
348
349           if (av.getColourText())
350           {
351             getboxColour = true;
352             getBoxColour(av.getGlobalColourScheme(), seq, i);
353
354             if (av.getShowBoxes())
355             {
356               graphics.setColor(resBoxColour.darker());
357             }
358             else
359             {
360               graphics.setColor(resBoxColour);
361             }
362           }
363
364           if (av.thresholdTextColour > 0)
365           {
366             if (!getboxColour)
367             {
368               getBoxColour(av.getGlobalColourScheme(), seq, i);
369             }
370
371             if (resBoxColour.getRed() + resBoxColour.getBlue()
372                     + resBoxColour.getGreen() < av.thresholdTextColour)
373             {
374               graphics.setColor(av.textColour2);
375             }
376           }
377           if (av.getShowUnconserved())
378           {
379             s = getDisplayChar(av.getAlignmentConsensusAnnotation(), i, s,
380                     '.');
381
382           }
383
384         }
385
386         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
387         graphics.drawString(String.valueOf(s), charOffset + av.charWidth
388                 * (i - start), y1);
389
390       }
391     }
392   }
393
394   private char getDisplayChar(AlignmentAnnotation consensus, int position,
395           char s, char c)
396   {
397     char conschar = consensus.annotations[position].displayCharacter
398             .charAt(0);
399     if (conschar != '-' && s == conschar)
400     {
401       s = c;
402     }
403     return s;
404   }
405
406   /**
407    * DOCUMENT ME!
408    * 
409    * @param res
410    *          DOCUMENT ME!
411    * 
412    * @return DOCUMENT ME!
413    */
414   boolean inCurrentSequenceGroup(int res)
415   {
416     if (allGroups == null)
417     {
418       return false;
419     }
420
421     for (int i = 0; i < allGroups.length; i++)
422     {
423       if ((allGroups[i].getStartRes() <= res)
424               && (allGroups[i].getEndRes() >= res))
425       {
426         currentSequenceGroup = allGroups[i];
427
428         return true;
429       }
430     }
431
432     return false;
433   }
434
435   /**
436    * DOCUMENT ME!
437    * 
438    * @param seq
439    *          DOCUMENT ME!
440    * @param start
441    *          DOCUMENT ME!
442    * @param end
443    *          DOCUMENT ME!
444    * @param x1
445    *          DOCUMENT ME!
446    * @param y1
447    *          DOCUMENT ME!
448    * @param width
449    *          DOCUMENT ME!
450    * @param height
451    *          DOCUMENT ME!
452    */
453   public void drawHighlightedText(SequenceI seq, int start, int end,
454           int x1, int y1)
455   {
456     int pady = av.charHeight / 5;
457     int charOffset = 0;
458     graphics.setColor(Color.BLACK);
459     graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
460             av.charHeight);
461     graphics.setColor(Color.white);
462
463     char s = '~';
464
465     // Need to find the sequence position here.
466     if (av.validCharWidth)
467     {
468       for (int i = start; i <= end; i++)
469       {
470         if (i < seq.getLength())
471         {
472           s = seq.getCharAt(i);
473         }
474
475         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
476         graphics.drawString(String.valueOf(s), charOffset + x1
477                 + (av.charWidth * (i - start)), (y1 + av.charHeight) - pady);
478       }
479     }
480   }
481
482   public void drawCursor(SequenceI seq, int res, int x1, int y1)
483   {
484     int pady = av.charHeight / 5;
485     int charOffset = 0;
486     graphics.setColor(Color.black);
487     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
488
489     if (av.validCharWidth)
490     {
491       graphics.setColor(Color.white);
492
493       char s = seq.getCharAt(res);
494
495       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
496       graphics.drawString(String.valueOf(s), charOffset + x1,
497               (y1 + av.charHeight) - pady);
498     }
499
500   }
501 }