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