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