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