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