JAL-2611 reinstated missed commit
[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()
71                     && av.getSequenceConsensusHash() != null));
72
73     oviewCanvas = new OverviewCanvas(od, av);
74     setLayout(new BorderLayout());
75     add(oviewCanvas, BorderLayout.CENTER);
76
77     setSize(new Dimension(od.getWidth(), od.getHeight()));
78
79     av.getRanges().addPropertyChangeListener(this);
80
81     addComponentListener(new ComponentAdapter()
82     {
83
84       @Override
85       public void componentResized(ComponentEvent evt)
86       {
87         if ((getWidth() != od.getWidth())
88                 || (getHeight() != (od.getHeight())))
89         {
90           updateOverviewImage();
91         }
92       }
93     });
94
95     addMouseMotionListener(this);
96
97     addMouseListener(this);
98
99     updateOverviewImage();
100
101   }
102
103   @Override
104   public void mouseEntered(MouseEvent evt)
105   {
106   }
107
108   @Override
109   public void mouseExited(MouseEvent evt)
110   {
111   }
112
113   @Override
114   public void mouseClicked(MouseEvent evt)
115   {
116     if ((evt.getModifiers()
117             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
118     {
119       showPopupMenu(evt);
120     }
121   }
122
123   @Override
124   public void mouseMoved(MouseEvent evt)
125   {
126     if (od.isPositionInBox(evt.getX(), evt.getY()))
127     {
128       // display drag cursor at mouse position
129       setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
130     }
131     else
132     {
133       // reset cursor
134       setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
135     }
136   }
137
138   @Override
139   public void mousePressed(MouseEvent evt)
140   {
141     if ((evt.getModifiers()
142             & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
143     {
144       if (!Platform.isAMac())
145       {
146         showPopupMenu(evt);
147       }
148     }
149     else
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       if (!od.isPositionInBox(evt.getX(), evt.getY()))
155       { 
156         draggingBox = false;
157         od.updateViewportFromMouse(evt.getX(), evt.getY(),
158                 av.getAlignment().getHiddenSequences(),
159                 av.getAlignment().getHiddenColumns());
160       }
161       else
162       {
163         draggingBox = true;
164         od.setDragPoint(evt.getX(), evt.getY(),
165                 av.getAlignment().getHiddenSequences(),
166                 av.getAlignment().getHiddenColumns());
167       }
168     }
169   }
170
171   @Override
172   public void mouseReleased(MouseEvent evt)
173   {
174   }
175
176   @Override
177   public void mouseDragged(MouseEvent evt)
178   {
179     if ((evt.getModifiers()
180             & 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()
248                     && av.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().getHiddenSequences(),
261             av.getAlignment().getHiddenColumns());
262     repaint();
263   }
264
265   /*
266    * Displays the popup menu and acts on user input
267    */
268   private void showPopupMenu(MouseEvent e)
269   {
270     PopupMenu popup = new PopupMenu();
271     ItemListener menuListener = new ItemListener()
272     {
273       @Override
274       public void itemStateChanged(ItemEvent e)
275       {
276         toggleHiddenColumns();
277       }
278     };
279     CheckboxMenuItem item = new CheckboxMenuItem(
280             MessageManager.getString("label.togglehidden"));
281     item.setState(showHidden);
282     popup.add(item);
283     item.addItemListener(menuListener);
284     this.add(popup);
285     popup.show(this, e.getX(), e.getY());
286   }
287
288   @Override
289   public void propertyChange(PropertyChangeEvent evt)
290   {
291     setBoxPosition();
292   }
293
294   /*
295    * Toggle overview display between showing hidden columns and hiding hidden columns
296    */
297   private void toggleHiddenColumns()
298   {
299     if (showHidden)
300     {
301       showHidden = false;
302       od = new OverviewDimensionsHideHidden(av.getRanges(),
303               (av.isShowAnnotation()
304                       && av.getAlignmentConservationAnnotation() != null));
305     }
306     else
307     {
308       showHidden = true;
309       od = new OverviewDimensionsShowHidden(av.getRanges(),
310               (av.isShowAnnotation()
311                       && av.getAlignmentConservationAnnotation() != null));
312     }
313     oviewCanvas.resetOviewDims(od);
314     updateOverviewImage();
315   }
316
317   /**
318    * Removes this object as a property change listener, and nulls references
319    */
320   protected void dispose()
321   {
322     try
323     {
324       av.getRanges().removePropertyChangeListener(this);
325     } finally
326     {
327       av = null;
328       oviewCanvas = null;
329       ap = null;
330       od = null;
331     }
332   }
333 }