/* * TreePaneSelector.java * * Copyright (C) 2006-2014 Andrew Rambaut * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package figtree.treeviewer; import jebl.evolution.graphs.Node; import jam.mac.Utils; import java.awt.*; import java.awt.event.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Set; /** * @author Andrew Rambaut * @version $Id$ * * $HeadURL$ * * $LastChangedBy$ * $LastChangedDate$ * $LastChangedRevision$ */ public class TreePaneSelector implements MouseListener, MouseMotionListener, KeyListener { public enum SelectionMode { NODE, CLADE, TAXA }; public enum DragMode { SELECT, SCROLL }; public enum ToolMode { SELECT, ROOTING, CARTOONING, COLLAPSING, ROTATING, ANNOTATING, COLOURING }; public TreePaneSelector(TreePane treePane) { this.treePane = treePane; treePane.addMouseListener(this); treePane.addMouseMotionListener(this); treePane.addKeyListener(this); } public SelectionMode getSelectionMode() { return selectionMode; } public DragMode getDragMode() { return dragMode; } public void setSelectionMode(SelectionMode selectionMode) { defaultSelectionMode = selectionMode; this.selectionMode = selectionMode; } public void setDragMode(DragMode dragMode) { this.dragMode = dragMode; } public void setToolMode(ToolMode toolMode) { this.toolMode = toolMode; setupCursor(); } public boolean isCrossHairCursor() { return crossHairCursor; } public void setCrossHairCursor(boolean crossHairCursor) { this.crossHairCursor = crossHairCursor; } private void setupCursor() { if (toolMode != ToolMode.SELECT) { treePane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR)); treePane.setCrosshairShown(crossHairCursor); } else if (dragMode == DragMode.SELECT) { if (crossHairCursor) { treePane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR)); } else { treePane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR)); } treePane.setCrosshairShown(crossHairCursor); } else { treePane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR)); treePane.setCrosshairShown(false); } treePane.repaint(); } public void mouseClicked(MouseEvent mouseEvent) { if (treePane.getTree() == null) { return; } if (toolMode == ToolMode.ROOTING) { Node node = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint()); if (node != null) { treePane.setRootLocation(node, 0.5); } } else if (toolMode == ToolMode.ROTATING) { Node node = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint()); treePane.rotateNode(node); } else if (dragMode == DragMode.SELECT) { boolean isCrossHairShown = treePane.isCrosshairShown(); treePane.setCrosshairShown(false); Node selectedNode = treePane.getNodeAt((Graphics2D) treePane.getGraphics(), mouseEvent.getPoint()); boolean extendSelection = mouseEvent.isShiftDown(); boolean invertSelection = isCommandKeyDown(mouseEvent); if (!extendSelection && !invertSelection) { treePane.clearSelection(); } SelectionMode mode = selectionMode; if (mouseEvent.isAltDown()) { if (mode == SelectionMode.NODE) { mode = SelectionMode.CLADE; } else if (mode == SelectionMode.CLADE) { mode = SelectionMode.NODE; } } switch (mode) { case NODE: treePane.addSelectedNode(selectedNode, invertSelection, extendSelection); break; case CLADE: treePane.addSelectedClade(selectedNode, invertSelection, extendSelection); break; case TAXA: treePane.addSelectedTip(selectedNode, invertSelection, extendSelection); break; default: throw new IllegalArgumentException("Unknown SelectionMode: " + selectionMode.name()); } treePane.setCrosshairShown(isCrossHairShown); } } public void mousePressed(MouseEvent mouseEvent) { treePane.requestFocus(); // This is used for dragging in combination with mouseDragged // in the MouseMotionListener, below. dragPoint = new Point2D.Double(mouseEvent.getPoint().getX(), mouseEvent.getPoint().getY()); } public void mouseReleased(MouseEvent mouseEvent) { if (treePane.getTree() == null) { return; } if (dragMode == DragMode.SELECT) { if (treePane.getDragRectangle() != null) { Set<Node> selectedNodes = treePane.getNodesAt((Graphics2D) treePane.getGraphics(), treePane.getDragRectangle().getBounds()); boolean extendSelection = mouseEvent.isShiftDown(); boolean invertSelection = isCommandKeyDown(mouseEvent); if (!extendSelection && !invertSelection) { treePane.clearSelection(); } SelectionMode mode = selectionMode; if (mouseEvent.isAltDown()) { if (mode == SelectionMode.NODE) { mode = SelectionMode.CLADE; } else if (mode == SelectionMode.CLADE) { mode = SelectionMode.NODE; } } for (Node selectedNode : selectedNodes) { switch (mode) { case NODE: treePane.addSelectedNode(selectedNode, invertSelection, extendSelection); break; case CLADE: treePane.addSelectedClade(selectedNode, invertSelection, extendSelection); break; case TAXA: treePane.addSelectedTip(selectedNode, invertSelection, extendSelection); break; default: throw new IllegalArgumentException("Unknown SelectionMode: " + selectionMode.name()); } } } } treePane.setDragRectangle(null); } public void mouseEntered(MouseEvent mouseEvent) { // treePane.requestFocusInWindow(); if (isCommandKeyDown(mouseEvent)) { treePane.setCursorPosition(mouseEvent.getPoint()); } } public void mouseExited(MouseEvent mouseEvent) { if (isCommandKeyDown(mouseEvent)) { treePane.setCursorPosition(mouseEvent.getPoint()); } } public void mouseMoved(MouseEvent mouseEvent) { if (isCommandKeyDown(mouseEvent)) { treePane.setCursorPosition(mouseEvent.getPoint()); } } /** * On Mac, check for the 'Command' key, otherwise use the 'Control' key * @param event * @return is it pressed */ private boolean isCommandKeyDown(InputEvent event) { return Utils.isMacOSX() ? event.isMetaDown() : event.isControlDown(); } /** * On Mac, check for the 'Option' key, otherwise use the 'Alt' key * @param event * @return is it pressed */ private boolean isOptionKeyDown(InputEvent event) { return event.isAltDown(); } public void mouseDragged(MouseEvent mouseEvent) { if (toolMode != ToolMode.SELECT || dragPoint == null) { return; } if (dragMode == DragMode.SCROLL) { // Calculate how far the mouse has been dragged from the point clicked in // mousePressed, above. int deltaX = (int) (mouseEvent.getX() - dragPoint.getX()); int deltaY = (int) (mouseEvent.getY() - dragPoint.getY()); // Get the currently visible window Rectangle visRect = treePane.getVisibleRect(); // Calculate how much we need to scroll if (deltaX > 0) { deltaX = visRect.x - deltaX; } else { deltaX = visRect.x + visRect.width - deltaX; } if (deltaY > 0) { deltaY = visRect.y - deltaY; } else { deltaY = visRect.y + visRect.height - deltaY; } // Scroll the visible region Rectangle r = new Rectangle(deltaX, deltaY, 1, 1); treePane.scrollRectToVisible(r); } else { double x1 = Math.min(dragPoint.getX(), mouseEvent.getPoint().getX()); double y1 = Math.min(dragPoint.getY(), mouseEvent.getPoint().getY()); double x2 = Math.max(dragPoint.getX(), mouseEvent.getPoint().getX()); double y2 = Math.max(dragPoint.getY(), mouseEvent.getPoint().getY()); treePane.setDragRectangle(new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1)); treePane.scrollPointToVisible(mouseEvent.getPoint()); } } public void keyTyped(KeyEvent event) { } public void keyPressed(KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_SPACE) { dragMode = DragMode.SCROLL; } if (isOptionKeyDown(event)) { switch (defaultSelectionMode) { case NODE: case CLADE: selectionMode = SelectionMode.TAXA; break; case TAXA: selectionMode = SelectionMode.NODE ; break; } } else { selectionMode = defaultSelectionMode; } crossHairCursor = isCommandKeyDown(event); setupCursor(); } public void keyReleased(KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_SPACE) { dragMode = DragMode.SELECT; } crossHairCursor = isCommandKeyDown(event); setupCursor(); } private TreePane treePane; private SelectionMode defaultSelectionMode = SelectionMode.NODE; private SelectionMode selectionMode = SelectionMode.NODE; private ToolMode toolMode = ToolMode.SELECT; private DragMode dragMode = DragMode.SELECT; private Point2D dragPoint = null; private boolean crossHairCursor = false; }