Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
OverviewPanel | 59 | 109 | 46 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.gui; | |
22 | ||
23 | import java.awt.BorderLayout; | |
24 | import java.awt.Cursor; | |
25 | import java.awt.Dimension; | |
26 | import java.awt.Rectangle; | |
27 | import java.awt.event.ActionEvent; | |
28 | import java.awt.event.ActionListener; | |
29 | import java.awt.event.ComponentAdapter; | |
30 | import java.awt.event.ComponentEvent; | |
31 | import java.awt.event.MouseAdapter; | |
32 | import java.awt.event.MouseEvent; | |
33 | import java.awt.event.MouseMotionAdapter; | |
34 | import java.beans.PropertyChangeEvent; | |
35 | import java.beans.PropertyVetoException; | |
36 | ||
37 | import javax.swing.JCheckBoxMenuItem; | |
38 | import javax.swing.JInternalFrame; | |
39 | import javax.swing.JPanel; | |
40 | import javax.swing.JPopupMenu; | |
41 | import javax.swing.SwingUtilities; | |
42 | ||
43 | import jalview.renderer.OverviewRenderer; | |
44 | import jalview.util.MessageManager; | |
45 | import jalview.util.Platform; | |
46 | import jalview.viewmodel.OverviewDimensions; | |
47 | import jalview.viewmodel.OverviewDimensionsHideHidden; | |
48 | import jalview.viewmodel.OverviewDimensionsShowHidden; | |
49 | import jalview.viewmodel.ViewportListenerI; | |
50 | ||
51 | /** | |
52 | * Panel displaying an overview of the full alignment, with an interactive box | |
53 | * representing the viewport onto the alignment. | |
54 | * | |
55 | * @author $author$ | |
56 | * @version $Revision$ | |
57 | */ | |
58 | @SuppressWarnings("serial") | |
59 | public class OverviewPanel extends JPanel | |
60 | implements Runnable, ViewportListenerI | |
61 | { | |
62 | protected OverviewDimensions od; | |
63 | ||
64 | private OverviewCanvas oviewCanvas; | |
65 | ||
66 | protected AlignViewport av; | |
67 | ||
68 | private AlignmentPanel ap; | |
69 | ||
70 | private JInternalFrame internalFrame; | |
71 | ||
72 | protected JCheckBoxMenuItem displayToggle; | |
73 | ||
74 | protected boolean showHidden = true; | |
75 | ||
76 | protected boolean draggingBox = false; | |
77 | ||
78 | protected ProgressPanel progressPanel; | |
79 | ||
80 | /** | |
81 | * Creates a new OverviewPanel object. | |
82 | * | |
83 | * @param alPanel | |
84 | * The alignment panel which is shown in the overview panel | |
85 | * @param frame | |
86 | * @param isShowHidden | |
87 | * TODO | |
88 | */ | |
89 | 43 | public OverviewPanel(AlignmentPanel alPanel, JInternalFrame frame, |
90 | boolean isShowHidden) | |
91 | { | |
92 | 43 | this.av = alPanel.av; |
93 | 43 | this.ap = alPanel; |
94 | 43 | this.internalFrame = frame; |
95 | ||
96 | 43 | showHidden = isShowHidden; |
97 | 43 | if (showHidden) |
98 | { | |
99 | 6 | od = new OverviewDimensionsShowHidden(av.getRanges(), |
100 | (av.isShowAnnotation() | |
101 | && av.getAlignmentConservationAnnotation() != null)); | |
102 | } | |
103 | else | |
104 | { | |
105 | 37 | od = new OverviewDimensionsHideHidden(av.getRanges(), |
106 | (av.isShowAnnotation() | |
107 | && av.getAlignmentConservationAnnotation() != null)); | |
108 | } | |
109 | ||
110 | 43 | setLayout(new BorderLayout()); |
111 | 43 | progressPanel = new ProgressPanel(OverviewRenderer.UPDATE, |
112 | MessageManager.getString("label.oview_calc"), getWidth()); | |
113 | 43 | this.add(progressPanel, BorderLayout.SOUTH); |
114 | 43 | oviewCanvas = new OverviewCanvas(od, av, progressPanel); |
115 | ||
116 | 43 | add(oviewCanvas, BorderLayout.CENTER); |
117 | ||
118 | 43 | av.getRanges().addPropertyChangeListener(this); |
119 | ||
120 | // without this the overview window does not size to fit the overview canvas | |
121 | 43 | setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); |
122 | ||
123 | 43 | addComponentListener(new ComponentAdapter() |
124 | { | |
125 | 98 | @Override |
126 | public void componentResized(ComponentEvent evt) | |
127 | { | |
128 | // Resize is called on the initial display of the overview. | |
129 | // This code adjusts sizes to account for the progress bar if it has not | |
130 | // already been accounted for, which triggers another resize call for | |
131 | // the correct sizing, at which point the overview image is updated. | |
132 | // (This avoids a double recalculation of the image.) | |
133 | 98 | if (getWidth() == od.getWidth() && getHeight() == od.getHeight() |
134 | + progressPanel.getHeight()) | |
135 | { | |
136 | 65 | updateOverviewImage(); |
137 | } | |
138 | else | |
139 | { | |
140 | 33 | if ((getWidth() > 0) && (getHeight() > 0)) |
141 | { | |
142 | 22 | od.setWidth(getWidth()); |
143 | 22 | od.setHeight(getHeight() - progressPanel.getHeight()); |
144 | } | |
145 | ||
146 | 33 | setPreferredSize(new Dimension(od.getWidth(), |
147 | od.getHeight() + progressPanel.getHeight())); | |
148 | } | |
149 | } | |
150 | ||
151 | }); | |
152 | ||
153 | 43 | addMouseMotionListener(new MouseMotionAdapter() |
154 | { | |
155 | 0 | @Override |
156 | public void mouseDragged(MouseEvent evt) | |
157 | { | |
158 | 0 | if (!SwingUtilities.isRightMouseButton(evt)) |
159 | { | |
160 | 0 | if (draggingBox) |
161 | { | |
162 | // set the mouse position as a fixed point in the box | |
163 | // and drag relative to that position | |
164 | 0 | od.adjustViewportFromMouse(evt.getX(), evt.getY(), |
165 | av.getAlignment().getHiddenSequences(), | |
166 | av.getAlignment().getHiddenColumns()); | |
167 | } | |
168 | else | |
169 | { | |
170 | 0 | od.updateViewportFromMouse(evt.getX(), evt.getY(), |
171 | av.getAlignment().getHiddenSequences(), | |
172 | av.getAlignment().getHiddenColumns()); | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | 0 | @Override |
178 | public void mouseMoved(MouseEvent evt) | |
179 | { | |
180 | 0 | if (od.isPositionInBox(evt.getX(), evt.getY())) |
181 | { | |
182 | /* | |
183 | * using HAND_CURSOR rather than DRAG_CURSOR | |
184 | * as the latter is not supported on Mac | |
185 | */ | |
186 | 0 | getParent().setCursor( |
187 | Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); | |
188 | } | |
189 | else | |
190 | { | |
191 | // reset cursor | |
192 | 0 | getParent().setCursor( |
193 | Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); | |
194 | } | |
195 | } | |
196 | ||
197 | }); | |
198 | ||
199 | 43 | addMouseListener(new MouseAdapter() |
200 | { | |
201 | 0 | @Override |
202 | public void mousePressed(MouseEvent evt) | |
203 | { | |
204 | ||
205 | 0 | if (Platform.isWinRightButton(evt)) |
206 | { | |
207 | 0 | showPopupMenu(evt); |
208 | 0 | return; |
209 | } | |
210 | 0 | if (SwingUtilities.isRightMouseButton(evt)) |
211 | { | |
212 | 0 | return; |
213 | } | |
214 | // don't do anything if the mouse press is in the overview's box | |
215 | // (wait to see if it's a drag instead) | |
216 | // otherwise update the viewport | |
217 | 0 | if (!od.isPositionInBox(evt.getX(), evt.getY())) |
218 | { | |
219 | 0 | draggingBox = false; |
220 | ||
221 | // display drag cursor at mouse position | |
222 | 0 | setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); |
223 | ||
224 | 0 | od.updateViewportFromMouse(evt.getX(), evt.getY(), |
225 | av.getAlignment().getHiddenSequences(), | |
226 | av.getAlignment().getHiddenColumns()); | |
227 | 0 | getParent().setCursor( |
228 | Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); | |
229 | } | |
230 | else | |
231 | { | |
232 | 0 | draggingBox = true; |
233 | 0 | od.setDragPoint(evt.getX(), evt.getY(), |
234 | av.getAlignment().getHiddenSequences(), | |
235 | av.getAlignment().getHiddenColumns()); | |
236 | } | |
237 | } | |
238 | ||
239 | 0 | @Override |
240 | public void mouseClicked(MouseEvent evt) | |
241 | { | |
242 | 0 | if (SwingUtilities.isRightMouseButton(evt)) |
243 | { | |
244 | 0 | showPopupMenu(evt); |
245 | } | |
246 | } | |
247 | ||
248 | 0 | @Override |
249 | public void mouseReleased(MouseEvent evt) | |
250 | { | |
251 | 0 | draggingBox = false; |
252 | } | |
253 | ||
254 | }); | |
255 | ||
256 | /* | |
257 | * Javascript does not call componentResized on initial display, | |
258 | * so do the update here | |
259 | */ | |
260 | 43 | if (Platform.isJS()) |
261 | { | |
262 | 0 | updateOverviewImage(); |
263 | } | |
264 | } | |
265 | ||
266 | /* | |
267 | * Displays the popup menu and acts on user input | |
268 | */ | |
269 | 0 | protected void showPopupMenu(MouseEvent e) |
270 | { | |
271 | 0 | JPopupMenu popup = new JPopupMenu(); |
272 | 0 | ActionListener menuListener = new ActionListener() |
273 | { | |
274 | 0 | @Override |
275 | public void actionPerformed(ActionEvent event) | |
276 | { | |
277 | // switch on/off the hidden columns view | |
278 | 0 | toggleHiddenColumns(); |
279 | 0 | displayToggle.setSelected(showHidden); |
280 | } | |
281 | }; | |
282 | 0 | displayToggle = new JCheckBoxMenuItem( |
283 | MessageManager.getString("label.togglehidden")); | |
284 | 0 | displayToggle.setEnabled(true); |
285 | 0 | displayToggle.setSelected(showHidden); |
286 | 0 | popup.add(displayToggle); |
287 | 0 | displayToggle.addActionListener(menuListener); |
288 | 0 | popup.show(this, e.getX(), e.getY()); |
289 | } | |
290 | ||
291 | /* | |
292 | * Toggle overview display between showing hidden columns and hiding hidden columns | |
293 | */ | |
294 | 0 | protected void toggleHiddenColumns() |
295 | { | |
296 | 0 | if (showHidden) |
297 | { | |
298 | 0 | showHidden = false; |
299 | 0 | od = new OverviewDimensionsHideHidden(av.getRanges(), |
300 | (av.isShowAnnotation() | |
301 | && av.getAlignmentConservationAnnotation() != null)); | |
302 | } | |
303 | else | |
304 | { | |
305 | 0 | showHidden = true; |
306 | 0 | od = new OverviewDimensionsShowHidden(av.getRanges(), |
307 | (av.isShowAnnotation() | |
308 | && av.getAlignmentConservationAnnotation() != null)); | |
309 | } | |
310 | 0 | oviewCanvas.resetOviewDims(od); |
311 | 0 | updateOverviewImage(); |
312 | 0 | setBoxPosition(); |
313 | } | |
314 | ||
315 | /** | |
316 | * Updates the overview image when the related alignment panel is updated | |
317 | */ | |
318 | 558 | public void updateOverviewImage() |
319 | { | |
320 | 558 | if (oviewCanvas == null) |
321 | { | |
322 | /* | |
323 | * panel has been disposed | |
324 | */ | |
325 | 0 | return; |
326 | } | |
327 | ||
328 | 558 | if ((getWidth() > 0) && (getHeight() > 0)) |
329 | { | |
330 | 557 | od.setWidth(getWidth()); |
331 | 557 | od.setHeight(getHeight() - progressPanel.getHeight()); |
332 | } | |
333 | ||
334 | 558 | setPreferredSize(new Dimension(od.getWidth(), |
335 | od.getHeight() + progressPanel.getHeight())); | |
336 | ||
337 | 558 | if (oviewCanvas.restartDraw()) |
338 | { | |
339 | 421 | return; |
340 | } | |
341 | ||
342 | 137 | Thread thread = new Thread(this); |
343 | 137 | thread.start(); |
344 | 137 | repaint(); |
345 | ||
346 | } | |
347 | ||
348 | 137 | @Override |
349 | public void run() | |
350 | { | |
351 | 137 | if (oviewCanvas != null) |
352 | { | |
353 | 134 | oviewCanvas.draw(av.isShowSequenceFeatures(), |
354 | (av.isShowAnnotation() | |
355 | && av.getAlignmentConservationAnnotation() != null), | |
356 | ap.getSeqPanel().seqCanvas.getFeatureRenderer()); | |
357 | 121 | setBoxPosition(); |
358 | } | |
359 | } | |
360 | ||
361 | /** | |
362 | * Update the overview panel box when the associated alignment panel is | |
363 | * changed | |
364 | * | |
365 | */ | |
366 | 118 | private void setBoxPositionOnly() |
367 | { | |
368 | 118 | if (od != null) |
369 | { | |
370 | 118 | int oldX = od.getBoxX(); |
371 | 118 | int oldY = od.getBoxY(); |
372 | 118 | int oldWidth = od.getBoxWidth(); |
373 | 118 | int oldHeight = od.getBoxHeight(); |
374 | 118 | od.setBoxPosition(av.getAlignment().getHiddenSequences(), |
375 | av.getAlignment().getHiddenColumns()); | |
376 | 118 | repaint(oldX - 1, oldY - 1, oldWidth + 2, oldHeight + 2); |
377 | 118 | repaint(od.getBoxX(), od.getBoxY(), od.getBoxWidth(), |
378 | od.getBoxHeight()); | |
379 | } | |
380 | } | |
381 | ||
382 | 121 | private void setBoxPosition() |
383 | { | |
384 | 121 | if (od != null) |
385 | { | |
386 | 121 | od.setBoxPosition(av.getAlignment().getHiddenSequences(), |
387 | av.getAlignment().getHiddenColumns()); | |
388 | 121 | repaint(); |
389 | } | |
390 | } | |
391 | ||
392 | 118 | @Override |
393 | public void propertyChange(PropertyChangeEvent evt) | |
394 | { | |
395 | 118 | setBoxPositionOnly(); |
396 | } | |
397 | ||
398 | /** | |
399 | * Removes this object as a property change listener, and nulls references | |
400 | */ | |
401 | 40 | protected void dispose() |
402 | { | |
403 | 40 | try |
404 | { | |
405 | 40 | if (av != null) |
406 | { | |
407 | 40 | av.getRanges().removePropertyChangeListener(this); |
408 | } | |
409 | ||
410 | 40 | oviewCanvas.dispose(); |
411 | ||
412 | /* | |
413 | * close the parent frame (which also removes it from the | |
414 | * Desktop Windows menu) | |
415 | */ | |
416 | 40 | ((JInternalFrame) SwingUtilities |
417 | .getAncestorOfClass(JInternalFrame.class, (this))) | |
418 | .setClosed(true); | |
419 | } catch (PropertyVetoException e) | |
420 | { | |
421 | // ignore | |
422 | } finally | |
423 | { | |
424 | 40 | progressPanel = null; |
425 | 40 | av = null; |
426 | 40 | oviewCanvas = null; |
427 | 40 | ap = null; |
428 | 40 | od = null; |
429 | 40 | internalFrame = null; |
430 | } | |
431 | } | |
432 | ||
433 | 17 | public boolean isShowHiddenRegions() |
434 | { | |
435 | 17 | return showHidden; |
436 | } | |
437 | ||
438 | 59 | public OverviewCanvas getCanvas() |
439 | { | |
440 | 59 | return oviewCanvas; |
441 | } | |
442 | ||
443 | /** | |
444 | * Sets the title of the enclosing frame | |
445 | * | |
446 | * @param title | |
447 | */ | |
448 | 113 | public void setTitle(String title) |
449 | { | |
450 | 113 | internalFrame.setTitle(title); |
451 | } | |
452 | ||
453 | /** | |
454 | * Returns the title of the enclosing frame | |
455 | * | |
456 | * @return title | |
457 | */ | |
458 | 68 | public String getTitle() |
459 | { | |
460 | 68 | return internalFrame.getTitle(); |
461 | } | |
462 | ||
463 | /** | |
464 | * Sets the bounds of the frame holding the Overview panel | |
465 | * | |
466 | * @param xpos | |
467 | * @param ypos | |
468 | * @param width | |
469 | * @param height | |
470 | */ | |
471 | 13 | public void setFrameBounds(int xpos, int ypos, int width, int height) |
472 | { | |
473 | 13 | internalFrame.setBounds(xpos, ypos, width, height); |
474 | } | |
475 | ||
476 | /** | |
477 | * Returns the bounds of the enclosing frame | |
478 | * | |
479 | * @return | |
480 | */ | |
481 | 16 | public Rectangle getFrameBounds() |
482 | { | |
483 | 16 | return internalFrame.getBounds(); |
484 | } | |
485 | ||
486 | /** | |
487 | * Closes the frame containing the overview panel | |
488 | */ | |
489 | 12 | public void close() |
490 | { | |
491 | 12 | internalFrame.dispose(); |
492 | } | |
493 | } |