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