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