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