JAL-1953 2.11.2 with Archeopteryx!
[jalview.git] / src / jalview / appletgui / OverviewPanel.java
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.appletgui;
22
23 import jalview.util.MessageManager;
24 import jalview.util.Platform;
25 import jalview.viewmodel.OverviewDimensions;
26 import jalview.viewmodel.OverviewDimensionsHideHidden;
27 import jalview.viewmodel.OverviewDimensionsShowHidden;
28 import jalview.viewmodel.ViewportListenerI;
29
30 import java.awt.BorderLayout;
31 import java.awt.CheckboxMenuItem;
32 import java.awt.Cursor;
33 import java.awt.Dimension;
34 import java.awt.Frame;
35 import java.awt.Panel;
36 import java.awt.PopupMenu;
37 import java.awt.event.ComponentAdapter;
38 import java.awt.event.ComponentEvent;
39 import java.awt.event.InputEvent;
40 import java.awt.event.ItemEvent;
41 import java.awt.event.ItemListener;
42 import java.awt.event.MouseEvent;
43 import java.awt.event.MouseListener;
44 import java.awt.event.MouseMotionListener;
45 import java.beans.PropertyChangeEvent;
46
47 import javax.swing.SwingUtilities;
48
49 public class OverviewPanel extends Panel implements Runnable,
50         MouseMotionListener, MouseListener, ViewportListenerI
51 {
52   private OverviewDimensions od;
53
54   private OverviewCanvas oviewCanvas;
55
56   private AlignViewport av;
57
58   private AlignmentPanel ap;
59
60   private boolean showHidden = true;
61
62   private boolean updateRunning = false;
63
64   private boolean draggingBox = false;
65
66   public OverviewPanel(AlignmentPanel alPanel)
67   {
68     this.av = alPanel.av;
69     this.ap = alPanel;
70     setLayout(null);
71
72     od = new OverviewDimensionsShowHidden(av.getRanges(),
73             (av.isShowAnnotation()
74                     && av.getSequenceConsensusHash() != null));
75
76     oviewCanvas = new OverviewCanvas(od, av);
77     setLayout(new BorderLayout());
78     add(oviewCanvas, BorderLayout.CENTER);
79
80     setSize(new Dimension(od.getWidth(), od.getHeight()));
81
82     av.getRanges().addPropertyChangeListener(this);
83
84     addComponentListener(new ComponentAdapter()
85     {
86
87       @Override
88       public void componentResized(ComponentEvent evt)
89       {
90         if ((getWidth() != od.getWidth())
91                 || (getHeight() != (od.getHeight())))
92         {
93           updateOverviewImage();
94         }
95       }
96     });
97
98     addMouseMotionListener(this);
99
100     addMouseListener(this);
101
102     updateOverviewImage();
103
104   }
105
106   @Override
107   public void mouseEntered(MouseEvent evt)
108   {
109   }
110
111   @Override
112   public void mouseExited(MouseEvent evt)
113   {
114   }
115
116   @Override
117   public void mouseClicked(MouseEvent evt)
118   {
119     if ((evt.getModifiersEx()
120             & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
121     {
122       showPopupMenu(evt);
123     }
124   }
125
126   @Override
127   public void mouseMoved(MouseEvent evt)
128   {
129     if (od.isPositionInBox(evt.getX(), evt.getY()))
130     {
131       this.getParent()
132               .setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
133     }
134     else
135     {
136       this.getParent().setCursor(
137               Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
138     }
139   }
140
141   @Override
142   public void mousePressed(MouseEvent evt)
143   {
144     if ((evt.getModifiersEx()
145             & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
146     {
147       if (!Platform.isMac()) // BH was excluding JavaScript
148       {
149         showPopupMenu(evt);
150       }
151     }
152     else
153     {
154       // don't do anything if the mouse press is in the overview's box
155       // (wait to see if it's a drag instead)
156       // otherwise update the viewport
157       if (!od.isPositionInBox(evt.getX(), evt.getY()))
158       {
159         draggingBox = false;
160
161         // display drag cursor at mouse position
162         setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
163
164         od.updateViewportFromMouse(evt.getX(), evt.getY(),
165                 av.getAlignment().getHiddenSequences(),
166                 av.getAlignment().getHiddenColumns());
167         getParent()
168                 .setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
169       }
170       else
171       {
172         draggingBox = true;
173         od.setDragPoint(evt.getX(), evt.getY(),
174                 av.getAlignment().getHiddenSequences(),
175                 av.getAlignment().getHiddenColumns());
176       }
177     }
178   }
179
180   @Override
181   public void mouseReleased(MouseEvent evt)
182   {
183     draggingBox = false;
184   }
185
186   @Override
187   public void mouseDragged(MouseEvent evt)
188   {
189     if (Platform.isWinRightButton(evt))
190     {
191       showPopupMenu(evt);
192       return;
193     }
194
195     if (SwingUtilities.isRightMouseButton(evt))
196     {
197       return;
198     }
199
200     if (draggingBox)
201     {
202       // set the mouse position as a fixed point in the box
203       // and drag relative to that position
204       od.adjustViewportFromMouse(evt.getX(), evt.getY(),
205               av.getAlignment().getHiddenSequences(),
206               av.getAlignment().getHiddenColumns());
207     }
208     else
209     {
210       od.updateViewportFromMouse(evt.getX(), evt.getY(),
211               av.getAlignment().getHiddenSequences(),
212               av.getAlignment().getHiddenColumns());
213     }
214     ap.paintAlignment(false, false);
215   }
216
217   /**
218    * Updates the overview image when the related alignment panel is updated
219    */
220   public void updateOverviewImage()
221   {
222     if (oviewCanvas == null)
223     {
224       /*
225        * panel has been disposed
226        */
227       return;
228     }
229
230     if ((getSize().width > 0) && (getSize().height > 0))
231     {
232       od.setWidth(getSize().width);
233       od.setHeight(getSize().height);
234     }
235     setSize(new Dimension(od.getWidth(), od.getHeight()));
236
237     synchronized (this)
238     {
239       if (updateRunning)
240       {
241         oviewCanvas.restartDraw();
242         return;
243       }
244
245       updateRunning = true;
246     }
247     Thread thread = new Thread(this, "OverviewUpdate");
248     thread.start();
249     repaint();
250     updateRunning = false;
251   }
252
253   @Override
254   public void run()
255   {
256     oviewCanvas.draw(av.isShowSequenceFeatures(),
257             (av.isShowAnnotation()
258                     && av.getAlignmentConservationAnnotation() != null),
259             ap.seqPanel.seqCanvas.getFeatureRenderer());
260     setBoxPosition();
261   }
262
263   /**
264    * Update the overview panel box when the associated alignment panel is
265    * changed
266    * 
267    */
268   private void setBoxPosition()
269   {
270     od.setBoxPosition(av.getAlignment().getHiddenSequences(),
271             av.getAlignment().getHiddenColumns());
272     repaint();
273   }
274
275   /*
276    * Displays the popup menu and acts on user input
277    */
278   private void showPopupMenu(MouseEvent e)
279   {
280     PopupMenu popup = new PopupMenu();
281     ItemListener menuListener = new ItemListener()
282     {
283       @Override
284       public void itemStateChanged(ItemEvent e)
285       {
286         toggleHiddenColumns();
287       }
288     };
289     CheckboxMenuItem item = new CheckboxMenuItem(
290             MessageManager.getString("label.togglehidden"));
291     item.setState(showHidden);
292     popup.add(item);
293     item.addItemListener(menuListener);
294     this.add(popup);
295     popup.show(this, e.getX(), e.getY());
296   }
297
298   @Override
299   public void propertyChange(PropertyChangeEvent evt)
300   {
301     setBoxPosition();
302   }
303
304   /*
305    * Toggle overview display between showing hidden columns and hiding hidden columns
306    */
307   private void toggleHiddenColumns()
308   {
309     if (showHidden)
310     {
311       showHidden = false;
312       od = new OverviewDimensionsHideHidden(av.getRanges(),
313               (av.isShowAnnotation()
314                       && av.getAlignmentConservationAnnotation() != null));
315     }
316     else
317     {
318       showHidden = true;
319       od = new OverviewDimensionsShowHidden(av.getRanges(),
320               (av.isShowAnnotation()
321                       && av.getAlignmentConservationAnnotation() != null));
322     }
323     oviewCanvas.resetOviewDims(od);
324     updateOverviewImage();
325   }
326
327   /**
328    * Removes this object as a property change listener, and nulls references
329    */
330   protected void dispose()
331   {
332     try
333     {
334       av.getRanges().removePropertyChangeListener(this);
335       Frame parent = (Frame) getParent();
336       parent.dispose();
337       parent.setVisible(false);
338     } finally
339     {
340       av = null;
341       if (oviewCanvas != null)
342       {
343         oviewCanvas.dispose();
344       }
345       oviewCanvas = null;
346       ap = null;
347       od = null;
348     }
349   }
350 }