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