merge from 2_4_Release branch
[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
328         }
329         else
330         {
331           if (!av.getShowText())
332           {
333             continue;
334           }
335
336           if (av.getColourText())
337           {
338             getboxColour = true;
339             getBoxColour(av.globalColourScheme, seq, i);
340
341             if (av.getShowBoxes())
342             {
343               graphics.setColor(resBoxColour.darker());
344             }
345             else
346             {
347               graphics.setColor(resBoxColour);
348             }
349           }
350
351           if (av.thresholdTextColour > 0)
352           {
353             if (!getboxColour)
354             {
355               getBoxColour(av.globalColourScheme, seq, i);
356             }
357
358             if (resBoxColour.getRed() + resBoxColour.getBlue()
359                     + resBoxColour.getGreen() < av.thresholdTextColour)
360             {
361               graphics.setColor(av.textColour2);
362             }
363           }
364
365         }
366
367         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
368         graphics.drawString(String.valueOf(s), charOffset + av.charWidth
369                 * (i - start), y1);
370
371       }
372     }
373   }
374
375   /**
376    * DOCUMENT ME!
377    * 
378    * @param res
379    *                DOCUMENT ME!
380    * 
381    * @return DOCUMENT ME!
382    */
383   boolean inCurrentSequenceGroup(int res)
384   {
385     if (allGroups == null)
386     {
387       return false;
388     }
389
390     for (int i = 0; i < allGroups.length; i++)
391     {
392       if ((allGroups[i].getStartRes() <= res)
393               && (allGroups[i].getEndRes() >= res))
394       {
395         currentSequenceGroup = allGroups[i];
396
397         return true;
398       }
399     }
400
401     return false;
402   }
403
404   /**
405    * DOCUMENT ME!
406    * 
407    * @param seq
408    *                DOCUMENT ME!
409    * @param start
410    *                DOCUMENT ME!
411    * @param end
412    *                DOCUMENT ME!
413    * @param x1
414    *                DOCUMENT ME!
415    * @param y1
416    *                DOCUMENT ME!
417    * @param width
418    *                DOCUMENT ME!
419    * @param height
420    *                DOCUMENT ME!
421    */
422   public void drawHighlightedText(SequenceI seq, int start, int end,
423           int x1, int y1)
424   {
425     int pady = av.charHeight / 5;
426     int charOffset = 0;
427     graphics.setColor(Color.BLACK);
428     graphics.fillRect(x1, y1, av.charWidth * (end - start + 1),
429             av.charHeight);
430     graphics.setColor(Color.white);
431
432     char s = '~';
433
434     // Need to find the sequence position here.
435     if (av.validCharWidth)
436     {
437       for (int i = start; i <= end; i++)
438       {
439         if (i < seq.getLength())
440         {
441           s = seq.getCharAt(i);
442         }
443
444         charOffset = (av.charWidth - fm.charWidth(s)) / 2;
445         graphics
446                 .drawString(String.valueOf(s), charOffset + x1
447                         + (av.charWidth * (i - start)),
448                         (y1 + av.charHeight) - pady);
449       }
450     }
451   }
452
453   public void drawCursor(SequenceI seq, int res, int x1, int y1)
454   {
455     int pady = av.charHeight / 5;
456     int charOffset = 0;
457     graphics.setColor(Color.black);
458     graphics.fillRect(x1, y1, av.charWidth, av.charHeight);
459
460     if (av.validCharWidth)
461     {
462       graphics.setColor(Color.white);
463
464       char s = seq.getCharAt(res);
465
466       charOffset = (av.charWidth - fm.charWidth(s)) / 2;
467       graphics.drawString(String.valueOf(s), charOffset + x1,
468               (y1 + av.charHeight) - pady);
469     }
470
471   }
472 }