Merge branch 'develop' (JAL-4102 2.11.2.6 patch release) into features/r2_11_2_alphaf...
[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.datamodel.SequenceI;
24 import jalview.renderer.AnnotationRenderer;
25 import jalview.viewmodel.OverviewDimensions;
26 import jalview.renderer.seqfeatures.FeatureColourFinder;
27
28 import java.awt.Color;
29 import java.awt.Dimension;
30 import java.awt.Graphics;
31 import java.awt.event.ComponentAdapter;
32 import java.awt.event.ComponentEvent;
33 import java.awt.event.MouseAdapter;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseMotionAdapter;
36 import java.awt.image.BufferedImage;
37
38 import javax.swing.JPanel;
39
40 /**
41  * Panel displaying an overview of the full alignment, with an interactive box
42  * representing the viewport onto the alignment.
43  * 
44  * @author $author$
45  * @version $Revision$
46  */
47 public class OverviewPanel extends JPanel implements Runnable
48 {
49   private static final Color TRANS_GREY = new Color(100, 100, 100, 25);
50
51   private final AnnotationRenderer renderer = new AnnotationRenderer();
52
53   private OverviewDimensions od;
54
55   private BufferedImage miniMe;
56
57   private BufferedImage lastMiniMe = null;
58
59   private AlignViewport av;
60
61   private AlignmentPanel ap;
62
63   //
64   private boolean resizing = false;
65
66   // This is set true if the user resizes whilst
67   // the overview is being calculated
68   private boolean resizeAgain = false;
69
70   // Can set different properties in this seqCanvas than
71   // main visible SeqCanvas
72   private SequenceRenderer sr;
73
74   private jalview.renderer.seqfeatures.FeatureRenderer fr;
75
76   /**
77    * Creates a new OverviewPanel object.
78    * 
79    * @param alPanel
80    *          The alignment panel which is shown in the overview panel
81    */
82   public OverviewPanel(AlignmentPanel alPanel)
83   {
84     this.av = alPanel.av;
85     this.ap = alPanel;
86     setLayout(null);
87
88     sr = new SequenceRenderer(av);
89     sr.renderGaps = false;
90     sr.forOverview = true;
91     fr = new FeatureRenderer(alPanel);
92
93     od = new OverviewDimensions(av.getRanges(), av.isShowAnnotation());
94
95     addComponentListener(new ComponentAdapter()
96     {
97       @Override
98       public void componentResized(ComponentEvent evt)
99       {
100         if ((getWidth() != od.getWidth())
101                 || (getHeight() != (od.getHeight())))
102         {
103           updateOverviewImage();
104         }
105       }
106     });
107
108     addMouseMotionListener(new MouseMotionAdapter()
109     {
110       @Override
111       public void mouseDragged(MouseEvent evt)
112       {
113         if (!av.getWrapAlignment())
114         {
115           od.updateViewportFromMouse(evt.getX(), evt.getY(), av
116                   .getAlignment().getHiddenSequences(), av
117                   .getColumnSelection(), av.getRanges());
118           ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
119         }
120       }
121     });
122
123     addMouseListener(new MouseAdapter()
124     {
125       @Override
126       public void mousePressed(MouseEvent evt)
127       {
128         if (!av.getWrapAlignment())
129         {
130           od.updateViewportFromMouse(evt.getX(), evt.getY(), av
131                   .getAlignment().getHiddenSequences(), av
132                   .getColumnSelection(), av.getRanges());
133           ap.setScrollValues(od.getScrollCol(), od.getScrollRow());
134         }
135       }
136     });
137
138     updateOverviewImage();
139   }
140
141   /**
142    * Updates the overview image when the related alignment panel is updated
143    */
144   public void updateOverviewImage()
145   {
146     if (resizing)
147     {
148       resizeAgain = true;
149       return;
150     }
151
152     resizing = true;
153
154     if ((getWidth() > 0) && (getHeight() > 0))
155     {
156       od.setWidth(getWidth());
157       od.setHeight(getHeight());
158     }
159
160     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
161
162     Thread thread = new Thread(this);
163     thread.start();
164     repaint();
165   }
166
167   @Override
168   public void run()
169   {
170     miniMe = null;
171
172     if (av.isShowSequenceFeatures())
173     {
174       fr.transferSettings(ap.getSeqPanel().seqCanvas.getFeatureRenderer());
175     }
176
177     // why do we need to set preferred size again? was set in
178     // updateOverviewImage
179     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
180
181     miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
182             BufferedImage.TYPE_INT_RGB);
183
184     Graphics mg = miniMe.getGraphics();
185     mg.setColor(Color.orange);
186     mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight());
187
188 <<<<<<< HEAD
189     // calculate sampleCol and sampleRow
190     // alignment width is max number of residues/bases
191     // alignment height is number of sequences
192     int alwidth = av.getAlignment().getWidth();
193     int alheight = av.getAlignment().getAbsoluteHeight();
194
195     // sampleCol or sampleRow is the width/height allocated to each residue
196     // in particular, sometimes we may need more than one row/col of the
197     // BufferedImage allocated
198     // sampleCol is how much of a residue to assign to each pixel
199     // sampleRow is how many sequences to assign to each pixel
200     float sampleCol = alwidth / (float) od.getWidth();
201     float sampleRow = alheight / (float) od.getSequencesHeight();
202
203     buildImage(sampleRow, sampleCol);
204 =======
205     float sampleCol = (float) alwidth / (float) width;
206     float sampleRow = (float) alheight / (float) sequencesHeight;
207
208     int lastcol = -1, lastrow = -1;
209     Color color = Color.white;
210     int row, col;
211     jalview.datamodel.SequenceI seq;
212     final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
213             .hasHiddenColumns();
214     boolean hiddenRow = false;
215     // get hidden row and hidden column map once at beginning.
216     // clone featureRenderer settings to avoid race conditions... if state is
217     // updated just need to refresh again
218
219     FeatureColourFinder finder = new FeatureColourFinder(fr);
220
221     for (row = 0; row < sequencesHeight; row++)
222     {
223       if (resizeAgain)
224       {
225         break;
226       }
227       if ((int) (row * sampleRow) == lastrow)
228       {
229         // No need to recalculate the colours,
230         // Just copy from the row above
231         for (col = 0; col < width; col++)
232         {
233           if (resizeAgain)
234           {
235             break;
236           }
237           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
238         }
239         continue;
240       }
241
242       lastrow = (int) (row * sampleRow);
243
244       hiddenRow = false;
245       if (hasHiddenRows)
246       {
247         seq = av.getAlignment().getHiddenSequences()
248                 .getHiddenSequence(lastrow);
249         if (seq == null)
250         {
251           int index = av.getAlignment().getHiddenSequences()
252                   .findIndexWithoutHiddenSeqs(lastrow);
253
254           seq = av.getAlignment().getSequenceAt(index);
255         }
256         else
257         {
258           hiddenRow = true;
259         }
260       }
261       else
262       {
263         seq = av.getAlignment().getSequenceAt(lastrow);
264       }
265
266       if (seq == null)
267       {
268         System.out.println(lastrow + " null");
269         continue;
270       }
271
272       for (col = 0; col < width; col++)
273       {
274         if (resizeAgain)
275         {
276           break;
277         }
278         if ((int) (col * sampleCol) == lastcol
279                 && (int) (row * sampleRow) == lastrow)
280         {
281           miniMe.setRGB(col, row, color.getRGB());
282           continue;
283         }
284
285         lastcol = (int) (col * sampleCol);
286
287         if (seq.getLength() > lastcol)
288         {
289           color = sr.getResidueColour(seq, lastcol, finder);
290         }
291         else
292         {
293           color = Color.WHITE;
294         }
295
296         if (hiddenRow
297                 || (hasHiddenCols && !av.getColumnSelection().isVisible(
298                         lastcol)))
299         {
300           color = color.darker().darker();
301         }
302
303         miniMe.setRGB(col, row, color.getRGB());
304 >>>>>>> bug/JAL-2436featureRendererThreading
305
306     if (av.isShowAnnotation())
307     {
308       renderer.updateFromAlignViewport(av);
309       for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
310       {
311         mg.translate(col, od.getSequencesHeight());
312         renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(),
313                 av.getAlignmentConservationAnnotation().annotations,
314                 (int) (sampleCol) + 1, od.getGraphHeight(),
315                 (int) (col * sampleCol), (int) (col * sampleCol) + 1);
316         mg.translate(-col, -od.getSequencesHeight());
317
318       }
319     }
320     System.gc();
321
322     resizing = false;
323
324     if (resizeAgain)
325     {
326       resizeAgain = false;
327       updateOverviewImage();
328     }
329     else
330     {
331       lastMiniMe = miniMe;
332     }
333
334     setBoxPosition();
335   }
336
337   /*
338    * Build the overview panel image
339    */
340   private void buildImage(float sampleRow, float sampleCol)
341   {
342     int lastcol = -1;
343     int lastrow = -1;
344     int color = Color.white.getRGB();
345
346     SequenceI seq = null;
347
348     final boolean hasHiddenCols = av.hasHiddenColumns();
349     boolean hiddenRow = false;
350     // get hidden row and hidden column map once at beginning.
351     // clone featureRenderer settings to avoid race conditions... if state is
352     // updated just need to refresh again
353     for (int row = 0; row < od.getSequencesHeight() && !resizeAgain; row++)
354     {
355       boolean doCopy = true;
356       int currentrow = (int) (row * sampleRow);
357       if (currentrow != lastrow)
358       {
359         doCopy = false;
360
361         lastrow = currentrow;
362
363         // get the sequence which would be at alignment index 'lastrow' if no
364         // rows were hidden, and determine whether it is hidden or not
365         hiddenRow = av.getAlignment().isHidden(lastrow);
366         seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow);
367       }
368
369       for (int col = 0; col < od.getWidth() && !resizeAgain; col++)
370       {
371         if (doCopy)
372         {
373           color = miniMe.getRGB(col, row - 1);
374         }
375         else if ((int) (col * sampleCol) != lastcol
376                 || (int) (row * sampleRow) != lastrow)
377         {
378           lastcol = (int) (col * sampleCol);
379           color = getColumnColourFromSequence(seq, hiddenRow, hasHiddenCols,
380                   lastcol);
381         }
382         // else we just use the color we already have , so don't need to set it
383
384         miniMe.setRGB(col, row, color);
385       }
386     }
387   }
388
389   /*
390    * Find the colour of a sequence at a specified column position
391    */
392   private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
393           boolean hiddenRow, boolean hasHiddenCols, int lastcol)
394   {
395     int color;
396
397     if (seq == null)
398     {
399       color = Color.white.getRGB();
400     }
401     else if (seq.getLength() > lastcol)
402     {
403       color = sr.getResidueBoxColour(seq, lastcol).getRGB();
404
405       if (av.isShowSequenceFeatures())
406       {
407         color = fr.findFeatureColour(color, seq, lastcol);
408       }
409     }
410     else
411     {
412       color = Color.white.getRGB();
413     }
414
415     if (hiddenRow
416             || (hasHiddenCols && !av.getColumnSelection()
417                     .isVisible(lastcol)))
418     {
419       color = new Color(color).darker().darker().getRGB();
420     }
421
422     return color;
423   }
424
425   /**
426    * Update the overview panel box when the associated alignment panel is
427    * changed
428    * 
429    */
430   public void setBoxPosition()
431   {
432     od.setBoxPosition(av.getAlignment()
433             .getHiddenSequences(), av.getColumnSelection(), av.getRanges());
434     repaint();
435   }
436
437
438   @Override
439   public void paintComponent(Graphics g)
440   {
441     if (resizing || resizeAgain)
442     {
443       if (lastMiniMe == null)
444       {
445         g.setColor(Color.white);
446         g.fillRect(0, 0, getWidth(), getHeight());
447       }
448       else
449       {
450         g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
451       }
452       g.setColor(TRANS_GREY);
453       g.fillRect(0, 0, getWidth(), getHeight());
454     }
455     else if (lastMiniMe != null)
456     {
457       g.drawImage(lastMiniMe, 0, 0, this);
458       if (lastMiniMe != miniMe)
459       {
460         g.setColor(TRANS_GREY);
461         g.fillRect(0, 0, getWidth(), getHeight());
462       }
463     }
464
465     g.setColor(Color.red);
466     od.drawBox(g);
467   }
468 }