Optimised for wide alignments
[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         ap.setScrollValues((int) (boxX / scalew / av.getCharWidth()),\r
144                 (int) (boxY / scaleh / av.getCharHeight()));\r
145     }\r
146 \r
147     /**\r
148      * DOCUMENT ME!\r
149      *\r
150      * @param evt DOCUMENT ME!\r
151      */\r
152     public void doMouseDragged(MouseEvent evt)\r
153     {\r
154         boxX = evt.getX();\r
155         boxY = evt.getY();\r
156         checkValid();\r
157 \r
158         ap.setScrollValues( (int) (boxX / scalew / av.getCharWidth()),\r
159                            (int) (boxY / scaleh / av.getCharHeight()));\r
160        repaint();\r
161     }\r
162 \r
163     /**\r
164      * DOCUMENT ME!\r
165      */\r
166     void checkValid()\r
167     {\r
168         if (boxY < 0)\r
169         {\r
170             boxY = 0;\r
171         }\r
172 \r
173         if (boxY > (sequencesHeight - boxHeight))\r
174         {\r
175             boxY = sequencesHeight - boxHeight + 1;\r
176         }\r
177 \r
178         if (boxX < 0)\r
179         {\r
180             boxX = 0;\r
181         }\r
182 \r
183         if (boxX > (width - boxWidth))\r
184         {\r
185             boxX = width - boxWidth;\r
186         }\r
187     }\r
188 \r
189     /**\r
190      * DOCUMENT ME!\r
191      */\r
192     public void updateOverviewImage()\r
193     {\r
194         if (resizing)\r
195         {\r
196             resizeAgain = true;\r
197             return;\r
198         }\r
199 \r
200         resizing = true;\r
201 \r
202         if ( (getWidth() > 0) && (getHeight() > 0))\r
203         {\r
204           width = getWidth();\r
205           sequencesHeight = getHeight() - graphHeight;\r
206         }\r
207         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
208         setBoxPosition();\r
209 \r
210         Thread thread = new Thread(this);\r
211         thread.start();\r
212         repaint();\r
213     }\r
214 \r
215     // This is set true if the user resizes whilst\r
216     // the overview is being calculated\r
217     boolean resizeAgain = false;\r
218 \r
219     /**\r
220      * DOCUMENT ME!\r
221      */\r
222     public void run()\r
223     {\r
224         miniMe = null;\r
225 \r
226         int alwidth = av.alignment.getWidth();\r
227         int alheight = av.alignment.getHeight();\r
228 \r
229         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
230 \r
231         int fullsizeWidth = alwidth * av.getCharWidth();\r
232         int fullsizeHeight = alheight * av.getCharHeight();\r
233 \r
234         scalew = (float) width / (float) fullsizeWidth;\r
235         scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
236 \r
237         miniMe = new BufferedImage(width, sequencesHeight + graphHeight,\r
238                 BufferedImage.TYPE_INT_RGB);\r
239 \r
240 \r
241 \r
242         Graphics mg = miniMe.getGraphics();\r
243         BufferedImage consensus;\r
244         Graphics cg;\r
245         Graphics g ;\r
246 \r
247         mg.setColor(Color.white);\r
248         mg.fillRect(0, 0, getWidth(), getHeight());\r
249         mg.setColor(Color.black);\r
250         mg.setFont(new Font("Verdana", Font.BOLD, 15));\r
251         mg.drawString("Recalculating", 5, sequencesHeight / 2);\r
252         mg.drawString("Overview.....", 5, (sequencesHeight / 2) + 20);\r
253 \r
254         boolean oldRenderGaps = av.renderGaps;\r
255 \r
256         try\r
257         {\r
258             // We'll have to draw the full size alignment in chunks, as an image of the\r
259             // whole alignment requires too much memory\r
260             // Max size depends on the font size, the following is a\r
261             // guess at a size which works\r
262             int maxSize = 2000 / av.getFont().getSize();\r
263             BufferedImage block;\r
264             int blockx = 0;\r
265             int blocky = 0;\r
266             int blockw = 0;\r
267             int blockh = 0;\r
268             int eRes = 0;\r
269             int eSeq = 0;\r
270 \r
271             av.setRenderGaps(false);\r
272 \r
273             for (int sRes = 0, chunkx = 0; sRes < alwidth;\r
274                     sRes += maxSize, chunkx++)\r
275             {\r
276                 if(resizeAgain)\r
277                   break;\r
278 \r
279                 eSeq = 0;\r
280                 eRes += maxSize;\r
281 \r
282                 if (eRes > alwidth)\r
283                 {\r
284                     eRes = alwidth;\r
285                 }\r
286 \r
287                 for (int sSeq = 0, chunky = 0; sSeq < alheight;\r
288                         sSeq += maxSize, chunky++)\r
289                 {\r
290                   if (resizeAgain)\r
291                     break;\r
292                     eSeq += maxSize;\r
293 \r
294                     if (eSeq > alheight)\r
295                     {\r
296                         eSeq = alheight;\r
297                     }\r
298 \r
299                     blocky = 0;\r
300                     blockx = (int) ((float) sRes / (float) alwidth * width);\r
301 \r
302                     block = new BufferedImage((eRes - sRes) * av.charWidth,\r
303                             (eSeq - sSeq) * av.charHeight,\r
304                             BufferedImage.TYPE_3BYTE_BGR);\r
305                     g = block.getGraphics();\r
306 \r
307                     ap.seqPanel.seqCanvas.drawPanel(g, sRes, eRes, sSeq, eSeq,\r
308                         sRes, sSeq, 0);\r
309 \r
310                     blockh = (int) ((float) (eSeq - sSeq) / (float) alheight * sequencesHeight) +\r
311                         1;\r
312                     blockw = (int) ((float) (eRes - sRes) / (float) alwidth * width) +\r
313                         1;\r
314 \r
315                     blocky += (int) ((float) sSeq / (float) alheight * sequencesHeight);\r
316 \r
317                     if(av.conservation!=null)\r
318                     {\r
319                       consensus = new BufferedImage( (eRes - sRes) * av.charWidth, 60,\r
320                                                     BufferedImage.TYPE_3BYTE_BGR);\r
321                       cg = consensus.getGraphics();\r
322 \r
323                       ap.annotationPanel.drawGraph(cg, av.conservation,\r
324                                                    (eRes - sRes) * av.charWidth, 60, sRes, eRes);\r
325 \r
326                       mg.drawImage(consensus, blockx, sequencesHeight, blockx + blockw,\r
327 \r
328                                    sequencesHeight + graphHeight, 0, 0,\r
329                                    (eRes - sRes) * av.charWidth, 60, this);\r
330                     }\r
331 \r
332                     mg.drawImage(block, blockx, blocky, blockx + blockw,\r
333                         blocky + blockh, 0, 0, block.getWidth(),\r
334                         block.getHeight(), null);\r
335 \r
336                     block = null;\r
337                     repaint();\r
338                 }\r
339 \r
340 \r
341             }\r
342         }\r
343         catch (OutOfMemoryError error)\r
344         {\r
345             System.err.println(\r
346                 "Out of memory when trying to calculate the overview window image!");\r
347         }\r
348 \r
349         System.gc();\r
350 \r
351         av.setRenderGaps(oldRenderGaps);\r
352         resizing = false;\r
353 \r
354         setBoxPosition();\r
355 \r
356         if(resizeAgain)\r
357         {\r
358           resizeAgain = false;\r
359           updateOverviewImage();\r
360         }\r
361     }\r
362 \r
363     /**\r
364      * DOCUMENT ME!\r
365      */\r
366     public void setBoxPosition()\r
367     {\r
368       int fullsizeWidth = av.alignment.getWidth() * av.getCharWidth();\r
369       int fullsizeHeight = av.alignment.getHeight() * av.getCharHeight();\r
370 \r
371       scalew = (float) width / (float) fullsizeWidth;\r
372       scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
373 \r
374         boxX = (int) (av.getStartRes() * av.getCharWidth() * scalew);\r
375         boxY = (int) (av.getStartSeq() * av.getCharHeight() * scaleh);\r
376         boxWidth = (int) ((av.getEndRes() - av.getStartRes() + 1) * av.getCharWidth() * scalew);\r
377         boxHeight = (int) (av.getEndSeq() * av.getCharHeight() * scaleh) -\r
378             boxY;\r
379         repaint();\r
380     }\r
381 \r
382     /**\r
383      * DOCUMENT ME!\r
384      *\r
385      * @param g DOCUMENT ME!\r
386      */\r
387     public void paintComponent(Graphics g)\r
388     {\r
389         if (miniMe != null)\r
390         {\r
391           g.drawImage(miniMe, 0, 0, this);\r
392         }\r
393         else\r
394         {\r
395           g.setColor(Color.white);\r
396           g.fillRect(0, 0, getWidth(), getHeight());\r
397           g.setColor(Color.black);\r
398           g.setFont(new Font("Verdana", Font.BOLD, 15));\r
399           g.drawString("Recalculating", 5, sequencesHeight / 2);\r
400           g.drawString("Overview.....", 5, (sequencesHeight / 2) + 20);\r
401         }\r
402 \r
403         g.setColor(Color.red);\r
404         g.drawRect(boxX, boxY, boxWidth, boxHeight);\r
405         g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);\r
406 \r
407     }\r
408 }\r