c81ac2f5f280c287b7d33c63662baa6c23820ac2
[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         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(),
120                     evt.getY(), av.getAlignment().getHiddenSequences(),
121                     av.getAlignment().getHiddenColumns());
122           }
123           else
124           {
125             od.updateViewportFromMouse(evt.getX(), evt.getY(), av
126                   .getAlignment().getHiddenSequences(), av.getAlignment()
127                   .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         // if (!av.getWrapAlignment())
162         {
163           if (!od.isPositionInBox(evt.getX(), evt.getY()))
164           {
165             // don't do anything if the mouse press is in the overview's box
166             // (wait to see if it's a drag instead)
167             // otherwise update the viewport
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 mouseReleased(MouseEvent evt)
184       {
185         if (draggingBox)
186         {
187           draggingBox = false;
188         }
189       }
190
191       @Override
192       public void mouseClicked(MouseEvent evt)
193       {
194         if (SwingUtilities.isRightMouseButton(evt))
195         {
196           showPopupMenu(evt);
197         }
198       }
199     });
200
201     updateOverviewImage();
202   }
203
204   /*
205    * Displays the popup menu and acts on user input
206    */
207   private void showPopupMenu(MouseEvent e)
208   {
209     JPopupMenu popup = new JPopupMenu();
210     ActionListener menuListener = new ActionListener()
211     {
212       @Override
213       public void actionPerformed(ActionEvent event)
214       {
215         // switch on/off the hidden columns view
216         toggleHiddenColumns();
217         displayToggle.setSelected(showHidden);
218       }
219     };
220     displayToggle = new JCheckBoxMenuItem(
221             MessageManager.getString("label.togglehidden"));
222     displayToggle.setEnabled(true);
223     displayToggle.setSelected(showHidden);
224     popup.add(displayToggle);
225     displayToggle.addActionListener(menuListener);
226     popup.show(this, e.getX(), e.getY());
227   }
228
229   /*
230    * Toggle overview display between showing hidden columns and hiding hidden columns
231    */
232   private void toggleHiddenColumns()
233   {
234     if (showHidden)
235     {
236       showHidden = false;
237       od = new OverviewDimensionsHideHidden(av.getRanges(),
238               (av.isShowAnnotation() && av
239                       .getAlignmentConservationAnnotation() != null));
240     }
241     else
242     {
243       showHidden = true;
244       od = new OverviewDimensionsShowHidden(av.getRanges(),
245               (av.isShowAnnotation() && av
246                       .getAlignmentConservationAnnotation() != null));
247     }
248     oviewCanvas.resetOviewDims(od);
249     updateOverviewImage();
250     setBoxPosition();
251   }
252
253   /**
254    * Updates the overview image when the related alignment panel is updated
255    */
256   public void updateOverviewImage()
257   {
258     if (oviewCanvas == null)
259     {
260       /*
261        * panel has been disposed
262        */
263       return;
264     }
265
266     if ((getWidth() > 0) && (getHeight() > 0))
267     {
268       od.setWidth(getWidth());
269       od.setHeight(getHeight());
270     }
271     
272     setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
273
274     if (oviewCanvas.restartDraw())
275     {
276       return;
277     }
278
279     Thread thread = new Thread(this);
280     thread.start();
281     repaint();
282   }
283
284   @Override
285   public void run()
286   {
287     oviewCanvas.draw(av.isShowSequenceFeatures(),
288             (av.isShowAnnotation() && av
289                     .getAlignmentConservationAnnotation() != null), ap
290                     .getSeqPanel().seqCanvas.getFeatureRenderer());
291     setBoxPosition();
292   }
293
294   /**
295    * Update the overview panel box when the associated alignment panel is
296    * changed
297    * 
298    */
299   private void setBoxPosition()
300   {
301     od.setBoxPosition(av.getAlignment().getHiddenSequences(), av
302             .getAlignment().getHiddenColumns());
303     repaint();
304   }
305
306   @Override
307   public void propertyChange(PropertyChangeEvent evt)
308   {
309     setBoxPosition();
310   }
311
312   /**
313    * Removes this object as a property change listener, and nulls references
314    */
315   protected void dispose()
316   {
317     try
318     {
319       av.getRanges().removePropertyChangeListener(this);
320     } finally
321     {
322       av = null;
323       oviewCanvas = null;
324       ap = null;
325       od = null;
326     }
327   }
328 }