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