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