JAL-2388 Corrected overview panel behaviour, updated tests
[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.viewmodel.OverviewDimensions;
24
25 import java.awt.Color;
26 import java.awt.Dimension;
27 import java.awt.Frame;
28 import java.awt.Graphics;
29 import java.awt.Image;
30 import java.awt.Panel;
31 import java.awt.event.ComponentAdapter;
32 import java.awt.event.ComponentEvent;
33 import java.awt.event.MouseEvent;
34 import java.awt.event.MouseListener;
35 import java.awt.event.MouseMotionListener;
36
37 public class OverviewPanel extends Panel implements Runnable,
38         MouseMotionListener, MouseListener
39 {
40   private OverviewDimensions od;
41
42   private Image miniMe;
43
44   private Image offscreen;
45
46   private AlignViewport av;
47
48   private AlignmentPanel ap;
49
50   private boolean resizing = false;
51
52   // This is set true if the user resizes whilst
53   // the overview is being calculated
54   private boolean resizeAgain = false;
55
56   // Can set different properties in this seqCanvas than
57   // main visible SeqCanvas
58   private SequenceRenderer sr;
59
60   private FeatureRenderer fr;
61
62   private Frame nullFrame;
63
64   public OverviewPanel(AlignmentPanel ap)
65   {
66     this.av = ap.av;
67     this.ap = ap;
68     setLayout(null);
69     nullFrame = new Frame();
70     nullFrame.addNotify();
71
72     sr = new SequenceRenderer(av);
73     sr.graphics = nullFrame.getGraphics();
74     sr.renderGaps = false;
75     sr.forOverview = true;
76     fr = new FeatureRenderer(av);
77
78     boolean showAnnotation = false;
79     // TODO: in applet this was getSequenceConsensusHash()
80     // check if it makes any functional difference: hconsensus or conservation
81     if (av.getAlignmentConservationAnnotation() == null)
82     {
83       showAnnotation = true;
84     }
85
86     od = new OverviewDimensions(av, showAnnotation);
87
88     setSize(new Dimension(od.getWidth(), od.getHeight()));
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());
152     ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
153     ap.paintAlignment(false);
154   }
155
156   /**
157    * DOCUMENT ME!
158    */
159   public void updateOverviewImage()
160   {
161     if (resizing)
162     {
163       resizeAgain = true;
164       return;
165     }
166
167     if (av.isShowSequenceFeatures())
168     {
169       fr.transferSettings(ap.seqPanel.seqCanvas.fr);
170     }
171
172     resizing = true;
173
174     if ((getWidth() > 0) && (getHeight() > 0))
175     {
176       od.setWidth(getWidth()); // width = getWidth();
177       od.setHeight(getHeight()); // sequencesHeight = getHeight() - graphHeight;
178     }
179     setSize(new Dimension(od.getWidth(), od.getHeight()));
180
181     Thread thread = new Thread(this);
182     thread.start();
183     repaint();
184   }
185
186   @Override
187   public void run()
188   {
189     miniMe = null;
190
191     if (av.isShowSequenceFeatures())
192     {
193       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
194     }
195
196     if (getSize().width > 0 && getSize().height > 0)
197     {
198       od.setWidth(getSize().width);
199       od.setHeight(getSize().height - od.getGraphHeight());
200     }
201
202     setSize(new Dimension(od.getWidth(), od.getHeight()));
203
204     miniMe = nullFrame.createImage(od.getWidth(), od.getHeight());
205     offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
206
207     Graphics mg = miniMe.getGraphics();
208
209     // od.updateScales();
210
211     int alwidth = av.getAlignment().getWidth();
212     int alheight = av.getAlignment().getAbsoluteHeight();
213     float sampleCol = alwidth / (float) od.getWidth();
214     float sampleRow = alheight / (float) od.getSequencesHeight();
215
216     buildImage(sampleRow, sampleCol, mg);
217
218     if (av.getAlignmentConservationAnnotation() != null)
219     {
220       for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
221       {
222         mg.translate(col, od.getSequencesHeight());
223         ap.annotationPanel.renderer.drawGraph(mg,
224                 av.getAlignmentConservationAnnotation(),
225                 av.getAlignmentConservationAnnotation().annotations,
226                 (int) (sampleCol) + 1, od.getGraphHeight(),
227                 (int) (col * sampleCol), (int) (col * sampleCol) + 1);
228         mg.translate(-col, -od.getSequencesHeight());
229       }
230     }
231     System.gc();
232
233     resizing = false;
234
235     setBoxPosition();
236
237     if (resizeAgain)
238     {
239       resizeAgain = false;
240       updateOverviewImage();
241     }
242   }
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     jalview.datamodel.SequenceI seq = null;
255
256     final boolean hasHiddenCols = av.hasHiddenColumns();
257     boolean hiddenRow = false;
258
259     for (int row = 0; row <= od.getSequencesHeight() && !resizeAgain; row++)
260     {
261       if ((int) (row * sampleRow) == lastrow)
262       {
263         sameRow++;
264       }
265       else
266       {
267         // get the sequence which would be at alignment index 'lastrow' if no
268         // columns were hidden, and determine whether it is hidden or not
269         hiddenRow = av.getAlignment().isHidden(lastrow);
270         seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
271
272         for (int col = 0; col < od.getWidth(); col++)
273         {
274           if ((int) (col * sampleCol) == lastcol
275                   && (int) (row * sampleRow) == lastrow)
276           {
277             sameCol++;
278           }
279           else
280           {
281             lastcol = (int) (col * sampleCol);
282
283             color = getColumnColourFromSequence(seq, hiddenRow,
284                     hasHiddenCols, lastcol);
285
286             mg.setColor(color);
287             if (sameCol == 1 && sameRow == 1)
288             {
289               mg.drawLine(xstart, ystart, xstart, ystart);
290             }
291             else
292             {
293               mg.fillRect(xstart, ystart, sameCol, sameRow);
294             }
295
296             xstart = col;
297             sameCol = 1;
298           }
299         }
300         lastrow = (int) (row * sampleRow);
301         ystart = row;
302         sameRow = 1;
303       }
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)
314   {
315     Color color;
316     if (seq.getLength() > lastcol)
317     {
318       color = sr.getResidueBoxColour(seq, lastcol);
319
320       if (av.isShowSequenceFeatures())
321       {
322         color = fr.findFeatureColour(color, seq, lastcol);
323       }
324     }
325     else
326     {
327       color = Color.white; // White
328     }
329
330     if (hiddenRow
331             || (hasHiddenCols && !av.getColumnSelection()
332                     .isVisible(lastcol)))
333     {
334       color = color.darker().darker();
335     }
336     return color;
337   }
338
339   /**
340    * Update the overview panel box when the associated alignment panel is
341    * changed
342    * 
343    */
344   public void setBoxPosition()
345   {
346     od.setBoxPosition();
347     repaint();
348   }
349
350   @Override
351   public void update(Graphics g)
352   {
353     paint(g);
354   }
355
356   @Override
357   public void paint(Graphics g)
358   {
359     Graphics og = offscreen.getGraphics();
360     if (miniMe != null)
361     {
362       og.drawImage(miniMe, 0, 0, this);
363       og.setColor(Color.red);
364       od.drawBox(og);
365       g.drawImage(offscreen, 0, 0, this);
366     }
367   }
368
369 }