bc6472841af05edf8a8a9900c016fa6881974ef2
[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
270     if (av.isShowSequenceFeatures())
271     {
272       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
273     }
274
275     if (getSize().width > 0 && getSize().height > 0)
276     {
277       width = getSize().width;
278       sequencesHeight = getSize().height - graphHeight;
279     }
280
281     setSize(new Dimension(width, sequencesHeight + graphHeight));
282
283     int fullsizeWidth = alwidth * av.getCharWidth();
284     int fullsizeHeight = alheight * av.getCharHeight();
285
286     scalew = (float) width / (float) fullsizeWidth;
287     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
288
289     miniMe = nullFrame.createImage(width, sequencesHeight + graphHeight);
290     offscreen = nullFrame.createImage(width, sequencesHeight + graphHeight);
291
292     Graphics mg = miniMe.getGraphics();
293     float sampleCol = (float) alwidth / (float) width;
294     float sampleRow = (float) alheight / (float) sequencesHeight;
295
296     int lastcol = 0, lastrow = 0;
297     int xstart = 0, ystart = 0;
298     Color color = Color.yellow;
299     int row, col, sameRow = 0, sameCol = 0;
300     jalview.datamodel.SequenceI seq;
301     final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
302             .hasHiddenColumns();
303     boolean hiddenRow = false;
304     AlignmentI alignment = av.getAlignment();
305     for (row = 0; row <= sequencesHeight; row++)
306     {
307       if ((int) (row * sampleRow) == lastrow)
308       {
309         sameRow++;
310         continue;
311       }
312
313       hiddenRow = false;
314       if (hasHiddenRows)
315       {
316         seq = alignment.getHiddenSequences().getHiddenSequence(lastrow);
317         if (seq == null)
318         {
319           int index = alignment.getHiddenSequences()
320                   .findIndexWithoutHiddenSeqs(lastrow);
321
322           seq = alignment.getSequenceAt(index);
323         }
324         else
325         {
326           hiddenRow = true;
327         }
328       }
329       else
330       {
331         seq = alignment.getSequenceAt(lastrow);
332       }
333
334       for (col = 0; col < width; col++)
335       {
336         if ((int) (col * sampleCol) == lastcol
337                 && (int) (row * sampleRow) == lastrow)
338         {
339           sameCol++;
340           continue;
341         }
342
343         lastcol = (int) (col * sampleCol);
344
345         if (seq.getLength() > lastcol)
346         {
347           color = sr.getResidueBoxColour(seq, lastcol);
348
349           if (av.isShowSequenceFeatures())
350           {
351             color = fr.findFeatureColour(color, seq, lastcol);
352           }
353         }
354         else
355         {
356           color = Color.white; // White
357         }
358
359         if (hiddenRow
360                 || (hasHiddenCols && !av.getColumnSelection().isVisible(
361                         lastcol)))
362         {
363           color = color.darker().darker();
364         }
365
366         mg.setColor(color);
367         if (sameCol == 1 && sameRow == 1)
368         {
369           mg.drawLine(xstart, ystart, xstart, ystart);
370         }
371         else
372         {
373           mg.fillRect(xstart, ystart, sameCol, sameRow);
374         }
375
376         xstart = col;
377         sameCol = 1;
378       }
379       lastrow = (int) (row * sampleRow);
380       ystart = row;
381       sameRow = 1;
382     }
383
384     if (av.getAlignmentConservationAnnotation() != null)
385     {
386       for (col = 0; col < width; col++)
387       {
388         lastcol = (int) (col * sampleCol);
389         {
390           mg.translate(col, sequencesHeight);
391           ap.annotationPanel.renderer.drawGraph(mg,
392                   av.getAlignmentConservationAnnotation(),
393                   av.getAlignmentConservationAnnotation().annotations,
394                   (int) (sampleCol) + 1, graphHeight,
395                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
396           mg.translate(-col, -sequencesHeight);
397         }
398       }
399     }
400     System.gc();
401
402     resizing = false;
403
404     setBoxPosition();
405
406     if (resizeAgain)
407     {
408       resizeAgain = false;
409       updateOverviewImage();
410     }
411   }
412
413   public void setBoxPosition()
414   {
415     int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
416     int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
417             .getHiddenSequences().getSize())
418             * av.getCharHeight();
419
420     int startRes = av.getStartRes();
421     int endRes = av.getEndRes();
422
423     if (av.hasHiddenColumns())
424     {
425       startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
426       endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
427     }
428
429     int startSeq = av.startSeq;
430     int endSeq = av.endSeq;
431
432     if (av.hasHiddenRows())
433     {
434       startSeq = av.getAlignment().getHiddenSequences()
435               .adjustForHiddenSeqs(startSeq);
436
437       endSeq = av.getAlignment().getHiddenSequences()
438               .adjustForHiddenSeqs(endSeq);
439
440     }
441
442     scalew = (float) width / (float) fullsizeWidth;
443     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
444
445     boxX = (int) (startRes * av.getCharWidth() * scalew);
446     boxY = (int) (startSeq * av.getCharHeight() * scaleh);
447
448     if (av.hasHiddenColumns())
449     {
450       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
451     }
452     else
453     {
454       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
455     }
456
457     boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
458
459     repaint();
460   }
461
462   @Override
463   public void update(Graphics g)
464   {
465     paint(g);
466   }
467
468   @Override
469   public void paint(Graphics g)
470   {
471     Graphics og = offscreen.getGraphics();
472     if (miniMe != null)
473     {
474       og.drawImage(miniMe, 0, 0, this);
475       og.setColor(Color.red);
476       og.drawRect(boxX, boxY, boxWidth, boxHeight);
477       og.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
478       g.drawImage(offscreen, 0, 0, this);
479     }
480   }
481
482 }