ae8a85bcf3a27af7d44ea3162f10868eef2d7fc0
[jalview.git] / src / jalview / appletgui / OverviewPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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       @Override
106       public void componentResized(ComponentEvent evt)
107       {
108         if (getSize().width != width
109                 || getSize().height != sequencesHeight + graphHeight)
110         {
111           updateOverviewImage();
112         }
113       }
114     });
115
116     addMouseMotionListener(this);
117
118     addMouseListener(this);
119
120     updateOverviewImage();
121
122   }
123
124   @Override
125   public void mouseEntered(MouseEvent evt)
126   {
127   }
128
129   @Override
130   public void mouseExited(MouseEvent evt)
131   {
132   }
133
134   @Override
135   public void mouseClicked(MouseEvent evt)
136   {
137   }
138
139   @Override
140   public void mouseMoved(MouseEvent evt)
141   {
142   }
143
144   @Override
145   public void mousePressed(MouseEvent evt)
146   {
147     boxX = evt.getX();
148     boxY = evt.getY();
149     checkValid();
150   }
151
152   @Override
153   public void mouseReleased(MouseEvent evt)
154   {
155     boxX = evt.getX();
156     boxY = evt.getY();
157     checkValid();
158   }
159
160   @Override
161   public void mouseDragged(MouseEvent evt)
162   {
163     boxX = evt.getX();
164     boxY = evt.getY();
165     checkValid();
166   }
167
168   void checkValid()
169   {
170     if (boxY < 0)
171     {
172       boxY = 0;
173     }
174
175     if (boxY > (sequencesHeight - boxHeight))
176     {
177       boxY = sequencesHeight - boxHeight + 1;
178     }
179
180     if (boxX < 0)
181     {
182       boxX = 0;
183     }
184
185     if (boxX > (width - boxWidth))
186     {
187       if (av.hasHiddenColumns())
188       {
189         // Try smallest possible box
190         boxWidth = (int) ((av.endRes - av.startRes + 1) * av.getCharWidth() * scalew);
191       }
192       boxX = width - boxWidth;
193     }
194
195     int col = (int) (boxX / scalew / av.getCharWidth());
196     int row = (int) (boxY / scaleh / av.getCharHeight());
197
198     if (av.hasHiddenColumns())
199     {
200       if (!av.getColumnSelection().isVisible(col))
201       {
202         return;
203       }
204
205       col = av.getColumnSelection().findColumnPosition(col);
206     }
207
208     if (av.hasHiddenRows())
209     {
210       row = av.getAlignment().getHiddenSequences()
211               .findIndexWithoutHiddenSeqs(row);
212     }
213
214     ap.setScrollValues(col, row);
215     ap.paintAlignment(false);
216   }
217
218   /**
219    * DOCUMENT ME!
220    */
221   public void updateOverviewImage()
222   {
223     if (resizing)
224     {
225       resizeAgain = true;
226       return;
227     }
228
229     if (av.showSequenceFeatures)
230     {
231       fr.featureGroups = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups;
232       fr.featureColours = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours;
233     }
234
235     resizing = true;
236
237     if ((getSize().width > 0) && (getSize().height > 0))
238     {
239       width = getSize().width;
240       sequencesHeight = getSize().height - graphHeight;
241     }
242     setSize(new Dimension(width, sequencesHeight + graphHeight));
243
244     Thread thread = new Thread(this);
245     thread.start();
246     repaint();
247   }
248
249   // This is set true if the user resizes whilst
250   // the overview is being calculated
251   boolean resizeAgain = false;
252
253   @Override
254   public void run()
255   {
256     miniMe = null;
257     int alwidth = av.getAlignment().getWidth();
258     int alheight = av.getAlignment().getHeight();
259
260     if (av.showSequenceFeatures)
261     {
262       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
263     }
264
265     if (getSize().width > 0 && getSize().height > 0)
266     {
267       width = getSize().width;
268       sequencesHeight = getSize().height - graphHeight;
269     }
270
271     setSize(new Dimension(width, sequencesHeight + graphHeight));
272
273     int fullsizeWidth = alwidth * av.getCharWidth();
274     int fullsizeHeight = alheight * av.getCharHeight();
275
276     scalew = (float) width / (float) fullsizeWidth;
277     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
278
279     miniMe = nullFrame.createImage(width, sequencesHeight + graphHeight);
280     offscreen = nullFrame.createImage(width, sequencesHeight + graphHeight);
281
282     Graphics mg = miniMe.getGraphics();
283     float sampleCol = (float) alwidth / (float) width;
284     float sampleRow = (float) alheight / (float) sequencesHeight;
285
286     int lastcol = 0, lastrow = 0;
287     int xstart = 0, ystart = 0;
288     Color color = Color.yellow;
289     int row, col, sameRow = 0, sameCol = 0;
290     jalview.datamodel.SequenceI seq;
291     boolean hiddenRow = false;
292     AlignmentI alignment = av.getAlignment();
293     for (row = 0; row <= sequencesHeight; row++)
294     {
295       if ((int) (row * sampleRow) == lastrow)
296       {
297         sameRow++;
298         continue;
299       }
300
301       hiddenRow = false;
302       if (av.hasHiddenRows())
303       {
304         seq = alignment.getHiddenSequences().getHiddenSequence(lastrow);
305         if (seq == null)
306         {
307           int index = alignment.getHiddenSequences()
308                   .findIndexWithoutHiddenSeqs(lastrow);
309
310           seq = alignment.getSequenceAt(index);
311         }
312         else
313         {
314           hiddenRow = true;
315         }
316       }
317       else
318       {
319         seq = alignment.getSequenceAt(lastrow);
320       }
321
322       for (col = 0; col < width; col++)
323       {
324         if ((int) (col * sampleCol) == lastcol
325                 && (int) (row * sampleRow) == lastrow)
326         {
327           sameCol++;
328           continue;
329         }
330
331         lastcol = (int) (col * sampleCol);
332
333         if (seq.getLength() > lastcol)
334         {
335           color = sr.getResidueBoxColour(seq, lastcol);
336
337           if (av.showSequenceFeatures)
338           {
339             color = fr.findFeatureColour(color, seq, lastcol);
340           }
341         }
342         else
343         {
344           color = Color.white; // White
345         }
346
347         if (hiddenRow
348                 || (av.hasHiddenColumns() && !av.getColumnSelection()
349                         .isVisible(lastcol)))
350         {
351           color = color.darker().darker();
352         }
353
354         mg.setColor(color);
355         if (sameCol == 1 && sameRow == 1)
356         {
357           mg.drawLine(xstart, ystart, xstart, ystart);
358         }
359         else
360         {
361           mg.fillRect(xstart, ystart, sameCol, sameRow);
362         }
363
364         xstart = col;
365         sameCol = 1;
366       }
367       lastrow = (int) (row * sampleRow);
368       ystart = row;
369       sameRow = 1;
370     }
371
372     if (av.getAlignmentConservationAnnotation() != null)
373     {
374       for (col = 0; col < width; col++)
375       {
376         lastcol = (int) (col * sampleCol);
377         {
378           mg.translate(col, sequencesHeight);
379           ap.annotationPanel.renderer.drawGraph(mg,
380                   av.getAlignmentConservationAnnotation(),
381                   av.getAlignmentConservationAnnotation().annotations,
382                   (int) (sampleCol) + 1, graphHeight,
383                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
384           mg.translate(-col, -sequencesHeight);
385         }
386       }
387     }
388     System.gc();
389
390     resizing = false;
391
392     setBoxPosition();
393
394     if (resizeAgain)
395     {
396       resizeAgain = false;
397       updateOverviewImage();
398     }
399   }
400
401   public void setBoxPosition()
402   {
403     int fullsizeWidth = av.getAlignment().getWidth() * av.getCharWidth();
404     int fullsizeHeight = (av.getAlignment().getHeight() + av.getAlignment()
405             .getHiddenSequences().getSize())
406             * av.getCharHeight();
407
408     int startRes = av.getStartRes();
409     int endRes = av.getEndRes();
410
411     if (av.hasHiddenColumns())
412     {
413       startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);
414       endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);
415     }
416
417     int startSeq = av.startSeq;
418     int endSeq = av.endSeq;
419
420     if (av.hasHiddenRows())
421     {
422       startSeq = av.getAlignment().getHiddenSequences()
423               .adjustForHiddenSeqs(startSeq);
424
425       endSeq = av.getAlignment().getHiddenSequences()
426               .adjustForHiddenSeqs(endSeq);
427
428     }
429
430     scalew = (float) width / (float) fullsizeWidth;
431     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
432
433     boxX = (int) (startRes * av.getCharWidth() * scalew);
434     boxY = (int) (startSeq * av.getCharHeight() * scaleh);
435
436     if (av.hasHiddenColumns())
437     {
438       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
439     }
440     else
441     {
442       boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);
443     }
444
445     boxHeight = (int) ((endSeq - startSeq) * av.getCharHeight() * scaleh);
446
447     repaint();
448   }
449
450   @Override
451   public void update(Graphics g)
452   {
453     paint(g);
454   }
455
456   @Override
457   public void paint(Graphics g)
458   {
459     Graphics og = offscreen.getGraphics();
460     if (miniMe != null)
461     {
462       og.drawImage(miniMe, 0, 0, this);
463       og.setColor(Color.red);
464       og.drawRect(boxX, boxY, boxWidth, boxHeight);
465       og.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
466       g.drawImage(offscreen, 0, 0, this);
467     }
468   }
469
470 }