JAL-2664 Updates following review
[jalview.git] / src / jalview / appletgui / 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.appletgui;
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.CheckboxMenuItem;
32 import java.awt.Cursor;
33 import java.awt.Dimension;
34 import java.awt.Panel;
35 import java.awt.PopupMenu;
36 import java.awt.event.ComponentAdapter;
37 import java.awt.event.ComponentEvent;
38 import java.awt.event.InputEvent;
39 import java.awt.event.ItemEvent;
40 import java.awt.event.ItemListener;
41 import java.awt.event.MouseEvent;
42 import java.awt.event.MouseListener;
43 import java.awt.event.MouseMotionListener;
44 import java.beans.PropertyChangeEvent;
45
46 public class OverviewPanel extends Panel implements Runnable,
47         MouseMotionListener, MouseListener, ViewportListenerI
48 {
49   private OverviewDimensions od;
50
51   private OverviewCanvas oviewCanvas;
52
53   private AlignViewport av;
54
55   private AlignmentPanel ap;
56
57   private boolean showHidden = true;
58
59   private boolean updateRunning = false;
60
61   private boolean draggingBox = false;
62
63   public OverviewPanel(AlignmentPanel alPanel)
64   {
65     this.av = alPanel.av;
66     this.ap = alPanel;
67     setLayout(null);
68
69     od = new OverviewDimensionsShowHidden(av.getRanges(),
70             (av.isShowAnnotation() && av.getSequenceConsensusHash() != null));
71
72     oviewCanvas = new OverviewCanvas(od, av);
73     setLayout(new BorderLayout());
74     add(oviewCanvas, BorderLayout.CENTER);
75
76     setSize(new Dimension(od.getWidth(), od.getHeight()));
77
78     av.getRanges().addPropertyChangeListener(this);
79
80     addComponentListener(new ComponentAdapter()
81     {
82
83       @Override
84       public void componentResized(ComponentEvent evt)
85       {
86         if ((getWidth() != od.getWidth())
87                 || (getHeight() != (od.getHeight())))
88         {
89           updateOverviewImage();
90         }
91       }
92     });
93
94     addMouseMotionListener(this);
95
96     addMouseListener(this);
97
98     updateOverviewImage();
99
100   }
101
102   @Override
103   public void mouseEntered(MouseEvent evt)
104   {
105   }
106
107   @Override
108   public void mouseExited(MouseEvent evt)
109   {
110   }
111
112   @Override
113   public void mouseClicked(MouseEvent evt)
114   {
115     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
116     {
117       showPopupMenu(evt);
118     }
119   }
120
121   @Override
122   public void mouseMoved(MouseEvent evt)
123   {
124     if (od.isPositionInBox(evt.getX(), evt.getY()))
125     {
126       // display drag cursor at mouse position
127       setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
128     }
129     else
130     {
131       // reset cursor
132       setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
133     }
134   }
135
136   @Override
137   public void mousePressed(MouseEvent evt)
138   {
139     if ((evt.getModifiers()
140             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
141     {
142       if (!Platform.isAMac())
143       {
144         showPopupMenu(evt);
145       }
146     }
147     else
148     {
149       if (!od.isPositionInBox(evt.getX(), evt.getY()))
150       {
151         // don't do anything if the mouse press is in the overview's box
152         // (wait to see if it's a drag instead)
153         // otherwise update the viewport
154         od.updateViewportFromMouse(evt.getX(), evt.getY(),
155                 av.getAlignment().getHiddenSequences(),
156                 av.getAlignment().getHiddenColumns());
157       }
158       else
159       {
160         draggingBox = true;
161         od.setDragPoint(evt.getX(), evt.getY(),
162                 av.getAlignment().getHiddenSequences(),
163                 av.getAlignment().getHiddenColumns());
164       }
165     }
166   }
167
168   @Override
169   public void mouseReleased(MouseEvent evt)
170   {
171     if (draggingBox)
172     {
173       draggingBox = false;
174     }
175   }
176
177   @Override
178   public void mouseDragged(MouseEvent evt)
179   {
180     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
181     {
182       if (!Platform.isAMac())
183       {
184         showPopupMenu(evt);
185       }
186     }
187     else
188     {
189       if (draggingBox)
190       {
191         // set the mouse position as a fixed point in the box
192         // and drag relative to that position
193         od.adjustViewportFromMouse(evt.getX(), evt.getY(),
194                 av.getAlignment().getHiddenSequences(),
195                 av.getAlignment().getHiddenColumns());
196       }
197       else
198       {
199         od.updateViewportFromMouse(evt.getX(), evt.getY(),
200                 av.getAlignment().getHiddenSequences(),
201                 av.getAlignment().getHiddenColumns());
202       }
203       ap.paintAlignment(false);
204     }
205   }
206
207   /**
208    * Updates the overview image when the related alignment panel is updated
209    */
210   public void updateOverviewImage()
211   {
212     if (oviewCanvas == null)
213     {
214       /*
215        * panel has been disposed
216        */
217       return;
218     }
219
220     if ((getSize().width > 0) && (getSize().height > 0))
221     {
222       od.setWidth(getSize().width);
223       od.setHeight(getSize().height);
224     }
225     setSize(new Dimension(od.getWidth(), od.getHeight()));
226
227     synchronized (this)
228     {
229       if (updateRunning)
230       {
231         oviewCanvas.restartDraw();
232         return;
233       }
234
235       updateRunning = true;
236     }
237     Thread thread = new Thread(this);
238     thread.start();
239     repaint();
240     updateRunning = false;
241   }
242
243   @Override
244   public void run()
245   {
246     oviewCanvas.draw(av.isShowSequenceFeatures(),
247             (av.isShowAnnotation() && av
248                     .getAlignmentConservationAnnotation() != null),
249             ap.seqPanel.seqCanvas.getFeatureRenderer());
250     setBoxPosition();
251   }
252
253   /**
254    * Update the overview panel box when the associated alignment panel is
255    * changed
256    * 
257    */
258   private void setBoxPosition()
259   {
260     od.setBoxPosition(av.getAlignment()
261 .getHiddenSequences(), av
262             .getAlignment().getHiddenColumns());
263     repaint();
264   }
265
266   /*
267    * Displays the popup menu and acts on user input
268    */
269   private void showPopupMenu(MouseEvent e)
270   {
271     PopupMenu popup = new PopupMenu();
272     ItemListener menuListener = new ItemListener()
273     {
274       @Override
275       public void itemStateChanged(ItemEvent e)
276       {
277         toggleHiddenColumns();
278       }
279     };
280     CheckboxMenuItem item = new CheckboxMenuItem(
281             MessageManager.getString("label.togglehidden"));
282     item.setState(showHidden);
283     popup.add(item);
284     item.addItemListener(menuListener);
285     this.add(popup);
286     popup.show(this, e.getX(), e.getY());
287   }
288
289   @Override
290   public void propertyChange(PropertyChangeEvent evt)
291   {
292     setBoxPosition();
293   }
294
295   /*
296    * Toggle overview display between showing hidden columns and hiding hidden columns
297    */
298   private void toggleHiddenColumns()
299   {
300     if (showHidden)
301     {
302       showHidden = false;
303       od = new OverviewDimensionsHideHidden(av.getRanges(),
304               (av.isShowAnnotation() && av
305                       .getAlignmentConservationAnnotation() != null));
306     }
307     else
308     {
309       showHidden = true;
310       od = new OverviewDimensionsShowHidden(av.getRanges(),
311               (av.isShowAnnotation() && av
312                       .getAlignmentConservationAnnotation() != null));
313     }
314     oviewCanvas.resetOviewDims(od);
315     updateOverviewImage();
316   }
317
318   /**
319    * Removes this object as a property change listener, and nulls references
320    */
321   protected void dispose()
322   {
323     try
324     {
325       av.getRanges().removePropertyChangeListener(this);
326     } finally
327     {
328       av = null;
329       oviewCanvas = null;
330       ap = null;
331       od = null;
332     }
333   }
334 }