JAL-2587 fiddling with dimensions
[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.renderer.OverviewRenderer;
25 import jalview.viewmodel.OverviewDimensions;
26
27 import java.awt.AlphaComposite;
28 import java.awt.Color;
29 import java.awt.Dimension;
30 import java.awt.Graphics;
31 import java.awt.Graphics2D;
32 import java.awt.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.image.BufferedImage;
35
36 import javax.swing.JComponent;
37 import javax.swing.Timer;
38
39 public class OverviewCanvas extends JComponent
40 {
41   private static final long RUNNING_TIME = 2000;
42
43   private static final int SPEED = 40;
44
45   private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
46
47   // This is set true if the alignment view changes whilst
48   // the overview is being calculated
49   private volatile boolean restart = false;
50
51   private volatile boolean updaterunning = false;
52
53   private BufferedImage miniMe;
54
55   private BufferedImage lastMiniMe = null;
56
57   private BufferedImage veryLastMiniMe = null;
58
59   // Can set different properties in this seqCanvas than
60   // main visible SeqCanvas
61   private SequenceRenderer sr;
62
63   private jalview.renderer.seqfeatures.FeatureRenderer fr;
64
65   private OverviewDimensions od;
66
67   private OverviewRenderer or = null;
68
69   private AlignViewportI av;
70
71   private float alpha = 0f;
72
73   private long startTime = -1;
74
75   private final Timer timer;
76
77   private ProgressPanel progressPanel;
78
79   public OverviewCanvas(OverviewDimensions overviewDims,
80           AlignViewportI alignvp, ProgressPanel pp)
81   {
82     od = overviewDims;
83     av = alignvp;
84     progressPanel = pp;
85
86     sr = new SequenceRenderer(av);
87     sr.renderGaps = false;
88     sr.forOverview = true;
89     fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
90
91     setSize(od.getWidth(), od.getHeight());
92
93     timer = new Timer(SPEED, new ActionListener()
94     {
95
96       @Override
97       public void actionPerformed(ActionEvent e)
98       {
99         if (startTime < 0)
100         {
101           startTime = System.currentTimeMillis();
102         }
103         else
104         {
105
106           long time = System.currentTimeMillis();
107           long duration = time - startTime;
108           if (duration >= RUNNING_TIME)
109           {
110             startTime = -1;
111             ((Timer) e.getSource()).stop();
112             alpha = 0f;
113           }
114           else
115           {
116             alpha = 1f - ((float) duration / (float) RUNNING_TIME);
117           }
118           repaint();
119         }
120       }
121     });
122   }
123
124   /**
125    * Update the overview dimensions object used by the canvas (e.g. if we change
126    * from showing hidden columns to hiding them or vice versa)
127    * 
128    * @param overviewDims
129    */
130   public void resetOviewDims(OverviewDimensions overviewDims)
131   {
132     od = overviewDims;
133   }
134
135   /**
136    * Signals to drawing code that the associated alignment viewport has changed
137    * and a redraw will be required
138    */
139   public boolean restartDraw()
140   {
141     synchronized (this)
142     {
143       if (updaterunning)
144       {
145         restart = true;
146         if (or != null)
147         {
148           or.setRedraw(true);
149         }
150       }
151       else
152       {
153         updaterunning = true;
154       }
155       return restart;
156     }
157   }
158
159   /**
160    * Draw the overview sequences
161    * 
162    * @param showSequenceFeatures
163    *          true if sequence features are to be shown
164    * @param showAnnotation
165    *          true if the annotation is to be shown
166    * @param transferRenderer
167    *          the renderer to transfer feature colouring from
168    */
169   public void draw(boolean showSequenceFeatures, boolean showAnnotation,
170           FeatureRenderer transferRenderer)
171   {
172     System.out.println(this.getHeight());
173     //od.setHeight(this.getHeight());
174     //od.setWidth(this.getWidth());
175     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
176
177     miniMe = null;
178     veryLastMiniMe = lastMiniMe;
179
180     if (showSequenceFeatures)
181     {
182       fr.transferSettings(transferRenderer);
183     }
184
185     or = new OverviewRenderer(sr, fr, od);
186     or.addPropertyChangeListener(progressPanel);
187     miniMe = or.draw(od.getRows(av.getAlignment()),
188             od.getColumns(av.getAlignment()));
189
190     Graphics mg = miniMe.getGraphics();
191
192     if (showAnnotation)
193     {
194       mg.translate(0, od.getSequencesHeight());
195       or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
196               av.getCharWidth(), od.getGraphHeight(),
197               od.getColumns(av.getAlignment()));
198       mg.translate(0, -od.getSequencesHeight());
199     }
200     System.gc();
201
202     or.removePropertyChangeListener(progressPanel);
203     if (restart)
204     {
205       restart = false;
206       draw(showSequenceFeatures, showAnnotation, transferRenderer);
207     }
208     else
209     {
210       updaterunning = false;
211       lastMiniMe = miniMe;
212       alpha = 1f;
213       timer.start();
214     }
215   }
216
217   @Override
218   public void paintComponent(Graphics g)
219   {
220
221     if (restart)
222     {
223       if (lastMiniMe == null)
224       {
225         g.setColor(Color.white);
226         g.fillRect(0, 0, getWidth(), getHeight());
227       }
228       else
229       {
230         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
231       }
232       g.setColor(TRANS_GREY);
233       g.fillRect(0, 0, getWidth(), getHeight());
234     }
235     else if (lastMiniMe != null)
236     {
237       if ((getWidth() > 0) && (getHeight() > 0)
238               && ((getWidth() != od.getWidth())
239                       || (getHeight() != od.getHeight())))
240       {
241         // scale the alignment and annotation separately *** if there is
242         // annotation ***
243         if (od.getGraphHeight() > 0)
244         {
245           BufferedImage topImage = lastMiniMe.getSubimage(0, 0,
246                   od.getWidth(), od.getSequencesHeight());
247           BufferedImage bottomImage = lastMiniMe.getSubimage(0,
248                   od.getSequencesHeight(), od.getWidth(),
249                   od.getGraphHeight());
250
251           // must be done at this point as we rely on using old width/height
252           // above, and new width/height below
253           od.setWidth(getWidth());
254           od.setHeight(getHeight());
255
256           // stick the images back together so lastMiniMe is consistent in the
257           // event of a repaint - BUT probably not thread safe
258           lastMiniMe = new BufferedImage(od.getWidth(), od.getHeight(),
259                   BufferedImage.TYPE_INT_RGB);
260           Graphics lg = lastMiniMe.getGraphics();
261           lg.drawImage(topImage, 0, 0, od.getWidth(),
262                   od.getSequencesHeight(), null);
263           lg.drawImage(bottomImage, 0, od.getSequencesHeight(),
264                   od.getWidth(), od.getGraphHeight(), this);
265           lg.dispose();
266         }
267         else
268         {
269           od.setWidth(getWidth());
270           od.setHeight(getHeight());
271         }
272
273         // scale lastMiniMe to the new size
274         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
275
276         // make sure the box is in the right place
277         od.setBoxPosition(av.getAlignment().getHiddenSequences(),
278                 av.getAlignment().getHiddenColumns());
279       }
280       else
281       {
282         if (alpha != 0) // this is a timer triggered dissolve
283         {
284           Graphics2D g2d = (Graphics2D) g.create();
285           
286           // draw the original image
287           g2d.drawImage(veryLastMiniMe, 0, 0, getWidth(), getHeight(),
288                   this);
289
290           // draw the new image on top with varying degrees of transparency
291           g2d.setComposite(AlphaComposite.SrcOver.derive(1f - alpha));
292           g2d.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
293
294           g2d.dispose();
295         }
296         /*       else if (lastMiniMe != miniMe)
297         {
298           g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
299           g.setColor(TRANS_GREY);
300           g.fillRect(0, 0, getWidth(), getHeight());
301         }*/
302         else
303         {
304           // fall back to normal behaviour
305           g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
306         }
307
308       }
309
310     }
311
312     // draw the box
313     g.setColor(Color.red);
314     od.drawBox(g);
315   }
316 }