JAL-2611 reinstated missed commit
[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
55         implements Runnable, 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()
84                     && av.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         if ((getWidth() != od.getWidth())
100                 || (getHeight() != (od.getHeight())))
101         {
102           updateOverviewImage();
103           setBoxPosition();
104         }
105       }
106     });
107
108     addMouseMotionListener(new MouseMotionAdapter()
109     {
110       @Override
111       public void mouseDragged(MouseEvent evt)
112       {
113         if (!SwingUtilities.isRightMouseButton(evt))
114         {
115           if (draggingBox)
116           {
117             // set the mouse position as a fixed point in the box
118             // and drag relative to that position
119             od.adjustViewportFromMouse(evt.getX(), evt.getY(),
120                     av.getAlignment().getHiddenSequences(),
121                     av.getAlignment().getHiddenColumns());
122           }
123           else
124           {
125             od.updateViewportFromMouse(evt.getX(), evt.getY(),
126                     av.getAlignment().getHiddenSequences(),
127                     av.getAlignment().getHiddenColumns());
128           }
129         }
130       }
131
132       @Override
133       public void mouseMoved(MouseEvent evt)
134       {
135         if (od.isPositionInBox(evt.getX(), evt.getY()))
136         {
137           // display drag cursor at mouse position
138           setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
139         }
140         else
141         {
142           // reset cursor
143           setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
144         }
145       }
146     });
147
148     addMouseListener(new MouseAdapter()
149     {
150       @Override
151       public void mousePressed(MouseEvent evt)
152       {
153         if (SwingUtilities.isRightMouseButton(evt))
154         {
155           if (!Platform.isAMac())
156           {
157             showPopupMenu(evt);
158           }
159         }
160         else
161         {
162           // don't do anything if the mouse press is in the overview's box
163           // (wait to see if it's a drag instead)
164           // otherwise update the viewport
165           if (!od.isPositionInBox(evt.getX(), evt.getY()))
166           {
167             draggingBox = false;
168             od.updateViewportFromMouse(evt.getX(), evt.getY(),
169                     av.getAlignment().getHiddenSequences(),
170                     av.getAlignment().getHiddenColumns());
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 mouseClicked(MouseEvent evt)
184       {
185         if (SwingUtilities.isRightMouseButton(evt))
186         {
187           showPopupMenu(evt);
188         }
189       }
190     });
191
192     updateOverviewImage();
193   }
194
195   /*
196    * Displays the popup menu and acts on user input
197    */
198   private void showPopupMenu(MouseEvent e)
199   {
200     JPopupMenu popup = new JPopupMenu();
201     ActionListener menuListener = new ActionListener()
202     {
203       @Override
204       public void actionPerformed(ActionEvent event)
205       {
206         // switch on/off the hidden columns view
207         toggleHiddenColumns();
208         displayToggle.setSelected(showHidden);
209       }
210     };
211     displayToggle = new JCheckBoxMenuItem(
212             MessageManager.getString("label.togglehidden"));
213     displayToggle.setEnabled(true);
214     displayToggle.setSelected(showHidden);
215     popup.add(displayToggle);
216     displayToggle.addActionListener(menuListener);
217     popup.show(this, e.getX(), e.getY());
218   }
219
220   /*
221    * Toggle overview display between showing hidden columns and hiding hidden columns
222    */
223   private void toggleHiddenColumns()
224   {
225     if (showHidden)
226     {
227       showHidden = false;
228       od = new OverviewDimensionsHideHidden(av.getRanges(),
229               (av.isShowAnnotation()
230                       && av.getAlignmentConservationAnnotation() != null));
231     }
232     else
233     {
234       showHidden = true;
235       od = new OverviewDimensionsShowHidden(av.getRanges(),
236               (av.isShowAnnotation()
237                       && av.getAlignmentConservationAnnotation() != null));
238     }
239     oviewCanvas.resetOviewDims(od);
240     updateOverviewImage();
241     setBoxPosition();
242   }
243
244   /**
245    * Updates the overview image when the related alignment panel is updated
246    */
247   public void updateOverviewImage()
248   {
249     if (oviewCanvas == null)
250     {
251       /*
252        * panel has been disposed
253        */
254       return;
255     }
256
257     if ((getWidth() > 0) && (getHeight() > 0))
258     {
259       od.setWidth(getWidth());
260       od.setHeight(getHeight());
261     }
262
263     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
264
265     if (oviewCanvas.restartDraw())
266     {
267       return;
268     }
269
270     Thread thread = new Thread(this);
271     thread.start();
272     repaint();
273   }
274
275   @Override
276   public void run()
277   {
278     oviewCanvas.draw(av.isShowSequenceFeatures(),
279             (av.isShowAnnotation()
280                     && av.getAlignmentConservationAnnotation() != null),
281             ap.getSeqPanel().seqCanvas.getFeatureRenderer());
282     setBoxPosition();
283   }
284
285   /**
286    * Update the overview panel box when the associated alignment panel is
287    * changed
288    * 
289    */
290   private void setBoxPosition()
291   {
292     od.setBoxPosition(av.getAlignment().getHiddenSequences(),
293             av.getAlignment().getHiddenColumns());
294     repaint();
295   }
296
297   @Override
298   public void propertyChange(PropertyChangeEvent evt)
299   {
300     setBoxPosition();
301   }
302
303   /**
304    * Removes this object as a property change listener, and nulls references
305    */
306   protected void dispose()
307   {
308     try
309     {
310       av.getRanges().removePropertyChangeListener(this);
311     } finally
312     {
313       av = null;
314       oviewCanvas = null;
315       ap = null;
316       od = null;
317     }
318   }
319 }