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