added Platform settings separating mouse use of win/mac difference
[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.getModifiers()
120             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_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       // display drag cursor at mouse position
132       setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
133     }
134     else
135     {
136       // reset cursor
137       setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
138     }
139   }
140
141   @Override
142   public void mousePressed(MouseEvent evt)
143   {
144     if ((evt.getModifiers()
145             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_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       }
168       else
169       {
170         draggingBox = true;
171         od.setDragPoint(evt.getX(), evt.getY(),
172                 av.getAlignment().getHiddenSequences(),
173                 av.getAlignment().getHiddenColumns());
174       }
175     }
176   }
177
178   @Override
179   public void mouseReleased(MouseEvent evt)
180   {
181     draggingBox = false;
182   }
183
184   @Override
185   public void mouseDragged(MouseEvent evt)
186   {
187     if (Platform.isWinRightButton(evt)) 
188     {
189         showPopupMenu(evt);
190         return;
191     }
192
193     if (SwingUtilities.isRightMouseButton(evt))
194     { 
195         return;
196     }
197     
198           if (draggingBox)
199           {
200             // set the mouse position as a fixed point in the box
201             // and drag relative to that position
202             od.adjustViewportFromMouse(evt.getX(), evt.getY(),
203                     av.getAlignment().getHiddenSequences(),
204                     av.getAlignment().getHiddenColumns());
205           }
206           else
207           {
208             od.updateViewportFromMouse(evt.getX(), evt.getY(),
209                     av.getAlignment().getHiddenSequences(),
210                     av.getAlignment().getHiddenColumns());
211           }
212           ap.paintAlignment(false, false);
213   }
214
215   /**
216    * Updates the overview image when the related alignment panel is updated
217    */
218   public void updateOverviewImage()
219   {
220     if (oviewCanvas == null)
221     {
222       /*
223        * panel has been disposed
224        */
225       return;
226     }
227
228     if ((getSize().width > 0) && (getSize().height > 0))
229     {
230       od.setWidth(getSize().width);
231       od.setHeight(getSize().height);
232     }
233     setSize(new Dimension(od.getWidth(), od.getHeight()));
234
235     synchronized (this)
236     {
237       if (updateRunning)
238       {
239         oviewCanvas.restartDraw();
240         return;
241       }
242
243       updateRunning = true;
244     }
245     Thread thread = new Thread(this);
246     thread.start();
247     repaint();
248     updateRunning = false;
249   }
250
251   @Override
252   public void run()
253   {
254     oviewCanvas.draw(av.isShowSequenceFeatures(),
255             (av.isShowAnnotation()
256                     && av.getAlignmentConservationAnnotation() != null),
257             ap.seqPanel.seqCanvas.getFeatureRenderer());
258     setBoxPosition();
259   }
260
261   /**
262    * Update the overview panel box when the associated alignment panel is
263    * changed
264    * 
265    */
266   private void setBoxPosition()
267   {
268     od.setBoxPosition(av.getAlignment().getHiddenSequences(),
269             av.getAlignment().getHiddenColumns());
270     repaint();
271   }
272
273   /*
274    * Displays the popup menu and acts on user input
275    */
276   private void showPopupMenu(MouseEvent e)
277   {
278     PopupMenu popup = new PopupMenu();
279     ItemListener menuListener = new ItemListener()
280     {
281       @Override
282       public void itemStateChanged(ItemEvent e)
283       {
284         toggleHiddenColumns();
285       }
286     };
287     CheckboxMenuItem item = new CheckboxMenuItem(
288             MessageManager.getString("label.togglehidden"));
289     item.setState(showHidden);
290     popup.add(item);
291     item.addItemListener(menuListener);
292     this.add(popup);
293     popup.show(this, e.getX(), e.getY());
294   }
295
296   @Override
297   public void propertyChange(PropertyChangeEvent evt)
298   {
299     setBoxPosition();
300   }
301
302   /*
303    * Toggle overview display between showing hidden columns and hiding hidden columns
304    */
305   private void toggleHiddenColumns()
306   {
307     if (showHidden)
308     {
309       showHidden = false;
310       od = new OverviewDimensionsHideHidden(av.getRanges(),
311               (av.isShowAnnotation()
312                       && av.getAlignmentConservationAnnotation() != null));
313     }
314     else
315     {
316       showHidden = true;
317       od = new OverviewDimensionsShowHidden(av.getRanges(),
318               (av.isShowAnnotation()
319                       && av.getAlignmentConservationAnnotation() != null));
320     }
321     oviewCanvas.resetOviewDims(od);
322     updateOverviewImage();
323   }
324
325   /**
326    * Removes this object as a property change listener, and nulls references
327    */
328   protected void dispose()
329   {
330     try
331     {
332       av.getRanges().removePropertyChangeListener(this);
333       Frame parent = (Frame) getParent();
334       parent.dispose();
335       parent.setVisible(false);
336     } finally
337     {
338       av = null;
339       if (oviewCanvas != null)
340       {
341         oviewCanvas.dispose();
342       }
343       oviewCanvas = null;
344       ap = null;
345       od = null;
346     }
347   }
348 }