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