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