JAL-2491 Tidies to Overview panel calls in light of new code
[jalview.git] / src / jalview / appletgui / OverviewPanel.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.appletgui;
22
23 import jalview.datamodel.SequenceI;
24 import jalview.renderer.seqfeatures.FeatureColourFinder;
25 import jalview.viewmodel.OverviewDimensions;
26 import jalview.viewmodel.ViewportListenerI;
27
28 import java.awt.Color;
29 import java.awt.Dimension;
30 import java.awt.Frame;
31 import java.awt.Graphics;
32 import java.awt.Image;
33 import java.awt.Panel;
34 import java.awt.event.ComponentAdapter;
35 import java.awt.event.ComponentEvent;
36 import java.awt.event.MouseEvent;
37 import java.awt.event.MouseListener;
38 import java.awt.event.MouseMotionListener;
39 import java.beans.PropertyChangeEvent;
40
41 public class OverviewPanel extends Panel implements Runnable,
42         MouseMotionListener, MouseListener, ViewportListenerI
43 {
44   private OverviewDimensions od;
45
46   private Image miniMe;
47
48   private Image offscreen;
49
50   private AlignViewport av;
51
52   private AlignmentPanel ap;
53
54   private boolean resizing = false;
55
56   // This is set true if the user resizes whilst
57   // the overview is being calculated
58   private boolean resizeAgain = false;
59
60   // Can set different properties in this seqCanvas than
61   // main visible SeqCanvas
62   private SequenceRenderer sr;
63
64   private FeatureRenderer fr;
65
66   private Frame nullFrame;
67
68   public OverviewPanel(AlignmentPanel alPanel)
69   {
70     this.av = alPanel.av;
71     this.ap = alPanel;
72     setLayout(null);
73     nullFrame = new Frame();
74     nullFrame.addNotify();
75
76     sr = new SequenceRenderer(av);
77     sr.graphics = nullFrame.getGraphics();
78     sr.renderGaps = false;
79     sr.forOverview = true;
80     fr = new FeatureRenderer(av);
81
82     od = new OverviewDimensions(av.getRanges(),
83             (av.isShowAnnotation() && av.getSequenceConsensusHash() != null));
84
85     setSize(new Dimension(od.getWidth(), od.getHeight()));
86
87     av.getRanges().addPropertyChangeListener(this);
88
89     addComponentListener(new ComponentAdapter()
90     {
91
92       @Override
93       public void componentResized(ComponentEvent evt)
94       {
95         if ((getWidth() != od.getWidth())
96                 || (getHeight() != (od.getHeight())))
97         {
98           updateOverviewImage();
99         }
100       }
101     });
102
103     addMouseMotionListener(this);
104
105     addMouseListener(this);
106
107     updateOverviewImage();
108
109   }
110
111   @Override
112   public void mouseEntered(MouseEvent evt)
113   {
114   }
115
116   @Override
117   public void mouseExited(MouseEvent evt)
118   {
119   }
120
121   @Override
122   public void mouseClicked(MouseEvent evt)
123   {
124   }
125
126   @Override
127   public void mouseMoved(MouseEvent evt)
128   {
129   }
130
131   @Override
132   public void mousePressed(MouseEvent evt)
133   {
134     mouseAction(evt);
135   }
136
137   @Override
138   public void mouseReleased(MouseEvent evt)
139   {
140     mouseAction(evt);
141   }
142
143   @Override
144   public void mouseDragged(MouseEvent evt)
145   {
146     mouseAction(evt);
147   }
148
149   private void mouseAction(MouseEvent evt)
150   {
151     od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment()
152             .getHiddenSequences(), av.getColumnSelection(), av
153             .getRanges());
154     ap.paintAlignment(false);
155   }
156
157   /**
158    * Updates the overview image when the related alignment panel is updated
159    */
160   public void updateOverviewImage()
161   {
162     if (resizing)
163     {
164       resizeAgain = true;
165       return;
166     }
167
168     if (av.isShowSequenceFeatures())
169     {
170       fr.transferSettings(ap.seqPanel.seqCanvas.fr);
171     }
172
173     resizing = true;
174
175     if ((getSize().width > 0) && (getSize().height > 0))
176     {
177       od.setWidth(getSize().width);
178       od.setHeight(getSize().height);
179     }
180     setSize(new Dimension(od.getWidth(), od.getHeight()));
181
182     Thread thread = new Thread(this);
183     thread.start();
184     repaint();
185   }
186
187   @Override
188   public void run()
189   {
190     miniMe = null;
191
192     if (av.isShowSequenceFeatures())
193     {
194       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
195     }
196
197     if (getSize().width > 0 && getSize().height > 0)
198     {
199       od.setWidth(getSize().width);
200       od.setHeight(getSize().height);
201     }
202
203     setSize(new Dimension(od.getWidth(), od.getHeight()));
204
205     miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
206     offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
207
208     Graphics mg = miniMe.getGraphics();
209
210     int alwidth = av.getAlignment().getWidth();
211     int alheight = av.getAlignment().getAbsoluteHeight();
212     float sampleCol = alwidth / (float) od.getWidth();
213     float sampleRow = alheight / (float) od.getSequencesHeight();
214
215     buildImage(sampleRow, sampleCol, mg);
216
217     // check for conservation annotation to make sure overview works for DNA too
218     if (av.isShowAnnotation()
219             && (av.getAlignmentConservationAnnotation() != null))
220     {
221       for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
222       {
223         mg.translate(col, od.getSequencesHeight());
224         ap.annotationPanel.renderer.drawGraph(mg,
225                 av.getAlignmentConservationAnnotation(),
226                 av.getAlignmentConservationAnnotation().annotations,
227                 (int) (sampleCol) + 1, od.getGraphHeight(),
228                 (int) (col * sampleCol), (int) (col * sampleCol) + 1);
229         mg.translate(-col, -od.getSequencesHeight());
230       }
231     }
232     System.gc();
233
234     resizing = false;
235
236     setBoxPosition();
237
238     if (resizeAgain)
239     {
240       resizeAgain = false;
241       updateOverviewImage();
242     }
243   }
244
245   /*
246    * Build the overview panel image
247    */
248   private void buildImage(float sampleRow, float sampleCol, Graphics mg)
249   {
250     int lastcol = 0;
251     int lastrow = 0;
252     int xstart = 0;
253     int ystart = 0;
254     Color color = Color.yellow;
255     int sameRow = 0;
256     int sameCol = 0;
257
258     SequenceI seq = null;
259     FeatureColourFinder finder = new FeatureColourFinder(fr);
260
261     final boolean hasHiddenCols = av.hasHiddenColumns();
262     boolean hiddenRow = false;
263
264     for (int row = 0; row <= od.getSequencesHeight() && !resizeAgain; row++)
265     {
266       if ((int) (row * sampleRow) == lastrow)
267       {
268         sameRow++;
269       }
270       else
271       {
272         // get the sequence which would be at alignment index 'lastrow' if no
273         // columns were hidden, and determine whether it is hidden or not
274         hiddenRow = av.getAlignment().isHidden(lastrow);
275         seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
276
277         for (int col = 0; col < od.getWidth(); col++)
278         {
279           if ((int) (col * sampleCol) == lastcol
280                   && (int) (row * sampleRow) == lastrow)
281           {
282             sameCol++;
283           }
284           else
285           {
286             lastcol = (int) (col * sampleCol);
287
288             color = getColumnColourFromSequence(seq, hiddenRow,
289                     hasHiddenCols, lastcol, finder);
290
291             mg.setColor(color);
292             if (sameCol == 1 && sameRow == 1)
293             {
294               mg.drawLine(xstart, ystart, xstart, ystart);
295             }
296             else
297             {
298               mg.fillRect(xstart, ystart, sameCol, sameRow);
299             }
300
301             xstart = col;
302             sameCol = 1;
303           }
304         }
305         lastrow = (int) (row * sampleRow);
306         ystart = row;
307         sameRow = 1;
308       }
309     }
310   }
311
312   /*
313    * Find the colour of a sequence at a specified column position
314    */
315   private Color getColumnColourFromSequence(
316           jalview.datamodel.SequenceI seq, boolean hiddenRow,
317           boolean hasHiddenCols, int lastcol, FeatureColourFinder finder)
318   {
319     Color color = Color.white;
320     if (seq.getLength() > lastcol)
321     {
322       color = sr.getResidueColour(seq, lastcol, finder);
323     }
324
325     if (hiddenRow
326             || (hasHiddenCols && !av.getColumnSelection()
327                     .isVisible(lastcol)))
328     {
329       color = color.darker().darker();
330     }
331     return color;
332   }
333
334   /**
335    * Update the overview panel box when the associated alignment panel is
336    * changed
337    * 
338    */
339   private void setBoxPosition()
340   {
341     od.setBoxPosition(av.getAlignment()
342             .getHiddenSequences(), av.getColumnSelection(), av.getRanges());
343     repaint();
344   }
345
346   @Override
347   public void update(Graphics g)
348   {
349     paint(g);
350   }
351
352   @Override
353   public void paint(Graphics g)
354   {
355     Graphics og = offscreen.getGraphics();
356     if (miniMe != null)
357     {
358       og.drawImage(miniMe, 0, 0, this);
359       og.setColor(Color.red);
360       od.drawBox(og);
361       g.drawImage(offscreen, 0, 0, this);
362     }
363   }
364
365   @Override
366   public void propertyChange(PropertyChangeEvent evt)
367   {
368     setBoxPosition();
369   }
370
371 }