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