39a232a57c3ce4bfae1fa46ccd1623c9a23fd2bb
[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     boolean hiddenRow = false;
296     for (row = 0; row < sequencesHeight; row++)
297     {
298       if ((int) (row * sampleRow) == lastrow)
299       {
300         // No need to recalculate the colours,
301         // Just copy from the row above
302         for (col = 0; col < width; col++)
303         {
304           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
305         }
306         continue;
307       }
308
309       lastrow = (int) (row * sampleRow);
310
311       hiddenRow = false;
312       if (av.hasHiddenRows())
313       {
314         seq = av.getAlignment().getHiddenSequences()
315                 .getHiddenSequence(lastrow);
316         if (seq == null)
317         {
318           int index = av.getAlignment().getHiddenSequences()
319                   .findIndexWithoutHiddenSeqs(lastrow);
320
321           seq = av.getAlignment().getSequenceAt(index);
322         }
323         else
324         {
325           hiddenRow = true;
326         }
327       }
328       else
329       {
330         seq = av.getAlignment().getSequenceAt(lastrow);
331       }
332
333       if (seq == null)
334       {
335         System.out.println(lastrow + " null");
336         continue;
337       }
338
339       for (col = 0; col < width; col++)
340       {
341         if ((int) (col * sampleCol) == lastcol
342                 && (int) (row * sampleRow) == lastrow)
343         {
344           miniMe.setRGB(col, row, color);
345           continue;
346         }
347
348         lastcol = (int) (col * sampleCol);
349
350         if (seq.getLength() > lastcol)
351         {
352           color = sr.getResidueBoxColour(seq, lastcol).getRGB();
353
354           if (av.isShowSequenceFeatures())
355           {
356             color = fr.findFeatureColour(color, seq, lastcol);
357           }
358         }
359         else
360         {
361           color = -1; // White
362         }
363
364         if (hiddenRow
365                 || (av.hasHiddenColumns() && !av.getColumnSelection()
366                         .isVisible(lastcol)))
367         {
368           color = new Color(color).darker().darker().getRGB();
369         }
370
371         miniMe.setRGB(col, row, color);
372
373       }
374     }
375
376     if (av.getAlignmentConservationAnnotation() != null)
377     {
378       renderer.updateFromAlignViewport(av);
379       for (col = 0; col < width; col++)
380       {
381         lastcol = (int) (col * sampleCol);
382         {
383           mg.translate(col, sequencesHeight);
384           renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
385                   av.getAlignmentConservationAnnotation().annotations,
386                   (int) (sampleCol) + 1, graphHeight,
387                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
388           mg.translate(-col, -sequencesHeight);
389         }
390       }
391     }
392     System.gc();
393
394     resizing = false;
395
396     setBoxPosition();
397
398     if (resizeAgain)
399     {
400       resizeAgain = false;
401       updateOverviewImage();
402     }
403   }
404
405   /**
406    * DOCUMENT ME!
407    */
408   public void setBoxPosition()
409   {
410     int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
411     int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
412             .getHiddenSequences().getSize())
413             * av.getCharHeight();
414
415     int startRes = av.getStartRes();
416     int endRes = av.getEndRes();
417
418     if (av.hasHiddenColumns())
419     {
420       startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
421       endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
422     }
423
424     int startSeq = av.startSeq;
425     int endSeq = av.endSeq;
426
427     if (av.hasHiddenRows())
428     {
429       startSeq = av.getAlignment().getHiddenSequences()
430               .adjustForHiddenSeqs(startSeq);
431
432       endSeq = av.getAlignment().getHiddenSequences()
433               .adjustForHiddenSeqs(endSeq);
434
435     }
436
437     scalew = (float) width / (float) fullsizeWidth;
438     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
439
440     boxX = (int) (startRes * av.getCharWidth() * scalew);
441     boxY = (int) (startSeq * av.getCharHeight() * scaleh);
442
443     if (av.hasHiddenColumns())
444     {
445       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
446     }
447     else
448     {
449       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
450     }
451
452     boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
453
454     repaint();
455   }
456
457   /**
458    * DOCUMENT ME!
459    * 
460    * @param g
461    *          DOCUMENT ME!
462    */
463   @Override
464   public void paintComponent(Graphics g)
465   {
466     if (resizing)
467     {
468       g.setColor(Color.white);
469       g.fillRect(0, 0, getWidth(), getHeight());
470     }
471     else if (miniMe != null)
472     {
473       g.drawImage(miniMe, 0, 0, this);
474     }
475
476     g.setColor(Color.red);
477     g.drawRect(boxX, boxY, boxWidth, boxHeight);
478     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
479
480   }
481 }