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