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