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