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