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