8ce597d06db20b454b89a05aa93f4e62cff0166b
[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         od.updateViewportFromMouse(evt.getX(), evt.getY(),
159                 av.getAlignment().getHiddenSequences(),
160                 av.getAlignment().getHiddenColumns());
161       }
162       else
163       {
164         draggingBox = true;
165         od.setDragPoint(evt.getX(), evt.getY(),
166                 av.getAlignment().getHiddenSequences(),
167                 av.getAlignment().getHiddenColumns());
168       }
169     }
170   }
171
172   @Override
173   public void mouseReleased(MouseEvent evt)
174   {
175   }
176
177   @Override
178   public void mouseDragged(MouseEvent evt)
179   {
180     if ((evt.getModifiers()
181             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
182     {
183       if (!Platform.isAMac())
184       {
185         showPopupMenu(evt);
186       }
187     }
188     else
189     {
190       if (draggingBox)
191       {
192         // set the mouse position as a fixed point in the box
193         // and drag relative to that position
194         od.adjustViewportFromMouse(evt.getX(), evt.getY(),
195                 av.getAlignment().getHiddenSequences(),
196                 av.getAlignment().getHiddenColumns());
197       }
198       else
199       {
200         od.updateViewportFromMouse(evt.getX(), evt.getY(),
201                 av.getAlignment().getHiddenSequences(),
202                 av.getAlignment().getHiddenColumns());
203       }
204       ap.paintAlignment(false, false);
205     }
206   }
207
208   /**
209    * Updates the overview image when the related alignment panel is updated
210    */
211   public void updateOverviewImage()
212   {
213     if (oviewCanvas == null)
214     {
215       /*
216        * panel has been disposed
217        */
218       return;
219     }
220
221     if ((getSize().width > 0) && (getSize().height > 0))
222     {
223       od.setWidth(getSize().width);
224       od.setHeight(getSize().height);
225     }
226     setSize(new Dimension(od.getWidth(), od.getHeight()));
227
228     synchronized (this)
229     {
230       if (updateRunning)
231       {
232         oviewCanvas.restartDraw();
233         return;
234       }
235
236       updateRunning = true;
237     }
238     Thread thread = new Thread(this);
239     thread.start();
240     repaint();
241     updateRunning = false;
242   }
243
244   @Override
245   public void run()
246   {
247     oviewCanvas.draw(av.isShowSequenceFeatures(),
248             (av.isShowAnnotation()
249                     && av.getAlignmentConservationAnnotation() != null),
250             ap.seqPanel.seqCanvas.getFeatureRenderer());
251     setBoxPosition();
252   }
253
254   /**
255    * Update the overview panel box when the associated alignment panel is
256    * changed
257    * 
258    */
259   private void setBoxPosition()
260   {
261     od.setBoxPosition(av.getAlignment().getHiddenSequences(),
262             av.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()
305                       && av.getAlignmentConservationAnnotation() != null));
306     }
307     else
308     {
309       showHidden = true;
310       od = new OverviewDimensionsShowHidden(av.getRanges(),
311               (av.isShowAnnotation()
312                       && av.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       Frame parent = (Frame) getParent();
327       parent.dispose();
328       parent.setVisible(false);
329     } finally
330     {
331       av = null;
332       oviewCanvas = null;
333       ap = null;
334       od = null;
335     }
336   }
337 }