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