JAL-3148 SequenceRenderer, ResidueColourFinder overloads and
[jalview.git] / src / jalview / gui / OverviewCanvas.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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.api.AlignViewportI;
24 import jalview.bin.Cache;
25 import jalview.renderer.OverviewRenderer;
26 import jalview.renderer.OverviewResColourFinder;
27 import jalview.viewmodel.OverviewDimensions;
28
29 import java.awt.Color;
30 import java.awt.Dimension;
31 import java.awt.Graphics;
32 import java.awt.image.BufferedImage;
33
34 import javax.swing.JComponent;
35
36 public class OverviewCanvas extends JComponent
37 {
38   private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
39
40   // This is set true if the alignment view changes whilst
41   // the overview is being calculated
42   private volatile boolean restart = false;
43
44   private volatile boolean updaterunning = false;
45
46   private boolean dispose = false;
47
48   private BufferedImage miniMe;
49
50   private BufferedImage lastMiniMe = null;
51
52   private jalview.renderer.seqfeatures.FeatureRenderer fr;
53
54   private OverviewDimensions od;
55
56   private OverviewRenderer or = null;
57
58   private AlignViewportI av;
59
60   private OverviewResColourFinder cf;
61
62   private ProgressPanel progressPanel;
63
64   public OverviewCanvas(OverviewDimensions overviewDims,
65           AlignViewportI alignvp, ProgressPanel pp)
66   {
67     od = overviewDims;
68     av = alignvp;
69     progressPanel = pp;
70
71     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
72
73     boolean useLegacy = Cache.getDefault(Preferences.USE_LEGACY_GAP, false);
74     Color gapCol = Cache.getDefaultColour(Preferences.GAP_COLOUR,
75             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP);
76     Color hiddenCol = Cache.getDefaultColour(Preferences.HIDDEN_COLOUR,
77             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN);
78     cf = new OverviewResColourFinder(useLegacy, gapCol, hiddenCol);
79
80     setSize(od.getWidth(), od.getHeight());
81   }
82
83   /**
84    * Update the overview dimensions object used by the canvas (e.g. if we change
85    * from showing hidden columns to hiding them or vice versa)
86    * 
87    * @param overviewDims
88    */
89   public void resetOviewDims(OverviewDimensions overviewDims)
90   {
91     od = overviewDims;
92   }
93
94   /**
95    * Signals to drawing code that the associated alignment viewport has changed
96    * and a redraw will be required
97    */
98   public boolean restartDraw()
99   {
100     synchronized (this)
101     {
102       if (updaterunning)
103       {
104         restart = true;
105         if (or != null)
106         {
107           or.setRedraw(true);
108         }
109       }
110       else
111       {
112         updaterunning = true;
113       }
114       return restart;
115     }
116   }
117
118   /**
119    * Draw the overview sequences
120    * 
121    * @param showSequenceFeatures
122    *          true if sequence features are to be shown
123    * @param showAnnotation
124    *          true if the annotation is to be shown
125    * @param transferRenderer
126    *          the renderer to transfer feature colouring from
127    */
128   public void draw(boolean showSequenceFeatures, boolean showAnnotation,
129           FeatureRenderer transferRenderer)
130   {
131     miniMe = null;
132
133     if (showSequenceFeatures)
134     {
135       fr.transferSettings(transferRenderer);
136     }
137
138     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
139
140     or = new OverviewRenderer(fr, od, av.getAlignment(),
141             av.getResidueShading(), cf);
142
143     or.addPropertyChangeListener(progressPanel);
144
145     miniMe = or.draw(od.getRows(av.getAlignment()),
146             od.getColumns(av.getAlignment()));
147
148     Graphics mg = miniMe.getGraphics();
149
150     if (showAnnotation)
151     {
152       mg.translate(0, od.getSequencesHeight());
153       or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
154               od.getGraphHeight(), od.getColumns(av.getAlignment()));
155       mg.translate(0, -od.getSequencesHeight());
156     }
157
158     or.removePropertyChangeListener(progressPanel);
159     or = null;
160     if (restart)
161     {
162       restart = false;
163       if (!dispose)
164       {
165         draw(showSequenceFeatures, showAnnotation, transferRenderer);
166       }
167     }
168     else
169     {
170       updaterunning = false;
171       lastMiniMe = miniMe;
172     }
173   }
174
175   @Override
176   public void paintComponent(Graphics g)
177   {
178     super.paintComponent(g);
179
180     if (restart)
181     {
182       if (lastMiniMe == null)
183       {
184         g.setColor(Color.white);
185         g.fillRect(0, 0, getWidth(), getHeight());
186       }
187       else
188       {
189         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
190       }
191       g.setColor(TRANS_GREY);
192       g.fillRect(0, 0, getWidth(), getHeight());
193     }
194     else if (lastMiniMe != null)
195     {
196       // is this a resize?
197       if ((getWidth() > 0) && (getHeight() > 0)
198               && ((getWidth() != od.getWidth())
199                       || (getHeight() != od.getHeight())))
200       {
201         // if there is annotation, scale the alignment and annotation
202         // separately
203         if (od.getGraphHeight() > 0)
204         {
205           BufferedImage topImage = lastMiniMe.getSubimage(0, 0,
206                   od.getWidth(), od.getSequencesHeight());
207           BufferedImage bottomImage = lastMiniMe.getSubimage(0,
208                   od.getSequencesHeight(), od.getWidth(),
209                   od.getGraphHeight());
210
211           // must be done at this point as we rely on using old width/height
212           // above, and new width/height below
213           od.setWidth(getWidth());
214           od.setHeight(getHeight());
215
216           // stick the images back together so lastMiniMe is consistent in the
217           // event of a repaint - BUT probably not thread safe
218           lastMiniMe = new BufferedImage(od.getWidth(), od.getHeight(),
219                   BufferedImage.TYPE_INT_RGB);
220           Graphics lg = lastMiniMe.getGraphics();
221           lg.drawImage(topImage, 0, 0, od.getWidth(),
222                   od.getSequencesHeight(), null);
223           lg.drawImage(bottomImage, 0, od.getSequencesHeight(),
224                   od.getWidth(), od.getGraphHeight(), this);
225           lg.dispose();
226         }
227         else
228         {
229           od.setWidth(getWidth());
230           od.setHeight(getHeight());
231         }
232
233         // make sure the box is in the right place
234         od.setBoxPosition(av.getAlignment().getHiddenSequences(),
235                 av.getAlignment().getHiddenColumns());
236       }
237       // fall back to normal behaviour
238       g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
239     }
240     else
241     {
242       g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
243     }
244     
245     // draw the box
246     g.setColor(Color.red);
247     od.drawBox(g);
248   }
249
250
251   public void dispose()
252   {
253     dispose = true;
254     synchronized (this)
255     {
256       restart = true;
257       if (or != null)
258       {
259         or.setRedraw(true);
260       }
261     }
262   }
263 }