6c968a259c8042c1d73253037bf3fa8e40292eba
[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 = 30;\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     /**\r
51      * Creates a new OverviewPanel object.\r
52      *\r
53      * @param ap DOCUMENT ME!\r
54      */\r
55     public OverviewPanel(AlignmentPanel ap)\r
56     {\r
57         this.av = ap.av;\r
58         this.ap = ap;\r
59         setLayout(null);\r
60 \r
61         // scale the initial size of overviewpanel to shape of alignment\r
62         float initialScale = (float) av.alignment.getWidth() / (float) av.alignment.getHeight();\r
63 \r
64         if (av.alignment.getWidth() > av.alignment.getHeight())\r
65         {\r
66             // wider\r
67             width = 400;\r
68             sequencesHeight = (int) (400f / initialScale);\r
69         }\r
70         else\r
71         {\r
72             // taller\r
73             width = (int) (400f * initialScale);\r
74             sequencesHeight = 300;\r
75 \r
76             if (width < 120)\r
77             {\r
78                 width = 120;\r
79             }\r
80         }\r
81 \r
82         addComponentListener(new ComponentAdapter()\r
83             {\r
84                 public void componentResized(ComponentEvent evt)\r
85                 {\r
86                     if ((getWidth() != width) ||\r
87                             (getHeight() != (sequencesHeight + graphHeight)))\r
88                     {\r
89                         updateOverviewImage();\r
90                     }\r
91                 }\r
92             });\r
93 \r
94         addMouseMotionListener(new MouseMotionAdapter()\r
95             {\r
96                 public void mouseDragged(MouseEvent evt)\r
97                 {\r
98                     doMouseDragged(evt);\r
99                 }\r
100             });\r
101 \r
102         addMouseListener(new MouseAdapter()\r
103             {\r
104                 public void mousePressed(MouseEvent evt)\r
105                 {\r
106                     doMousePressed(evt);\r
107                 }\r
108 \r
109                 public void mouseReleased(MouseEvent evt)\r
110                 {\r
111                     doMouseReleased(evt);\r
112                 }\r
113             });\r
114 \r
115         updateOverviewImage();\r
116     }\r
117 \r
118     /**\r
119      * DOCUMENT ME!\r
120      *\r
121      * @param evt DOCUMENT ME!\r
122      */\r
123     public void doMousePressed(MouseEvent evt)\r
124     {\r
125         boxX = evt.getX();\r
126         boxY = evt.getY();\r
127 \r
128         checkValid();\r
129         repaint();\r
130     }\r
131 \r
132     /**\r
133      * DOCUMENT ME!\r
134      *\r
135      * @param evt DOCUMENT ME!\r
136      */\r
137     public void doMouseReleased(MouseEvent evt)\r
138     {\r
139         boxX = evt.getX();\r
140         boxY = evt.getY();\r
141         checkValid();\r
142 \r
143         if (!resizing)\r
144         {\r
145             ap.setScrollValues((int) (boxX / scalew / av.getCharWidth()),\r
146                 (int) (boxY / scaleh / av.getCharHeight()));\r
147         }\r
148     }\r
149 \r
150     /**\r
151      * DOCUMENT ME!\r
152      *\r
153      * @param evt DOCUMENT ME!\r
154      */\r
155     public void doMouseDragged(MouseEvent evt)\r
156     {\r
157         boxX = evt.getX();\r
158         boxY = evt.getY();\r
159         checkValid();\r
160 \r
161         if (!resizing)\r
162         {\r
163             ap.setScrollValues((int) (boxX / scalew / av.getCharWidth()),\r
164                 (int) (boxY / scaleh / av.getCharHeight()));\r
165         }\r
166     }\r
167 \r
168     /**\r
169      * DOCUMENT ME!\r
170      */\r
171     void checkValid()\r
172     {\r
173         if (boxY < 0)\r
174         {\r
175             boxY = 0;\r
176         }\r
177 \r
178         if (boxY > (sequencesHeight - boxHeight))\r
179         {\r
180             boxY = sequencesHeight - boxHeight + 1;\r
181         }\r
182 \r
183         if (boxX < 0)\r
184         {\r
185             boxX = 0;\r
186         }\r
187 \r
188         if (boxX > (width - boxWidth))\r
189         {\r
190             boxX = width - boxWidth;\r
191         }\r
192     }\r
193 \r
194     /**\r
195      * DOCUMENT ME!\r
196      */\r
197     public void updateOverviewImage()\r
198     {\r
199         if (resizing)\r
200         {\r
201             resizeAgain = true;\r
202             return;\r
203         }\r
204 \r
205         resizing = true;\r
206 \r
207         Thread thread = new Thread(this);\r
208         thread.start();\r
209         repaint();\r
210     }\r
211 \r
212     // This is set true if the user resizes whilst\r
213     // the overview is being calculated\r
214     boolean resizeAgain = false;\r
215 \r
216     /**\r
217      * DOCUMENT ME!\r
218      */\r
219     public void run()\r
220     {\r
221         miniMe = null;\r
222 \r
223         int alwidth = av.alignment.getWidth();\r
224         int alheight = av.alignment.getHeight();\r
225 \r
226         if ((getWidth() > 0) && (getHeight() > 0))\r
227         {\r
228             width = getWidth();\r
229             sequencesHeight = getHeight() - graphHeight;\r
230         }\r
231 \r
232         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
233 \r
234         int fullsizeWidth = alwidth * av.getCharWidth();\r
235         int fullsizeHeight = alheight * av.getCharHeight();\r
236 \r
237         scalew = (float) width / (float) fullsizeWidth;\r
238         scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
239 \r
240         miniMe = new BufferedImage(width, sequencesHeight + graphHeight,\r
241                 BufferedImage.TYPE_INT_RGB);\r
242 \r
243         Graphics mg = miniMe.getGraphics();\r
244         BufferedImage consensus = new BufferedImage(fullsizeWidth, 60,\r
245                 BufferedImage.TYPE_3BYTE_BGR);\r
246         Graphics g = consensus.getGraphics();\r
247         ap.annotationPanel.drawGraph(g, av.conservation, fullsizeWidth, 60);\r
248         mg.drawImage(consensus, 0, sequencesHeight, width,\r
249             sequencesHeight + graphHeight, 0, 0, fullsizeWidth, 60, this);\r
250 \r
251         boolean oldRenderGaps = av.renderGaps;\r
252 \r
253         try\r
254         {\r
255             // We'll have to draw the full size alignment in chunks, as an image of the\r
256             // whole alignment requires too much memory\r
257             // Max size depends on the font size, the following is a\r
258             // guess at a size which works\r
259             int maxSize = 2000 / av.getFont().getSize();\r
260             BufferedImage block;\r
261             int blockx = 0;\r
262             int blocky = 0;\r
263             int blockw = 0;\r
264             int blockh = 0;\r
265             int eRes = 0;\r
266             int eSeq = 0;\r
267 \r
268             av.setRenderGaps(false);\r
269 \r
270             for (int sRes = 0, chunkx = 0; sRes < alwidth;\r
271                     sRes += maxSize, chunkx++)\r
272             {\r
273                 if(resizeAgain)\r
274                   break;\r
275 \r
276                 eSeq = 0;\r
277                 eRes += maxSize;\r
278 \r
279                 if (eRes > alwidth)\r
280                 {\r
281                     eRes = alwidth;\r
282                 }\r
283 \r
284                 for (int sSeq = 0, chunky = 0; sSeq < alheight;\r
285                         sSeq += maxSize, chunky++)\r
286                 {\r
287                     eSeq += maxSize;\r
288 \r
289                     if (eSeq > alheight)\r
290                     {\r
291                         eSeq = alheight;\r
292                     }\r
293 \r
294                     blocky = 0;\r
295                     blockx = (int) ((float) sRes / (float) alwidth * width);\r
296 \r
297                     block = new BufferedImage((eRes - sRes) * av.charWidth,\r
298                             (eSeq - sSeq) * av.charHeight,\r
299                             BufferedImage.TYPE_3BYTE_BGR);\r
300                     g = block.getGraphics();\r
301 \r
302                     ap.seqPanel.seqCanvas.drawPanel(g, sRes, eRes, sSeq, eSeq,\r
303                         sRes, sSeq, 0);\r
304 \r
305                     blockh = (int) ((float) (eSeq - sSeq) / (float) alheight * sequencesHeight) +\r
306                         1;\r
307                     blockw = (int) ((float) (eRes - sRes) / (float) alwidth * width) +\r
308                         1;\r
309 \r
310                     blocky += (int) ((float) sSeq / (float) alheight * sequencesHeight);\r
311 \r
312                     mg.drawImage(block, blockx, blocky, blockx + blockw,\r
313                         blocky + blockh, 0, 0, block.getWidth(),\r
314                         block.getHeight(), null);\r
315 \r
316                     block = null;\r
317                 }\r
318             }\r
319         }\r
320         catch (OutOfMemoryError error)\r
321         {\r
322             System.err.println(\r
323                 "Out of memory when trying to calculate the overview window image!");\r
324         }\r
325 \r
326         System.gc();\r
327 \r
328         av.setRenderGaps(oldRenderGaps);\r
329         resizing = false;\r
330 \r
331         setBoxPosition();\r
332 \r
333         if(resizeAgain)\r
334         {\r
335           resizeAgain = false;\r
336           updateOverviewImage();\r
337         }\r
338     }\r
339 \r
340     /**\r
341      * DOCUMENT ME!\r
342      */\r
343     public void setBoxPosition()\r
344     {\r
345         boxX = (int) (av.getStartRes() * av.getCharWidth() * scalew);\r
346         boxY = (int) (av.getStartSeq() * av.getCharHeight() * scaleh);\r
347         boxWidth = (int) ((av.getEndRes() - av.getStartRes() + 1) * av.getCharWidth() * scalew);\r
348         boxHeight = (int) (av.getEndSeq() * av.getCharHeight() * scaleh) -\r
349             boxY;\r
350         repaint();\r
351     }\r
352 \r
353     /**\r
354      * DOCUMENT ME!\r
355      *\r
356      * @param g DOCUMENT ME!\r
357      */\r
358     public void paintComponent(Graphics g)\r
359     {\r
360         g.setColor(Color.white);\r
361         g.fillRect(0, 0, getWidth(), getHeight());\r
362         g.setColor(Color.black);\r
363 \r
364         if (resizing)\r
365         {\r
366             g.setFont(new Font("Verdana", Font.BOLD, 15));\r
367             g.drawString("Recalculating", 5, sequencesHeight / 2);\r
368             g.drawString("Overview.....", 5, (sequencesHeight / 2) + 20);\r
369         }\r
370         else\r
371         {\r
372             if (miniMe != null)\r
373             {\r
374                 g.drawImage(miniMe, 0, 0, this);\r
375             }\r
376 \r
377             g.setColor(Color.red);\r
378             g.drawRect(boxX, boxY, boxWidth, boxHeight);\r
379             g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);\r
380         }\r
381     }\r
382 }\r