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