added help and documentation for annotation editing.
[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             return;\r
202         }\r
203 \r
204         resizing = true;\r
205 \r
206         Thread thread = new Thread(this);\r
207         thread.start();\r
208         repaint();\r
209     }\r
210 \r
211     /**\r
212      * DOCUMENT ME!\r
213      */\r
214     public void run()\r
215     {\r
216         miniMe = null;\r
217 \r
218         int alwidth = av.alignment.getWidth();\r
219         int alheight = av.alignment.getHeight();\r
220 \r
221         if ((getWidth() > 0) && (getHeight() > 0))\r
222         {\r
223             width = getWidth();\r
224             sequencesHeight = getHeight() - graphHeight;\r
225         }\r
226 \r
227         setPreferredSize(new Dimension(width, sequencesHeight + graphHeight));\r
228 \r
229         int fullsizeWidth = alwidth * av.getCharWidth();\r
230         int fullsizeHeight = alheight * av.getCharHeight();\r
231 \r
232         scalew = (float) width / (float) fullsizeWidth;\r
233         scaleh = (float) sequencesHeight / (float) fullsizeHeight;\r
234 \r
235         miniMe = new BufferedImage(width, sequencesHeight + graphHeight,\r
236                 BufferedImage.TYPE_INT_RGB);\r
237 \r
238         Graphics mg = miniMe.getGraphics();\r
239         BufferedImage consensus = new BufferedImage(fullsizeWidth, 60,\r
240                 BufferedImage.TYPE_3BYTE_BGR);\r
241         Graphics g = consensus.getGraphics();\r
242         ap.annotationPanel.drawGraph(g, av.conservation, fullsizeWidth, 60);\r
243         mg.drawImage(consensus, 0, sequencesHeight, width,\r
244             sequencesHeight + graphHeight, 0, 0, fullsizeWidth, 60, this);\r
245 \r
246         boolean oldRenderGaps = av.renderGaps;\r
247 \r
248         try\r
249         {\r
250             // We'll have to draw the full size alignment in chunks, as an image of the\r
251             // whole alignment requires too much memory\r
252             // Max size depends on the font size, the following is a\r
253             // guess at a size which works\r
254             int maxSize = 2000 / av.getFont().getSize();\r
255             BufferedImage block;\r
256             int blockx = 0;\r
257             int blocky = 0;\r
258             int blockw = 0;\r
259             int blockh = 0;\r
260             int eRes = 0;\r
261             int eSeq = 0;\r
262 \r
263             av.setRenderGaps(false);\r
264 \r
265             for (int sRes = 0, chunkx = 0; sRes < alwidth;\r
266                     sRes += maxSize, chunkx++)\r
267             {\r
268                 eSeq = 0;\r
269                 eRes += maxSize;\r
270 \r
271                 if (eRes > alwidth)\r
272                 {\r
273                     eRes = alwidth;\r
274                 }\r
275 \r
276                 for (int sSeq = 0, chunky = 0; sSeq < alheight;\r
277                         sSeq += maxSize, chunky++)\r
278                 {\r
279                     eSeq += maxSize;\r
280 \r
281                     if (eSeq > alheight)\r
282                     {\r
283                         eSeq = alheight;\r
284                     }\r
285 \r
286                     blocky = 0;\r
287                     blockx = (int) ((float) sRes / (float) alwidth * width);\r
288 \r
289                     block = new BufferedImage((eRes - sRes) * av.charWidth,\r
290                             (eSeq - sSeq) * av.charHeight,\r
291                             BufferedImage.TYPE_3BYTE_BGR);\r
292                     g = block.getGraphics();\r
293 \r
294                     ap.seqPanel.seqCanvas.drawPanel(g, sRes, eRes, sSeq, eSeq,\r
295                         sRes, sSeq, 0);\r
296 \r
297                     blockh = (int) ((float) (eSeq - sSeq) / (float) alheight * sequencesHeight) +\r
298                         1;\r
299                     blockw = (int) ((float) (eRes - sRes) / (float) alwidth * width) +\r
300                         1;\r
301 \r
302                     blocky += (int) ((float) sSeq / (float) alheight * sequencesHeight);\r
303 \r
304                     mg.drawImage(block, blockx, blocky, blockx + blockw,\r
305                         blocky + blockh, 0, 0, block.getWidth(),\r
306                         block.getHeight(), this);\r
307 \r
308                     block = null;\r
309                 }\r
310             }\r
311         }\r
312         catch (OutOfMemoryError error)\r
313         {\r
314             System.err.println(\r
315                 "Out of memory when trying to calculate the overview window image!");\r
316         }\r
317 \r
318         av.setRenderGaps(oldRenderGaps);\r
319         resizing = false;\r
320 \r
321         setBoxPosition();\r
322     }\r
323 \r
324     /**\r
325      * DOCUMENT ME!\r
326      */\r
327     public void setBoxPosition()\r
328     {\r
329         boxX = (int) (av.getStartRes() * av.getCharWidth() * scalew);\r
330         boxY = (int) (av.getStartSeq() * av.getCharHeight() * scaleh);\r
331         boxWidth = (int) ((av.getEndRes() - av.getStartRes() + 1) * av.getCharWidth() * scalew);\r
332         boxHeight = (int) (av.getEndSeq() * av.getCharHeight() * scaleh) -\r
333             boxY;\r
334         repaint();\r
335     }\r
336 \r
337     /**\r
338      * DOCUMENT ME!\r
339      *\r
340      * @param g DOCUMENT ME!\r
341      */\r
342     public void paintComponent(Graphics g)\r
343     {\r
344         g.setColor(Color.white);\r
345         g.fillRect(0, 0, getWidth(), getHeight());\r
346         g.setColor(Color.black);\r
347 \r
348         if (resizing)\r
349         {\r
350             g.setFont(new Font("Verdana", Font.BOLD, 15));\r
351             g.drawString("Recalculating", 5, sequencesHeight / 2);\r
352             g.drawString("Overview.....", 5, (sequencesHeight / 2) + 20);\r
353         }\r
354         else\r
355         {\r
356             if (miniMe != null)\r
357             {\r
358                 g.drawImage(miniMe, 0, 0, this);\r
359             }\r
360 \r
361             g.setColor(Color.red);\r
362             g.drawRect(boxX, boxY, boxWidth, boxHeight);\r
363             g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);\r
364         }\r
365     }\r
366 }\r