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