Faster
[jalview.git] / src / jalview / gui / OverviewPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.gui;\r
20 \r
21 import java.awt.*;\r
22 import java.awt.event.*;\r
23 import java.awt.image.*;\r
24 \r
25 import javax.swing.*;\r
26 \r
27 \r
28 /**\r
29  * DOCUMENT ME!\r
30  *\r
31  * @author $author$\r
32  * @version $Revision$\r
33  */\r
34 public class OverviewPanel extends JPanel implements Runnable\r
35 {\r
36     BufferedImage miniMe;\r
37     AlignViewport av;\r
38     AlignmentPanel ap;\r
39     float scalew = 1f;\r
40     float scaleh = 1f;\r
41     int width;\r
42     int sequencesHeight;\r
43     int graphHeight = 20;\r
44     int boxX = -1;\r
45     int boxY = -1;\r
46     int boxWidth = -1;\r
47     int boxHeight = -1;\r
48     boolean resizing = false;\r
49 \r
50     // Can set different properties in this seqCanvas than\r
51     // main visible SeqCanvas\r
52     SequenceRenderer sr;\r
53     FeatureRenderer fr;\r
54 \r
55     /**\r
56      * Creates a new OverviewPanel object.\r
57      *\r
58      * @param ap DOCUMENT ME!\r
59      */\r
60     public OverviewPanel(AlignmentPanel ap)\r
61     {\r
62         this.av = ap.av;\r
63         this.ap = ap;\r
64         setLayout(null);\r
65 \r
66         sr = new SequenceRenderer(av);\r
67         sr.renderGaps( false );\r
68         sr.forOverview = true;\r
69         fr = new FeatureRenderer(av);\r
70         fr.overview = true;\r
71 \r
72 \r
73         // scale the initial size of overviewpanel to shape of alignment\r
74         float initialScale = (float) av.alignment.getWidth() / (float) av.alignment.getHeight();\r
75 \r
76         if(av.conservation==null)\r
77           graphHeight = 0;\r
78 \r
79 \r
80         if (av.alignment.getWidth() > av.alignment.getHeight())\r
81         {\r
82             // wider\r
83             width = 400;\r
84             sequencesHeight = (int) (400f / initialScale);\r
85             if(sequencesHeight<40)\r
86               sequencesHeight = 40;\r
87         }\r
88         else\r
89         {\r
90             // taller\r
91             width = (int) (400f * initialScale);\r
92             sequencesHeight = 300;\r
93 \r
94             if (width < 120)\r
95             {\r
96                 width = 120;\r
97             }\r
98         }\r
99 \r
100         addComponentListener(new ComponentAdapter()\r
101             {\r
102                 public void componentResized(ComponentEvent evt)\r
103                 {\r
104                     if ((getWidth() != width) ||\r
105                             (getHeight() != (sequencesHeight + graphHeight)))\r
106                     {\r
107                         updateOverviewImage();\r
108                     }\r
109                 }\r
110             });\r
111 \r
112         addMouseMotionListener(new MouseMotionAdapter()\r
113             {\r
114                 public void mouseDragged(MouseEvent evt)\r
115                 {\r
116                   if(!av.wrapAlignment)\r
117                     doMouseDragged(evt);\r
118                 }\r
119             });\r
120 \r
121         addMouseListener(new MouseAdapter()\r
122             {\r
123                 public void mousePressed(MouseEvent evt)\r
124                 {\r
125                   if(!av.wrapAlignment)\r
126                     doMousePressed(evt);\r
127                 }\r
128 \r
129                 public void mouseReleased(MouseEvent evt)\r
130                 {\r
131                   if(!av.wrapAlignment)\r
132                     doMouseReleased(evt);\r
133                 }\r
134             });\r
135 \r
136         updateOverviewImage();\r
137     }\r
138 \r
139     /**\r
140      * DOCUMENT ME!\r
141      *\r
142      * @param evt DOCUMENT ME!\r
143      */\r
144     public void doMousePressed(MouseEvent evt)\r
145     {\r
146         boxX = evt.getX();\r
147         boxY = evt.getY();\r
148 \r
149         checkValid();\r
150         repaint();\r
151     }\r
152 \r
153     /**\r
154      * DOCUMENT ME!\r
155      *\r
156      * @param evt DOCUMENT ME!\r
157      */\r
158     public void doMouseReleased(MouseEvent evt)\r
159     {\r
160         boxX = evt.getX();\r
161         boxY = evt.getY();\r
162         checkValid();\r
163 \r
164         if(av.hasHiddenColumns)\r
165         {\r
166           int col = (int) ( boxX / scalew / av.getCharWidth());\r
167 \r
168           if(av.getColumnSelection().isVisible(col))\r
169           {\r
170             ap.setScrollValues(\r
171                 av.getColumnSelection().findColumnPosition(col),\r
172                 (int) (boxY / scaleh / av.getCharHeight()));\r
173           }\r
174           else\r
175             System.out.println(col +" not visible");\r
176         }\r
177         else\r
178           ap.setScrollValues( (int) (boxX / scalew / av.getCharWidth()),\r
179                             (int) (boxY / scaleh / av.getCharHeight()));\r
180     }\r
181 \r
182     /**\r
183      * DOCUMENT ME!\r
184      *\r
185      * @param evt DOCUMENT ME!\r
186      */\r
187     public void doMouseDragged(MouseEvent evt)\r
188     {\r
189         boxX = evt.getX();\r
190         boxY = evt.getY();\r
191         checkValid();\r
192 \r
193         if(av.hasHiddenColumns)\r
194         {\r
195           int col = (int) ( boxX / scalew / av.getCharWidth());\r
196 \r
197           if(!av.getColumnSelection().isVisible(col))\r
198           {\r
199             return;\r
200           }\r
201 \r
202             ap.setScrollValues(\r
203                av.getColumnSelection().findColumnPosition( col ),\r
204                 (int) (boxY / scaleh / av.getCharHeight()));\r
205         }\r
206         else\r
207         ap.setScrollValues( (int) (boxX / scalew / av.getCharWidth()),\r
208                             (int) (boxY / scaleh / av.getCharHeight()));\r
209        repaint();\r
210     }\r
211 \r
212     /**\r
213      * DOCUMENT ME!\r
214      */\r
215     void checkValid()\r
216     {\r
217         if (boxY < 0)\r
218         {\r
219             boxY = 0;\r
220         }\r
221 \r
222         if (boxY > (sequencesHeight - boxHeight))\r
223         {\r
224             boxY = sequencesHeight - boxHeight + 1;\r
225         }\r
226 \r
227         if (boxX < 0)\r
228         {\r
229             boxX = 0;\r
230         }\r
231 \r
232         if (boxX > (width - boxWidth))\r
233         {\r
234           if(av.hasHiddenColumns)\r
235           {\r
236             //Try smallest possible box\r
237             boxWidth = (int) ( (av.endRes - av.startRes + 1) *\r
238                                    av.getCharWidth() * scalew);\r
239           }\r
240 \r
241           boxX = width - boxWidth;\r
242         }\r
243     }\r
244 \r
245     /**\r
246      * DOCUMENT ME!\r
247      */\r
248     public void updateOverviewImage()\r
249     {\r
250         if (resizing)\r
251         {\r
252             resizeAgain = true;\r
253             return;\r
254         }\r
255 \r
256         resizing = true;\r
257 \r
258         if ( (getWidth() > 0) && (getHeight() > 0))\r
259         {\r
260           width = getWidth();\r
261           sequencesHeight = getHeight() - graphHeight;\r
262         }\r
263 \r
264         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
265        // setBoxPosition();\r
266 \r
267         Thread thread = new Thread(this);\r
268         thread.start();\r
269         repaint();\r
270     }\r
271 \r
272     // This is set true if the user resizes whilst\r
273     // the overview is being calculated\r
274     boolean resizeAgain = false;\r
275 \r
276     /**\r
277      * DOCUMENT ME!\r
278      */\r
279     public void run()\r
280     {\r
281         miniMe = null;\r
282 \r
283        if (av.showSequenceFeatures)\r
284        {\r
285          fr.featureGroups = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups;\r
286          fr.featureColours = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours;\r
287          fr.transparency = ap.seqPanel.seqCanvas.getFeatureRenderer().transparency;\r
288          fr.sequenceFeatures = ap.seqPanel.seqCanvas.getFeatureRenderer().sequenceFeatures;\r
289        }\r
290 \r
291         int alwidth = av.alignment.getWidth();\r
292         int alheight = av.alignment.getHeight()\r
293             +av.alignment.getHiddenSequences().getSize();\r
294 \r
295         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
296 \r
297         int fullsizeWidth = alwidth * av.getCharWidth();\r
298         int fullsizeHeight = alheight * av.getCharHeight();\r
299 \r
300         scalew = (float) width / (float) fullsizeWidth;\r
301         scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
302 \r
303         miniMe = new BufferedImage(width, sequencesHeight + graphHeight,\r
304                 BufferedImage.TYPE_INT_RGB);\r
305 \r
306 \r
307         Graphics mg = miniMe.getGraphics();\r
308         mg.setColor(Color.orange);\r
309         mg.fillRect(0,0,width, miniMe.getHeight());\r
310 \r
311         float sampleCol = (float) alwidth / (float) width;\r
312         float sampleRow = (float) alheight / (float) sequencesHeight;\r
313 \r
314         int lastcol=-1, lastrow=-1;\r
315         int color = Color.white.getRGB();\r
316         int row, col;\r
317         jalview.datamodel.SequenceI seq;\r
318         boolean hiddenRow = false;\r
319         for (row = 0; row < sequencesHeight; row++)\r
320         {\r
321           if((int)(row*sampleRow)==lastrow)\r
322           {\r
323             //No need to recalculate the colours,\r
324             //Just copy from the row above\r
325             for (col = 0; col < width; col++)\r
326             {\r
327               miniMe.setRGB(col, row, miniMe.getRGB(col, row-1));\r
328             }\r
329             continue;\r
330           }\r
331 \r
332           lastrow = (int)(row*sampleRow);\r
333 \r
334           hiddenRow = false;\r
335           if (av.hasHiddenRows)\r
336           {\r
337             seq = av.alignment.getHiddenSequences().getHiddenSequence(lastrow);\r
338             if (seq == null)\r
339             {\r
340 \r
341               int index =\r
342                  av.alignment.getHiddenSequences().findIndexWithoutHiddenSeqs(lastrow);\r
343 \r
344 \r
345              seq = av.alignment.getSequenceAt(index);\r
346             }\r
347             else\r
348             {\r
349               hiddenRow = true;\r
350             }\r
351           }\r
352           else\r
353             seq = av.alignment.getSequenceAt(lastrow);\r
354 \r
355           if(seq==null)\r
356           {\r
357             System.out.println(lastrow+" null");\r
358             continue;\r
359           }\r
360 \r
361             for (col = 0; col < width; col++)\r
362             {\r
363             if((int)(col*sampleCol) == lastcol && (int)(row*sampleRow)==lastrow)\r
364             {\r
365               miniMe.setRGB(col,row,color);\r
366               continue;\r
367             }\r
368 \r
369 \r
370             lastcol = (int)(col*sampleCol);\r
371 \r
372             if (seq.getLength() > lastcol)\r
373             {\r
374               color = sr.getResidueBoxColour(\r
375                   seq, lastcol).getRGB();\r
376 \r
377               if (av.showSequenceFeatures)\r
378                 color = fr.findFeatureColour(color, lastrow, lastcol);\r
379             }\r
380             else\r
381             {\r
382               color = -1; //White\r
383             }\r
384 \r
385             if(hiddenRow ||\r
386                (av.hasHiddenColumns && !av.getColumnSelection().isVisible(lastcol)))\r
387             {\r
388               color = new Color(color).darker().darker().getRGB();\r
389             }\r
390 \r
391 \r
392             miniMe.setRGB(col,row,color);\r
393 \r
394 \r
395           }\r
396         }\r
397 \r
398         if (av.conservation != null)\r
399         {\r
400           for (col = 0; col < width; col++)\r
401           {\r
402             lastcol = (int) (col * sampleCol);\r
403             {\r
404               mg.translate(col, sequencesHeight);\r
405               ap.annotationPanel.drawGraph(mg, av.conservation,\r
406                                            (int) (sampleCol) + 1,\r
407                                            graphHeight,\r
408                                            (int) (col * sampleCol),\r
409                                            (int) (col * sampleCol) + 1);\r
410               mg.translate( -col, -sequencesHeight);\r
411             }\r
412           }\r
413         }\r
414         System.gc();\r
415 \r
416         resizing = false;\r
417 \r
418         setBoxPosition();\r
419 \r
420         if(resizeAgain)\r
421         {\r
422           resizeAgain = false;\r
423           updateOverviewImage();\r
424         }\r
425     }\r
426 \r
427     /**\r
428      * DOCUMENT ME!\r
429      */\r
430     public void setBoxPosition()\r
431     {\r
432       int fullsizeWidth = av.alignment.getWidth() * av.getCharWidth();\r
433       int fullsizeHeight = (av.alignment.getHeight()\r
434                    +av.alignment.getHiddenSequences().getSize()) * av.getCharHeight();\r
435 \r
436       int startRes = av.getStartRes();\r
437       int endRes = av.getEndRes();\r
438 \r
439       if(av.hasHiddenColumns)\r
440       {\r
441         startRes = av.getColumnSelection().adjustForHiddenColumns(startRes);\r
442         endRes = av.getColumnSelection().adjustForHiddenColumns(endRes);\r
443       }\r
444 \r
445 \r
446       scalew = (float) width / (float) fullsizeWidth;\r
447       scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
448 \r
449         boxX = (int) (startRes * av.getCharWidth() * scalew);\r
450         boxY = (int) (av.getStartSeq() * av.getCharHeight() * scaleh);\r
451 \r
452         if(av.hasHiddenColumns)\r
453           boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);\r
454         else\r
455           boxWidth = (int) ((endRes - startRes + 1) * av.getCharWidth() * scalew);\r
456 \r
457         boxHeight = (int) (av.getEndSeq() * av.getCharHeight() * scaleh) -\r
458             boxY;\r
459         repaint();\r
460     }\r
461 \r
462     /**\r
463      * DOCUMENT ME!\r
464      *\r
465      * @param g DOCUMENT ME!\r
466      */\r
467     public void paintComponent(Graphics g)\r
468     {\r
469         if (miniMe != null && !resizing)\r
470         {\r
471           g.drawImage(miniMe, 0, 0, this);\r
472         }\r
473         else\r
474         {\r
475           g.setColor(Color.white);\r
476           g.fillRect(0, 0, getWidth(), getHeight());\r
477           g.setColor(Color.black);\r
478           g.setFont(new Font("Verdana", Font.BOLD, 15));\r
479           g.drawString("Recalculating", 5, sequencesHeight / 2);\r
480           g.drawString("Overview.....", 5, (sequencesHeight / 2) + 20);\r
481         }\r
482 \r
483 \r
484         g.setColor(Color.red);\r
485         g.drawRect(boxX, boxY, boxWidth, boxHeight);\r
486         g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);\r
487 \r
488     }\r
489 }\r