JAL-2587 fiddling with dimensions
[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.renderer.OverviewRenderer;
24 import jalview.util.MessageManager;
25 import jalview.util.Platform;
26 import jalview.viewmodel.OverviewDimensions;
27 import jalview.viewmodel.OverviewDimensionsHideHidden;
28 import jalview.viewmodel.OverviewDimensionsShowHidden;
29 import jalview.viewmodel.ViewportListenerI;
30
31 import java.awt.BorderLayout;
32 import java.awt.Cursor;
33 import java.awt.Dimension;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.ActionListener;
36 import java.awt.event.ComponentAdapter;
37 import java.awt.event.ComponentEvent;
38 import java.awt.event.MouseAdapter;
39 import java.awt.event.MouseEvent;
40 import java.awt.event.MouseMotionAdapter;
41 import java.beans.PropertyChangeEvent;
42
43 import javax.swing.JCheckBoxMenuItem;
44 import javax.swing.JPanel;
45 import javax.swing.JPopupMenu;
46 import javax.swing.SwingUtilities;
47
48 /**
49  * Panel displaying an overview of the full alignment, with an interactive box
50  * representing the viewport onto the alignment.
51  * 
52  * @author $author$
53  * @version $Revision$
54  */
55 public class OverviewPanel extends JPanel implements Runnable,
56         ViewportListenerI
57 {
58   private OverviewDimensions od;
59
60   private OverviewCanvas oviewCanvas;
61
62   private AlignViewport av;
63
64   private AlignmentPanel ap;
65
66   private JCheckBoxMenuItem displayToggle;
67
68   private boolean showHidden = true;
69
70   private boolean draggingBox = false;
71
72   private ProgressPanel progressPanel;
73
74   /**
75    * Creates a new OverviewPanel object.
76    * 
77    * @param alPanel
78    *          The alignment panel which is shown in the overview panel
79    */
80   public OverviewPanel(AlignmentPanel alPanel)
81   {
82     this.av = alPanel.av;
83     this.ap = alPanel;
84
85     od = new OverviewDimensionsShowHidden(av.getRanges(),
86             (av.isShowAnnotation() && av
87                     .getAlignmentConservationAnnotation() != null));
88
89     setLayout(new BorderLayout());
90     progressPanel = new ProgressPanel(OverviewRenderer.UPDATE,
91             MessageManager.getString("label.oview_calc"));
92     this.add(progressPanel, BorderLayout.SOUTH);
93     oviewCanvas = new OverviewCanvas(od, av, progressPanel);
94
95     add(oviewCanvas, BorderLayout.CENTER);
96
97     av.getRanges().addPropertyChangeListener(this);
98
99     addComponentListener(new ComponentAdapter()
100     {
101       @Override
102       public void componentResized(ComponentEvent evt)
103       {
104         updateOverviewImage();
105       }
106
107     });
108
109     addMouseMotionListener(new MouseMotionAdapter()
110     {
111       @Override
112       public void mouseDragged(MouseEvent evt)
113       {
114         if (!SwingUtilities.isRightMouseButton(evt))
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       @Override
134       public void mouseMoved(MouseEvent evt)
135       {
136         if (od.isPositionInBox(evt.getX(), evt.getY()))
137         {
138           // display drag cursor at mouse position
139           setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
140         }
141         else
142         {
143           // reset cursor
144           setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
145         }
146       }
147     });
148
149     addMouseListener(new MouseAdapter()
150     {
151       @Override
152       public void mousePressed(MouseEvent evt)
153       {
154         if (SwingUtilities.isRightMouseButton(evt))
155         {
156           if (!Platform.isAMac())
157           {
158             showPopupMenu(evt);
159           }
160         }
161         else
162         // 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     // updateOverviewImage();
203   }
204
205   /*
206    * Displays the popup menu and acts on user input
207    */
208   private void showPopupMenu(MouseEvent e)
209   {
210     JPopupMenu popup = new JPopupMenu();
211     ActionListener menuListener = new ActionListener()
212     {
213       @Override
214       public void actionPerformed(ActionEvent event)
215       {
216         // switch on/off the hidden columns view
217         toggleHiddenColumns();
218         displayToggle.setSelected(showHidden);
219       }
220     };
221     displayToggle = new JCheckBoxMenuItem(
222             MessageManager.getString("label.togglehidden"));
223     displayToggle.setEnabled(true);
224     displayToggle.setSelected(showHidden);
225     popup.add(displayToggle);
226     displayToggle.addActionListener(menuListener);
227     popup.show(this, e.getX(), e.getY());
228   }
229
230   /*
231    * Toggle overview display between showing hidden columns and hiding hidden columns
232    */
233   private void toggleHiddenColumns()
234   {
235     if (showHidden)
236     {
237       showHidden = false;
238       od = new OverviewDimensionsHideHidden(av.getRanges(),
239               (av.isShowAnnotation() && av
240                       .getAlignmentConservationAnnotation() != null));
241     }
242     else
243     {
244       showHidden = true;
245       od = new OverviewDimensionsShowHidden(av.getRanges(),
246               (av.isShowAnnotation() && av
247                       .getAlignmentConservationAnnotation() != null));
248     }
249     oviewCanvas.resetOviewDims(od);
250     updateOverviewImage();
251     setBoxPosition();
252   }
253
254   /**
255    * Updates the overview image when the related alignment panel is updated
256    */
257   public void updateOverviewImage()
258   {
259     if (oviewCanvas == null)
260     {
261       /*
262        * panel has been disposed
263        */
264       return;
265     }
266
267     if ((getWidth() > 0) && (getHeight() > 0))
268     {
269       od.setWidth(getWidth());
270       od.setHeight(getHeight() - progressPanel.getHeight());
271     }
272     
273     setPreferredSize(new Dimension(od.getWidth(),
274             od.getHeight()));
275
276     if (oviewCanvas.restartDraw())
277     {
278       return;
279     }
280
281     Thread thread = new Thread(this);
282     thread.start();
283     repaint();
284
285     
286   }
287
288   @Override
289   public void run()
290   {
291     oviewCanvas.draw(av.isShowSequenceFeatures(),
292             (av.isShowAnnotation() && av
293                     .getAlignmentConservationAnnotation() != null), ap
294                     .getSeqPanel().seqCanvas.getFeatureRenderer());
295     setBoxPosition();
296   }
297
298   /**
299    * Update the overview panel box when the associated alignment panel is
300    * changed
301    * 
302    */
303   private void setBoxPosition()
304   {
305     od.setBoxPosition(av.getAlignment().getHiddenSequences(), av
306             .getAlignment().getHiddenColumns());
307     repaint();
308   }
309
310   @Override
311   public void propertyChange(PropertyChangeEvent evt)
312   {
313     setBoxPosition();
314   }
315
316   /**
317    * Removes this object as a property change listener, and nulls references
318    */
319   protected void dispose()
320   {
321     try
322     {
323       av.getRanges().removePropertyChangeListener(this);
324     } finally
325     {
326       progressPanel = null;
327       av = null;
328       oviewCanvas = null;
329       ap = null;
330       od = null;
331     }
332   }
333 }