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