98329b4920fef212710fe3c504bfdccfde4e0d95
[jalview.git] / src / jalview / gui / SequenceRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 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     int i = start;
185     int length = seq.getLength();
186
187     int curStart = -1;
188     int curWidth = av.charWidth;
189
190     Color tempColour = null;
191
192     while (i <= end)
193     {
194       resBoxColour = Color.white;
195
196       if (i < length)
197       {
198         if (inCurrentSequenceGroup(i))
199         {
200           if (currentSequenceGroup.getDisplayBoxes())
201           {
202             getBoxColour(currentSequenceGroup.cs, seq, i);
203           }
204         }
205         else if (av.getShowBoxes())
206         {
207           getBoxColour(av.globalColourScheme, seq, i);
208         }
209
210       }
211
212       if (resBoxColour != tempColour)
213       {
214         if (tempColour != null)
215         {
216           graphics.fillRect(av.charWidth * (curStart - start), y1,
217                   curWidth, av.charHeight);
218         }
219
220         graphics.setColor(resBoxColour);
221
222         curStart = i;
223         curWidth = av.charWidth;
224         tempColour = resBoxColour;
225       }
226       else
227       {
228         curWidth += av.charWidth;
229       }
230
231       i++;
232     }
233
234     graphics.fillRect(av.charWidth * (curStart - start), y1, curWidth,
235             av.charHeight);
236
237   }
238
239   /**
240    * DOCUMENT ME!
241    * 
242    * @param seq
243    *                DOCUMENT ME!
244    * @param start
245    *                DOCUMENT ME!
246    * @param end
247    *                DOCUMENT ME!
248    * @param x1
249    *                DOCUMENT ME!
250    * @param y1
251    *                DOCUMENT ME!
252    * @param width
253    *                DOCUMENT ME!
254    * @param height
255    *                DOCUMENT ME!
256    */
257   public void drawText(SequenceI seq, int start, int end, int y1)
258   {
259     y1 += av.charHeight - av.charHeight / 5; // height/5 replaces pady
260     int charOffset = 0;
261     char s;
262
263     if (end + 1 >= seq.getLength())
264     {
265       end = seq.getLength() - 1;
266     }
267     graphics.setColor(av.textColour);
268
269     if (monospacedFont && av.showText && allGroups.length == 0
270             && !av.getColourText() && av.thresholdTextColour == 0)
271     {
272       if (av.renderGaps)
273       {
274         graphics.drawString(seq.getSequenceAsString(start, end + 1), 0, y1);
275       }
276       else
277       {
278         char gap = av.getGapCharacter();
279         graphics.drawString(seq.getSequenceAsString(start, end + 1)
280                 .replace(gap, ' '), 0, y1);
281       }
282     }
283     else
284     {
285       boolean getboxColour = false;
286       for (int i = start; i <= end; i++)
287       {
288         graphics.setColor(av.textColour);
289         getboxColour = false;
290         s = seq.getCharAt(i);
291         if (!renderGaps && jalview.util.Comparison.isGap(s))
292         {
293           continue;
294         }
295
296         if (inCurrentSequenceGroup(i))
297         {
298           if (!currentSequenceGroup.getDisplayText())
299           {
300             continue;
301           }
302
303           if (currentSequenceGroup.thresholdTextColour > 0
304                   || currentSequenceGroup.getColourText())
305           {
306             getboxColour = true;
307             getBoxColour(currentSequenceGroup.cs, seq, i);
308
309             if (currentSequenceGroup.getColourText())
310             {
311               graphics.setColor(resBoxColour.darker());
312             }
313
314             if (currentSequenceGroup.thresholdTextColour > 0)
315             {
316               if (resBoxColour.getRed() + resBoxColour.getBlue()
317                       + resBoxColour.getGreen() < currentSequenceGroup.thresholdTextColour)
318               {
319                 graphics.setColor(currentSequenceGroup.textColour2);
320               }
321             }
322           }
323           else
324           {
325             graphics.setColor(currentSequenceGroup.textColour);
326           }
327           if (currentSequenceGroup.getShowunconserved()) // todo optimize
328           {
329             // todo - use sequence group consensus
330             s = getDisplayChar(av.consensus, i, s, '.');                       
331    
332           }
333
334         }
335         else
336         {
337           if (!av.getShowText())
338           {
339             continue;
340           }
341
342           if (av.getColourText())
343           {
344             getboxColour = true;
345             getBoxColour(av.globalColourScheme, seq, i);
346
347             if (av.getShowBoxes())
348             {
349               graphics.setColor(resBoxColour.darker());
350             }
351             else
352             {
353               graphics.setColor(resBoxColour);
354             }
355           }
356
357           if (av.thresholdTextColour > 0)
358           {
359             if (!getboxColour)
360             {
361               getBoxColour(av.globalColourScheme, seq, i);
362             }
363
364             if (resBoxColour.getRed() + resBoxColour.getBlue()
365                     + resBoxColour.getGreen() < av.thresholdTextColour)
366             {
367               graphics.setColor(av.textColour2);
368             }
369           }
370           if (av.showUnconserved)
371           {
372             s = getDisplayChar(av.consensus, i, s, '.');                       
373    
374           }
375
376         }
377
378         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
379         graphics.drawString(String.valueOf(s), charOffset + av.charWidth
380                 * (i - start), y1);
381
382       }
383     }
384   }
385   private char getDisplayChar(AlignmentAnnotation consensus, int position, char s, char c)
386   {
387     char conschar = consensus.annotations[position].displayCharacter.charAt(0);
388     if (conschar!='-' && s==conschar)
389     {
390       s= c;
391     }
392     return s;
393   }
394
395   /**
396    * DOCUMENT ME!
397    * 
398    * @param res
399    *                DOCUMENT ME!
400    * 
401    * @return DOCUMENT ME!
402    */
403   boolean inCurrentSequenceGroup(int res)
404   {
405     if (allGroups == null)
406     {
407       return false;
408     }
409
410     for (int i = 0; i < allGroups.length; i++)
411     {
412       if ((allGroups[i].getStartRes() <= res)
413               && (allGroups[i].getEndRes() >= res))
414       {
415         currentSequenceGroup = allGroups[i];
416
417         return true;
418       }
419     }
420
421     return false;
422   }
423
424   /**
425    * DOCUMENT ME!
426    * 
427    * @param seq
428    *                DOCUMENT ME!
429    * @param start
430    *                DOCUMENT ME!
431    * @param end
432    *                DOCUMENT ME!
433    * @param x1
434    *                DOCUMENT ME!
435    * @param y1
436    *                DOCUMENT ME!
437    * @param width
438    *                DOCUMENT ME!
439    * @param height
440    *                DOCUMENT ME!
441    */
442   public void drawHighlightedText(SequenceI seq, int start, int end,
443           int x1, int y1)
444   {
445     int pady = av.charHeight / 5;
446     int charOffset = 0;
447     graphics.setColor(Color.BLACK);
448     graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
449             av.charHeight);
450     graphics.setColor(Color.white);
451
452     char s = '~';
453
454     // Need to find the sequence position here.
455     if (av.validCharWidth)
456     {
457       for (int i = start; i <= end; i++)
458       {
459         if (i < seq.getLength())
460         {
461           s = seq.getCharAt(i);
462         }
463
464         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
465         graphics
466                 .drawString(String.valueOf(s), charOffset + x1
467                         + (av.charWidth * (i - start)),
468                         (y1 + av.charHeight) - pady);
469       }
470     }
471   }
472
473   public void drawCursor(SequenceI seq, int res, int x1, int y1)
474   {
475     int pady = av.charHeight / 5;
476     int charOffset = 0;
477     graphics.setColor(Color.black);
478     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
479
480     if (av.validCharWidth)
481     {
482       graphics.setColor(Color.white);
483
484       char s = seq.getCharAt(res);
485
486       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
487       graphics.drawString(String.valueOf(s), charOffset + x1,
488               (y1 + av.charHeight) - pady);
489     }
490
491   }
492 }