apply gpl development license
[jalview.git] / src / jalview / gui / OverviewPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.awt.*;
22 import java.awt.event.*;
23 import java.awt.image.*;
24 import javax.swing.*;
25
26 /**
27  * DOCUMENT ME!
28  * 
29  * @author $author$
30  * @version $Revision$
31  */
32 public class OverviewPanel extends JPanel implements Runnable
33 {
34   BufferedImage miniMe;
35
36   AlignViewport av;
37
38   AlignmentPanel ap;
39
40   float scalew = 1f;
41
42   float scaleh = 1f;
43
44   int width;
45
46   int sequencesHeight;
47
48   int graphHeight = 20;
49
50   int boxX = -1;
51
52   int boxY = -1;
53
54   int boxWidth = -1;
55
56   int 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   /**
67    * Creates a new OverviewPanel object.
68    * 
69    * @param ap
70    *                DOCUMENT ME!
71    */
72   public OverviewPanel(AlignmentPanel ap)
73   {
74     this.av = ap.av;
75     this.ap = ap;
76     setLayout(null);
77
78     sr = new SequenceRenderer(av);
79     sr.renderGaps = false;
80     sr.forOverview = true;
81     fr = new FeatureRenderer(ap);
82
83     // scale the initial size of overviewpanel to shape of alignment
84     float initialScale = (float) av.alignment.getWidth()
85             / (float) av.alignment.getHeight();
86
87     if (av.conservation == null)
88     {
89       graphHeight = 0;
90     }
91
92     if (av.alignment.getWidth() > av.alignment.getHeight())
93     {
94       // wider
95       width = 400;
96       sequencesHeight = (int) (400f / initialScale);
97       if (sequencesHeight < 40)
98       {
99         sequencesHeight = 40;
100       }
101     }
102     else
103     {
104       // taller
105       width = (int) (400f * initialScale);
106       sequencesHeight = 300;
107
108       if (width < 120)
109       {
110         width = 120;
111       }
112     }
113
114     addComponentListener(new ComponentAdapter()
115     {
116       public void componentResized(ComponentEvent evt)
117       {
118         if ((getWidth() != width)
119                 || (getHeight() != (sequencesHeight + graphHeight)))
120         {
121           updateOverviewImage();
122         }
123       }
124     });
125
126     addMouseMotionListener(new MouseMotionAdapter()
127     {
128       public void mouseDragged(MouseEvent evt)
129       {
130         if (!av.wrapAlignment)
131         {
132           boxX = evt.getX();
133           boxY = evt.getY();
134           checkValid();
135         }
136       }
137     });
138
139     addMouseListener(new MouseAdapter()
140     {
141       public void mousePressed(MouseEvent evt)
142       {
143         if (!av.wrapAlignment)
144         {
145           boxX = evt.getX();
146           boxY = evt.getY();
147           checkValid();
148         }
149       }
150     });
151
152     updateOverviewImage();
153   }
154
155   /**
156    * DOCUMENT ME!
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
206   }
207
208   /**
209    * DOCUMENT ME!
210    */
211   public void updateOverviewImage()
212   {
213     if (resizing)
214     {
215       resizeAgain = true;
216       return;
217     }
218
219     resizing = true;
220
221     if ((getWidth() > 0) && (getHeight() > 0))
222     {
223       width = getWidth();
224       sequencesHeight = getHeight() - graphHeight;
225     }
226
227     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
228
229     Thread thread = new Thread(this);
230     thread.start();
231     repaint();
232   }
233
234   // This is set true if the user resizes whilst
235   // the overview is being calculated
236   boolean resizeAgain = false;
237
238   /**
239    * DOCUMENT ME!
240    */
241   public void run()
242   {
243     miniMe = null;
244
245     if (av.showSequenceFeatures)
246     {
247       fr.transferSettings(ap.seqPanel.seqCanvas.getFeatureRenderer());
248     }
249
250     int alwidth = av.alignment.getWidth();
251     int alheight = av.alignment.getHeight()
252             + av.alignment.getHiddenSequences().getSize();
253
254     setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));
255
256     int fullsizeWidth = alwidth * av.getCharWidth();
257     int fullsizeHeight = alheight * av.getCharHeight();
258
259     scalew = (float) width / (float) fullsizeWidth;
260     scaleh = (float) sequencesHeight / (float) fullsizeHeight;
261
262     miniMe = new BufferedImage(width, sequencesHeight + graphHeight,
263             BufferedImage.TYPE_INT_RGB);
264
265     Graphics mg = miniMe.getGraphics();
266     mg.setColor(Color.orange);
267     mg.fillRect(0, 0, width, miniMe.getHeight());
268
269     float sampleCol = (float) alwidth / (float) width;
270     float sampleRow = (float) alheight / (float) sequencesHeight;
271
272     int lastcol = -1, lastrow = -1;
273     int color = Color.white.getRGB();
274     int row, col;
275     jalview.datamodel.SequenceI seq;
276     boolean hiddenRow = false;
277     for (row = 0; row < sequencesHeight; row++)
278     {
279       if ((int) (row * sampleRow) == lastrow)
280       {
281         // No need to recalculate the colours,
282         // Just copy from the row above
283         for (col = 0; col < width; col++)
284         {
285           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
286         }
287         continue;
288       }
289
290       lastrow = (int) (row * sampleRow);
291
292       hiddenRow = false;
293       if (av.hasHiddenRows)
294       {
295         seq = av.alignment.getHiddenSequences().getHiddenSequence(lastrow);
296         if (seq == null)
297         {
298           int index = av.alignment.getHiddenSequences()
299                   .findIndexWithoutHiddenSeqs(lastrow);
300
301           seq = av.alignment.getSequenceAt(index);
302         }
303         else
304         {
305           hiddenRow = true;
306         }
307       }
308       else
309       {
310         seq = av.alignment.getSequenceAt(lastrow);
311       }
312
313       if (seq == null)
314       {
315         System.out.println(lastrow + " null");
316         continue;
317       }
318
319       for (col = 0; col < width; col++)
320       {
321         if ((int) (col * sampleCol) == lastcol
322                 && (int) (row * sampleRow) == lastrow)
323         {
324           miniMe.setRGB(col, row, color);
325           continue;
326         }
327
328         lastcol = (int) (col * sampleCol);
329
330         if (seq.getLength() > lastcol)
331         {
332           color = sr.getResidueBoxColour(seq, lastcol).getRGB();
333
334           if (av.showSequenceFeatures)
335           {
336             color = fr.findFeatureColour(color, seq, lastcol);
337           }
338         }
339         else
340         {
341           color = -1; // White
342         }
343
344         if (hiddenRow
345                 || (av.hasHiddenColumns && !av.getColumnSelection()
346                         .isVisible(lastcol)))
347         {
348           color = new Color(color).darker().darker().getRGB();
349         }
350
351         miniMe.setRGB(col, row, color);
352
353       }
354     }
355
356     if (av.conservation != null)
357     {
358       for (col = 0; col < width; col++)
359       {
360         lastcol = (int) (col * sampleCol);
361         {
362           mg.translate(col, sequencesHeight);
363           ap.annotationPanel.drawGraph(mg, av.conservation,
364                   (int) (sampleCol) + 1, graphHeight,
365                   (int) (col * sampleCol), (int) (col * sampleCol) + 1);
366           mg.translate(-col, -sequencesHeight);
367         }
368       }
369     }
370     System.gc();
371
372     resizing = false;
373
374     setBoxPosition();
375
376     if (resizeAgain)
377     {
378       resizeAgain = false;
379       updateOverviewImage();
380     }
381   }
382
383   /**
384    * DOCUMENT ME!
385    */
386   public void setBoxPosition()
387   {
388     int fullsizeWidth = av.alignment.getWidth() * av.getCharWidth();
389     int fullsizeHeight = (av.alignment.getHeight() + av.alignment
390             .getHiddenSequences().getSize())
391             * 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   /**
436    * DOCUMENT ME!
437    * 
438    * @param g
439    *                DOCUMENT ME!
440    */
441   public void paintComponent(Graphics g)
442   {
443     if (resizing)
444     {
445       g.setColor(Color.white);
446       g.fillRect(0, 0, getWidth(), getHeight());
447     }
448     else if (miniMe != null)
449     {
450       g.drawImage(miniMe, 0, 0, this);
451     }
452
453     g.setColor(Color.red);
454     g.drawRect(boxX, boxY, boxWidth, boxHeight);
455     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
456
457   }
458 }