(JAL-969) refactored alignmentViewport base class to own package and pulled up most...
[jalview.git] / src / jalview / gui / OverviewPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import jalview.renderer.AnnotationRenderer;
21
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.awt.image.*;
25 import javax.swing.*;
26
27 /**
28  * DOCUMENT ME!
29  * 
30  * @author $author$
31  * @version $Revision$
32  */
33 public class OverviewPanel extends JPanel implements Runnable
34 {
35   BufferedImage miniMe;
36
37   AlignViewport av;
38
39   AlignmentPanel ap;
40
41   final AnnotationRenderer renderer = new AnnotationRenderer();
42   float scalew = 1f;
43
44   float scaleh = 1f;
45
46   int width;
47
48   int sequencesHeight;
49
50   int graphHeight = 20;
51
52   int boxX = -1;
53
54   int boxY = -1;
55
56   int boxWidth = -1;
57
58   int boxHeight = -1;
59
60   boolean resizing = false;
61
62   // Can set different properties in this seqCanvas than
63   // main visible SeqCanvas
64   SequenceRenderer sr;
65
66   FeatureRenderer fr;
67
68   /**
69    * Creates a new OverviewPanel object.
70    * 
71    * @param ap
72    *          DOCUMENT ME!
73    */
74   public OverviewPanel(AlignmentPanel ap)
75   {
76     this.av = ap.av;
77     this.ap = ap;
78     setLayout(null);
79
80     sr = new SequenceRenderer(av);
81     sr.renderGaps = false;
82     sr.forOverview = true;
83     fr = new FeatureRenderer(ap);
84
85     // scale the initial size of overviewpanel to shape of alignment
86     float initialScale = (float) av.getAlignment().getWidth()
87             / (float) av.getAlignment().getHeight();
88
89     if (av.getAlignmentConservationAnnotation()== null)
90     {
91       graphHeight = 0;
92     }
93
94     if (av.getAlignment().getWidth() > av.getAlignment().getHeight())
95     {
96       // wider
97       width = 400;
98       sequencesHeight = (int) (400f / initialScale);
99       if (sequencesHeight < 40)
100       {
101         sequencesHeight = 40;
102       }
103     }
104     else
105     {
106       // taller
107       width = (int) (400f * initialScale);
108       sequencesHeight = 300;
109
110       if (width < 120)
111       {
112         width = 120;
113       }
114     }
115
116     addComponentListener(new ComponentAdapter()
117     {
118       public void componentResized(ComponentEvent evt)
119       {
120         if ((getWidth() != width)
121                 || (getHeight() != (sequencesHeight + graphHeight)))
122         {
123           updateOverviewImage();
124         }
125       }
126     });
127
128     addMouseMotionListener(new MouseMotionAdapter()
129     {
130       public void mouseDragged(MouseEvent evt)
131       {
132         if (!av.wrapAlignment)
133         {
134           // TODO: feature: jv2.5 detect shift drag and update selection from
135           // it.
136           boxX = evt.getX();
137           boxY = evt.getY();
138           checkValid();
139         }
140       }
141     });
142
143     addMouseListener(new MouseAdapter()
144     {
145       public void mousePressed(MouseEvent evt)
146       {
147         if (!av.wrapAlignment)
148         {
149           boxX = evt.getX();
150           boxY = evt.getY();
151           checkValid();
152         }
153       }
154     });
155
156     updateOverviewImage();
157   }
158
159   /**
160    * DOCUMENT ME!
161    */
162   void checkValid()
163   {
164     if (boxY < 0)
165     {
166       boxY = 0;
167     }
168
169     if (boxY > (sequencesHeight - boxHeight))
170     {
171       boxY = sequencesHeight - boxHeight + 1;
172     }
173
174     if (boxX < 0)
175     {
176       boxX = 0;
177     }
178
179     if (boxX > (width - boxWidth))
180     {
181       if (av.hasHiddenColumns())
182       {
183         // Try smallest possible box
184         boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
185       }
186       boxX = width - boxWidth;
187     }
188
189     int col = (int) (boxX / scalew / av.getCharWidth());
190     int row = (int) (boxY / scaleh / av.getCharHeight());
191
192     if (av.hasHiddenColumns())
193     {
194       if (!av.getColumnSelection().isVisible(col))
195       {
196         return;
197       }
198
199       col = av.getColumnSelection().findColumnPosition(col);
200     }
201
202     if (av.hasHiddenRows())
203     {
204       row = av.getAlignment().getHiddenSequences().findIndexWithoutHiddenSeqs(
205               row);
206     }
207
208     ap.setScrollValues(col, row);
209
210   }
211
212   /**
213    * DOCUMENT ME!
214    */
215   public void updateOverviewImage()
216   {
217     if (resizing)
218     {
219       resizeAgain = true;
220       return;
221     }
222
223     resizing = true;
224
225     if ((getWidth() > 0) && (getHeight() > 0))
226     {
227       width = getWidth();
228       sequencesHeight = getHeight() - graphHeight;
229     }
230
231     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
232
233     Thread thread = new Thread(this);
234     thread.start();
235     repaint();
236   }
237
238   // This is set true if the user resizes whilst
239   // the overview is being calculated
240   boolean resizeAgain = false;
241
242   /**
243    * DOCUMENT ME!
244    */
245   public void run()
246   {
247     miniMe = null;
248
249     if (av.showSequenceFeatures)
250     {
251       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
252     }
253
254     int alwidth = av.getAlignment().getWidth();
255     int alheight = av.getAlignment().getHeight()
256             + av.getAlignment().getHiddenSequences().getSize();
257
258     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
259
260     int fullsizeWidth = alwidth * av.getCharWidth();
261     int fullsizeHeight = alheight * av.getCharHeight();
262
263     scalew = (float) width / (float) fullsizeWidth;
264     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
265
266     miniMe = new BufferedImage(width, sequencesHeight + graphHeight,
267             BufferedImage.TYPE_INT_RGB);
268
269     Graphics mg = miniMe.getGraphics();
270     mg.setColor(Color.orange);
271     mg.fillRect(0, 0, width, miniMe.getHeight());
272
273     float sampleCol = (float) alwidth / (float) width;
274     float sampleRow = (float) alheight / (float) sequencesHeight;
275
276     int lastcol = -1, lastrow = -1;
277     int color = Color.white.getRGB();
278     int row, col;
279     jalview.datamodel.SequenceI seq;
280     boolean hiddenRow = false;
281     for (row = 0; row < sequencesHeight; row++)
282     {
283       if ((int) (row * sampleRow) == lastrow)
284       {
285         // No need to recalculate the colours,
286         // Just copy from the row above
287         for (col = 0; col < width; col++)
288         {
289           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
290         }
291         continue;
292       }
293
294       lastrow = (int) (row * sampleRow);
295
296       hiddenRow = false;
297       if (av.hasHiddenRows())
298       {
299         seq = av.getAlignment().getHiddenSequences().getHiddenSequence(lastrow);
300         if (seq == null)
301         {
302           int index = av.getAlignment().getHiddenSequences()
303                   .findIndexWithoutHiddenSeqs(lastrow);
304
305           seq = av.getAlignment().getSequenceAt(index);
306         }
307         else
308         {
309           hiddenRow = true;
310         }
311       }
312       else
313       {
314         seq = av.getAlignment().getSequenceAt(lastrow);
315       }
316
317       if (seq == null)
318       {
319         System.out.println(lastrow + " null");
320         continue;
321       }
322
323       for (col = 0; col < width; col++)
324       {
325         if ((int) (col * sampleCol) == lastcol
326                 && (int) (row * sampleRow) == lastrow)
327         {
328           miniMe.setRGB(col, row, color);
329           continue;
330         }
331
332         lastcol = (int) (col * sampleCol);
333
334         if (seq.getLength() > lastcol)
335         {
336           color = sr.getResidueBoxColour(seq, lastcol).getRGB();
337
338           if (av.showSequenceFeatures)
339           {
340             color = fr.findFeatureColour(color, seq, lastcol);
341           }
342         }
343         else
344         {
345           color = -1; // White
346         }
347
348         if (hiddenRow
349                 || (av.hasHiddenColumns() && !av.getColumnSelection()
350                         .isVisible(lastcol)))
351         {
352           color = new Color(color).darker().darker().getRGB();
353         }
354
355         miniMe.setRGB(col, row, color);
356
357       }
358     }
359
360     if (av.getAlignmentConservationAnnotation()!= null)
361     {
362       renderer.updateFromAlignViewport(av); 
363       for (col = 0; col < width; col++)
364       {
365         lastcol = (int) (col * sampleCol);
366         {
367           mg.translate(col, sequencesHeight);
368           renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
369                   (int) (sampleCol) + 1, graphHeight,
370                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
371           mg.translate(-col, -sequencesHeight);
372         }
373       }
374     }
375     System.gc();
376
377     resizing = false;
378
379     setBoxPosition();
380
381     if (resizeAgain)
382     {
383       resizeAgain = false;
384       updateOverviewImage();
385     }
386   }
387
388   /**
389    * DOCUMENT ME!
390    */
391   public void setBoxPosition()
392   {
393     int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
394     int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
395             .getHiddenSequences().getSize()) * av.getCharHeight();
396
397     int startRes = av.getStartRes();
398     int endRes = av.getEndRes();
399
400     if (av.hasHiddenColumns())
401     {
402       startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
403       endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
404     }
405
406     int startSeq = av.startSeq;
407     int endSeq = av.endSeq;
408
409     if (av.hasHiddenRows())
410     {
411       startSeq = av.getAlignment().getHiddenSequences().adjustForHiddenSeqs(
412               startSeq);
413
414       endSeq = av.getAlignment().getHiddenSequences()
415               .adjustForHiddenSeqs(endSeq);
416
417     }
418
419     scalew = (float) width / (float) fullsizeWidth;
420     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
421
422     boxX = (int) (startRes * av.getCharWidth() * scalew);
423     boxY = (int) (startSeq * av.getCharHeight() * scaleh);
424
425     if (av.hasHiddenColumns())
426     {
427       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
428     }
429     else
430     {
431       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
432     }
433
434     boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
435
436     repaint();
437   }
438
439   /**
440    * DOCUMENT ME!
441    * 
442    * @param g
443    *          DOCUMENT ME!
444    */
445   public void paintComponent(Graphics g)
446   {
447     if (resizing)
448     {
449       g.setColor(Color.white);
450       g.fillRect(0, 0, getWidth(), getHeight());
451     }
452     else if (miniMe != null)
453     {
454       g.drawImage(miniMe, 0, 0, this);
455     }
456
457     g.setColor(Color.red);
458     g.drawRect(boxX, boxY, boxWidth, boxHeight);
459     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
460
461   }
462 }