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