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