JAL-967, JAL-1114, JAL-1115 - modified render and calculation api to work on bare...
[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       @Override
119       public void componentResized(ComponentEvent evt)
120       {
121         if ((getWidth() != width)
122                 || (getHeight() != (sequencesHeight + graphHeight)))
123         {
124           updateOverviewImage();
125         }
126       }
127     });
128
129     addMouseMotionListener(new MouseMotionAdapter()
130     {
131       @Override
132       public void mouseDragged(MouseEvent evt)
133       {
134         if (!av.wrapAlignment)
135         {
136           // TODO: feature: jv2.5 detect shift drag and update selection from
137           // it.
138           boxX = evt.getX();
139           boxY = evt.getY();
140           checkValid();
141         }
142       }
143     });
144
145     addMouseListener(new MouseAdapter()
146     {
147       @Override
148       public void mousePressed(MouseEvent evt)
149       {
150         if (!av.wrapAlignment)
151         {
152           boxX = evt.getX();
153           boxY = evt.getY();
154           checkValid();
155         }
156       }
157     });
158
159     updateOverviewImage();
160   }
161
162   /**
163    * DOCUMENT ME!
164    */
165   void checkValid()
166   {
167     if (boxY < 0)
168     {
169       boxY = 0;
170     }
171
172     if (boxY > (sequencesHeight - boxHeight))
173     {
174       boxY = sequencesHeight - boxHeight + 1;
175     }
176
177     if (boxX < 0)
178     {
179       boxX = 0;
180     }
181
182     if (boxX > (width - boxWidth))
183     {
184       if (av.hasHiddenColumns())
185       {
186         // Try smallest possible box
187         boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
188       }
189       boxX = width - boxWidth;
190     }
191
192     int col = (int) (boxX / scalew / av.getCharWidth());
193     int row = (int) (boxY / scaleh / av.getCharHeight());
194
195     if (av.hasHiddenColumns())
196     {
197       if (!av.getColumnSelection().isVisible(col))
198       {
199         return;
200       }
201
202       col = av.getColumnSelection().findColumnPosition(col);
203     }
204
205     if (av.hasHiddenRows())
206     {
207       row = av.getAlignment().getHiddenSequences().findIndexWithoutHiddenSeqs(
208               row);
209     }
210
211     ap.setScrollValues(col, row);
212
213   }
214
215   /**
216    * DOCUMENT ME!
217    */
218   public void updateOverviewImage()
219   {
220     if (resizing)
221     {
222       resizeAgain = true;
223       return;
224     }
225
226     resizing = true;
227
228     if ((getWidth() > 0) && (getHeight() > 0))
229     {
230       width = getWidth();
231       sequencesHeight = getHeight() - graphHeight;
232     }
233
234     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
235
236     Thread thread = new Thread(this);
237     thread.start();
238     repaint();
239   }
240
241   // This is set true if the user resizes whilst
242   // the overview is being calculated
243   boolean resizeAgain = false;
244
245   /**
246    * DOCUMENT ME!
247    */
248   @Override
249   public void run()
250   {
251     miniMe = null;
252
253     if (av.showSequenceFeatures)
254     {
255       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
256     }
257
258     int alwidth = av.getAlignment().getWidth();
259     int alheight = av.getAlignment().getHeight()
260             + av.getAlignment().getHiddenSequences().getSize();
261
262     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
263
264     int fullsizeWidth = alwidth * av.getCharWidth();
265     int fullsizeHeight = alheight * av.getCharHeight();
266
267     scalew = (float) width / (float) fullsizeWidth;
268     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
269
270     miniMe = new BufferedImage(width, sequencesHeight + graphHeight,
271             BufferedImage.TYPE_INT_RGB);
272
273     Graphics mg = miniMe.getGraphics();
274     mg.setColor(Color.orange);
275     mg.fillRect(0, 0, width, miniMe.getHeight());
276
277     float sampleCol = (float) alwidth / (float) width;
278     float sampleRow = (float) alheight / (float) sequencesHeight;
279
280     int lastcol = -1, lastrow = -1;
281     int color = Color.white.getRGB();
282     int row, col;
283     jalview.datamodel.SequenceI seq;
284     boolean hiddenRow = false;
285     for (row = 0; row < sequencesHeight; row++)
286     {
287       if ((int) (row * sampleRow) == lastrow)
288       {
289         // No need to recalculate the colours,
290         // Just copy from the row above
291         for (col = 0; col < width; col++)
292         {
293           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
294         }
295         continue;
296       }
297
298       lastrow = (int) (row * sampleRow);
299
300       hiddenRow = false;
301       if (av.hasHiddenRows())
302       {
303         seq = av.getAlignment().getHiddenSequences().getHiddenSequence(lastrow);
304         if (seq == null)
305         {
306           int index = av.getAlignment().getHiddenSequences()
307                   .findIndexWithoutHiddenSeqs(lastrow);
308
309           seq = av.getAlignment().getSequenceAt(index);
310         }
311         else
312         {
313           hiddenRow = true;
314         }
315       }
316       else
317       {
318         seq = av.getAlignment().getSequenceAt(lastrow);
319       }
320
321       if (seq == null)
322       {
323         System.out.println(lastrow + " null");
324         continue;
325       }
326
327       for (col = 0; col < width; col++)
328       {
329         if ((int) (col * sampleCol) == lastcol
330                 && (int) (row * sampleRow) == lastrow)
331         {
332           miniMe.setRGB(col, row, color);
333           continue;
334         }
335
336         lastcol = (int) (col * sampleCol);
337
338         if (seq.getLength() > lastcol)
339         {
340           color = sr.getResidueBoxColour(seq, lastcol).getRGB();
341
342           if (av.showSequenceFeatures)
343           {
344             color = fr.findFeatureColour(color, seq, lastcol);
345           }
346         }
347         else
348         {
349           color = -1; // White
350         }
351
352         if (hiddenRow
353                 || (av.hasHiddenColumns() && !av.getColumnSelection()
354                         .isVisible(lastcol)))
355         {
356           color = new Color(color).darker().darker().getRGB();
357         }
358
359         miniMe.setRGB(col, row, color);
360
361       }
362     }
363
364     if (av.getAlignmentConservationAnnotation()!= null)
365     {
366       renderer.updateFromAlignViewport(av);
367       for (col = 0; col < width; col++)
368       {
369         lastcol = (int) (col * sampleCol);
370         {
371           mg.translate(col, sequencesHeight);
372           renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),av.getAlignmentConservationAnnotation().annotations,
373                   (int) (sampleCol) + 1, graphHeight,
374                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
375           mg.translate(-col, -sequencesHeight);
376         }
377       }
378     }
379     System.gc();
380
381     resizing = false;
382
383     setBoxPosition();
384
385     if (resizeAgain)
386     {
387       resizeAgain = false;
388       updateOverviewImage();
389     }
390   }
391
392   /**
393    * DOCUMENT ME!
394    */
395   public void setBoxPosition()
396   {
397     int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
398     int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
399             .getHiddenSequences().getSize()) * av.getCharHeight();
400
401     int startRes = av.getStartRes();
402     int endRes = av.getEndRes();
403
404     if (av.hasHiddenColumns())
405     {
406       startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
407       endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
408     }
409
410     int startSeq = av.startSeq;
411     int endSeq = av.endSeq;
412
413     if (av.hasHiddenRows())
414     {
415       startSeq = av.getAlignment().getHiddenSequences().adjustForHiddenSeqs(
416               startSeq);
417
418       endSeq = av.getAlignment().getHiddenSequences()
419               .adjustForHiddenSeqs(endSeq);
420
421     }
422
423     scalew = (float) width / (float) fullsizeWidth;
424     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
425
426     boxX = (int) (startRes * av.getCharWidth() * scalew);
427     boxY = (int) (startSeq * av.getCharHeight() * scaleh);
428
429     if (av.hasHiddenColumns())
430     {
431       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
432     }
433     else
434     {
435       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
436     }
437
438     boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
439
440     repaint();
441   }
442
443   /**
444    * DOCUMENT ME!
445    *
446    * @param g
447    *          DOCUMENT ME!
448    */
449   @Override
450   public void paintComponent(Graphics g)
451   {
452     if (resizing)
453     {
454       g.setColor(Color.white);
455       g.fillRect(0, 0, getWidth(), getHeight());
456     }
457     else if (miniMe != null)
458     {
459       g.drawImage(miniMe, 0, 0, this);
460     }
461
462     g.setColor(Color.red);
463     g.drawRect(boxX, boxY, boxWidth, boxHeight);
464     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
465
466   }
467 }