JAL-2611 Better
[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                 && !av.getWrapAlignment())
115         {
116           if (draggingBox)
117           {
118             // set the mouse position as a fixed point in the box
119             // and drag relative to that position
120             od.adjustViewportFromMouse(evt.getX(),
121                     evt.getY(), av.getAlignment().getHiddenSequences(),
122                     av.getAlignment().getHiddenColumns());
123           }
124           else
125           {
126             od.updateViewportFromMouse(evt.getX(), evt.getY(), av
127                   .getAlignment().getHiddenSequences(), av.getAlignment()
128                   .getHiddenColumns());
129           }
130
131         }
132       }
133
134       @Override
135       public void mouseMoved(MouseEvent evt)
136       {
137         if (od.isPositionInBox(evt.getX(), evt.getY()))
138         {
139           // display drag cursor at mouse position
140           setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
141         }
142         else
143         {
144           // reset cursor
145           setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
146         }
147       }
148     });
149
150     addMouseListener(new MouseAdapter()
151     {
152       @Override
153       public void mousePressed(MouseEvent evt)
154       {
155         if (SwingUtilities.isRightMouseButton(evt))
156         {
157           if (!Platform.isAMac())
158           {
159             showPopupMenu(evt);
160           }
161         }
162         else if (!av.getWrapAlignment())
163         {
164           if (!od.isPositionInBox(evt.getX(), evt.getY()))
165           {
166             // don't do anything if the mouse press is in the overview's box
167             // (wait to see if it's a drag instead)
168             // otherwise update the viewport
169             od.updateViewportFromMouse(evt.getX(), evt.getY(),
170                     av.getAlignment().getHiddenSequences(),
171                     av.getAlignment().getHiddenColumns());
172           }
173           else
174           {
175             draggingBox = true;
176             od.setDragPoint(evt.getX(), evt.getY(),
177                     av.getAlignment().getHiddenSequences(),
178                     av.getAlignment().getHiddenColumns());
179           }
180         }
181       }
182
183       @Override
184       public void mouseReleased(MouseEvent evt)
185       {
186         if (draggingBox)
187         {
188           draggingBox = false;
189         }
190       }
191
192       @Override
193       public void mouseClicked(MouseEvent evt)
194       {
195         if (SwingUtilities.isRightMouseButton(evt))
196         {
197           showPopupMenu(evt);
198         }
199       }
200
201
202     });
203
204     updateOverviewImage();
205   }
206
207   /*
208    * Displays the popup menu and acts on user input
209    */
210   private void showPopupMenu(MouseEvent e)
211   {
212     JPopupMenu popup = new JPopupMenu();
213     ActionListener menuListener = new ActionListener()
214     {
215       @Override
216       public void actionPerformed(ActionEvent event)
217       {
218         // switch on/off the hidden columns view
219         toggleHiddenColumns();
220         displayToggle.setSelected(showHidden);
221       }
222     };
223     displayToggle = new JCheckBoxMenuItem(
224             MessageManager.getString("label.togglehidden"));
225     displayToggle.setEnabled(true);
226     displayToggle.setSelected(showHidden);
227     popup.add(displayToggle);
228     displayToggle.addActionListener(menuListener);
229     popup.show(this, e.getX(), e.getY());
230   }
231
232   /*
233    * Toggle overview display between showing hidden columns and hiding hidden columns
234    */
235   private void toggleHiddenColumns()
236   {
237     if (showHidden)
238     {
239       showHidden = false;
240       od = new OverviewDimensionsHideHidden(av.getRanges(),
241               (av.isShowAnnotation() && av
242                       .getAlignmentConservationAnnotation() != null));
243     }
244     else
245     {
246       showHidden = true;
247       od = new OverviewDimensionsShowHidden(av.getRanges(),
248               (av.isShowAnnotation() && av
249                       .getAlignmentConservationAnnotation() != null));
250     }
251     oviewCanvas.resetOviewDims(od);
252     updateOverviewImage();
253     setBoxPosition();
254   }
255
256   /**
257    * Updates the overview image when the related alignment panel is updated
258    */
259   public void updateOverviewImage()
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
280   @Override
281   public void run()
282   {
283     oviewCanvas.draw(av.isShowSequenceFeatures(),
284             (av.isShowAnnotation() && av
285                     .getAlignmentConservationAnnotation() != null), ap
286                     .getSeqPanel().seqCanvas.getFeatureRenderer());
287     setBoxPosition();
288   }
289
290   /**
291    * Update the overview panel box when the associated alignment panel is
292    * changed
293    * 
294    */
295   private void setBoxPosition()
296   {
297     od.setBoxPosition(av.getAlignment().getHiddenSequences(), av
298             .getAlignment().getHiddenColumns());
299     repaint();
300   }
301
302   @Override
303   public void propertyChange(PropertyChangeEvent evt)
304   {
305     setBoxPosition();
306   }
307 }