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