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