/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.bin.Cache; import jalview.renderer.OverviewRenderer; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.OverviewDimensions; import jalview.viewmodel.OverviewDimensionsHideHidden; import jalview.viewmodel.OverviewDimensionsShowHidden; import jalview.viewmodel.ViewportListenerI; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import javax.swing.JCheckBoxMenuItem; import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; /** * Panel displaying an overview of the full alignment, with an interactive box * representing the viewport onto the alignment. * * @author $author$ * @version $Revision$ */ @SuppressWarnings("serial") public class OverviewPanel extends JPanel implements Runnable, ViewportListenerI { protected OverviewDimensions od; OverviewCanvas canvas; protected AlignViewportI av; AlignmentViewPanel ap; protected JCheckBoxMenuItem displayToggle; protected boolean showHidden = true; protected boolean draggingBox = false; protected ProgressPanel progressPanel; private Dimension dim; private boolean showProgress = !Platform.isJS(); // Jalview.getInstance().getShowStatus() /** * Creates a new OverviewPanel object. * * @param alPanel * The alignment panel which is shown in the overview panel */ public OverviewPanel(AlignmentViewPanel alPanel, Dimension dim) { this.av = alPanel.getAlignViewport(); this.ap = alPanel; this.dim = dim; showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START, false); createOverviewDimensions(); setLayout(new BorderLayout()); progressPanel = new ProgressPanel(OverviewRenderer.UPDATE, MessageManager.getString("label.oview_calc"), getWidth()); if (showProgress) // BH 2019 { add(progressPanel, BorderLayout.SOUTH); } canvas = new OverviewCanvas(this, od, av, showProgress ? progressPanel : null); canvas.setPreferredSize(canvas.getSize()); add(canvas, BorderLayout.CENTER); av.getRanges().addPropertyChangeListener(this); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent evt) { resizePanel(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent evt) { if (!SwingUtilities.isRightMouseButton(evt)) { if (draggingBox) { // set the mouse position as a fixed point in the box // and drag relative to that position od.adjustViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } else { od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } } } @Override public void mouseMoved(MouseEvent evt) { if (od.isPositionInBox(evt.getX(), evt.getY())) { /* * using HAND_CURSOR rather than DRAG_CURSOR * as the latter is not supported on Mac */ getParent().setCursor( Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { // reset cursor getParent().setCursor( Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } } }); addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent evt) { if (Platform.isWinRightButton(evt)) { showPopupMenu(evt); return; } if (SwingUtilities.isRightMouseButton(evt)) { return; } // don't do anything if the mouse press is in the overview's box // (wait to see if it's a drag instead) // otherwise update the viewport if (!od.isPositionInBox(evt.getX(), evt.getY())) { draggingBox = false; // display drag cursor at mouse position setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); getParent().setCursor( Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { draggingBox = true; od.setDragPoint(evt.getX(), evt.getY(), av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } } @Override public void mouseClicked(MouseEvent evt) { if (SwingUtilities.isRightMouseButton(evt)) { showPopupMenu(evt); } } @Override public void mouseReleased(MouseEvent evt) { draggingBox = false; } }); } protected void resizePanel() { int ph = (progressPanel.getParent() == null ? 0 : progressPanel.getHeight()); // Resize is called on the initial display of the overview. // This code adjusts sizes to account for the progress bar if it has not // already been accounted for, which triggers another resize call for // the correct sizing, at which point the overview image is updated. // (This avoids a double recalculation of the image.) if (getWidth() == od.getWidth() && getHeight() == od.getHeight() + ph) { if (canvas.lastMiniMe == null) { updateOverviewImage(); } } else { int w = getWidth(); int h = getHeight(); if ((w > 0) && (h > 0)) { if (dim != null) { dim.setSize(w, h - ph); } od.setWidth(w); od.setHeight(h - ph); updateOverviewImage(); } } } /** * Create the appropriate type of OverViewDimensions, with the desired size. */ private void createOverviewDimensions() { boolean showAnnotation = (av.isShowAnnotation() && av.getAlignmentConservationAnnotation() != null); if (showHidden) { od = new OverviewDimensionsShowHidden(av.getRanges(), showAnnotation, dim); } else { od = new OverviewDimensionsHideHidden(av.getRanges(), showAnnotation, dim); } } /** * Displays the popup menu and acts on user input */ protected void showPopupMenu(MouseEvent e) { JPopupMenu popup = new JPopupMenu(); ActionListener menuListener = new ActionListener() { @Override public void actionPerformed(ActionEvent event) { // switch on/off the hidden columns view toggleHiddenColumns(); displayToggle.setSelected(showHidden); } }; displayToggle = new JCheckBoxMenuItem( MessageManager.getString("label.togglehidden")); displayToggle.setEnabled(true); displayToggle.setSelected(showHidden); popup.add(displayToggle); displayToggle.addActionListener(menuListener); popup.show(this, e.getX(), e.getY()); } /** * Toggle overview display between showing hidden columns and hiding hidden * columns */ protected void toggleHiddenColumns() { showHidden = !showHidden; createOverviewDimensions(); canvas.resetOviewDims(od); updateOverviewImage(); setBoxPosition(); } /** * Updates the overview image when the related alignment panel is updated. * * Cases: * * AlignFrame.setFeatureGroupState * * AlignmentPanel.paintAlignment(true,...) (117 references) * * OverviewPanel..componentResized() OverviewPanel.toggleHiddenColumns() * * PopupMenu for action.reveal_sequences, action.reveal_all * * SliderPanel.mouseReleased() * */ public void updateOverviewImage() { if (canvas == null) { /* * panel has been disposed */ return; } int ph = (progressPanel.getParent() == null ? 0 : progressPanel.getHeight()); if ((getWidth() > 0) && (getHeight() > 0)) { od.setWidth(getWidth()); od.setHeight(getHeight() - ph); } setPreferredSize(new Dimension(od.getWidth(), od.getHeight() + ph)); if (canvas.restartDraw()) { return; } Thread thread = new Thread(this); thread.start(); } @Override public void run() { if (canvas != null) { setBoxPosition(); canvas.draw(av.isShowSequenceFeatures(), (av.isShowAnnotation() && av.getAlignmentConservationAnnotation() != null), ap.getFeatureRenderer()); } } /** * Update the overview panel box when the associated alignment panel is * changed * */ private void setBoxPositionOnly() { if (od != null) { od.updateBox(); int oldX = od.getBoxX(); int oldY = od.getBoxY(); int oldWidth = od.getBoxWidth(); int oldHeight = od.getBoxHeight(); od.setBoxPosition(av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); repaint(oldX - 1, oldY - 1, oldWidth + 2, oldHeight + 2); repaint(od.getBoxX(), od.getBoxY(), od.getBoxWidth(), od.getBoxHeight()); } } private void setBoxPosition() { if (od != null) { od.setBoxPosition(av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); repaint(); } } @Override public void propertyChange(PropertyChangeEvent evt) { setBoxPositionOnly(); } /** * Removes this object as a property change listener, and nulls references */ protected void dispose() { try { if (av != null) { av.getRanges().removePropertyChangeListener(this); } canvas.dispose(); /* * close the parent frame (which also removes it from the * Desktop Windows menu) */ ((JInternalFrame) SwingUtilities .getAncestorOfClass(JInternalFrame.class, (this))) .setClosed(true); } catch (PropertyVetoException e) { // ignore } finally { progressPanel = null; av = null; canvas = null; ap = null; od = null; } } }