/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* 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 3 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, see <http://www.gnu.org/licenses/>
*/
package ketUI;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import geom.*;
import ket.display.box.Box;
import ketUI.panel.KetPanel;
import ketUI.responder.Responder;
import ket.*;
import ket.math.*;
import ketUI.modes.*;
import ket.math.EquationList;
public class MouseEventHandler implements MouseListener, MouseMotionListener {
// TODO: Untested for false values: switch selection back on.
public static final boolean MOUSE_MOTION = true;
// External connections.
final Document document;
// Internal state.
Position initialPosition; // click for click-drag-release
Position currentPosition; // movement location.
final MouseLoop mouseLoop;
/**
* In some contexts, a click on the window by the user only seeks to
* clear a button menu and so should otherwise be ignored.
*/
boolean hold;
public MouseEventHandler(Document document) {
this.document = document;
mouseLoop = new MouseLoop(document);
initialPosition = null;
currentPosition = null;
hold = false;
}
@Override
public void mouseClicked(MouseEvent e) {
mouseLoop.clear(); //?
}
@Override
public void mouseEntered(MouseEvent e) {
// This is the only time an event is thrown that provides
// access to the destination component and mouse position.
// Record it for later.
setDestination(e.getComponent(), new Position(e.getX(), e.getY()));
}
@Override
public void mouseExited(MouseEvent e) {
// Do nothing
}
@Override
public void mousePressed(MouseEvent e) {
// TODO: Move this somewhere more specific, e.g. add another callback to responder.respondToClick().
//-? boolean left = MouseButton.getMouseButton(e)==MouseButton.LEFT;
boolean normalMode = document.getModes().getDocumentState()==DocumentState.NORMAL;
if (/*-? left && */ normalMode) {
Position p = new Position(e.getX(), e.getY());
Argument a = getKetPanel().findDeepestArgument(p);
if (a!=null) {
document.getCursor().setCurrent(a);
}
}
// Ingore previous destinations to which equations have
// been dragged.
setDestination(null, null);
// Start a new selection loop.
mouseLoop.clear();
// TODO: Coordinate selection with chord events.
setInitialPosition(e.getX(), e.getY());
// Select the current equation location.
getKetPanel().updateDragIcon(initialPosition);
getKetPanel().updateAndRepaint();
}
private MathCollection getMathCollection() {
return document.getMathCollection();
}
private KetPanel getKetPanel() {
return document.getKetPanel();
}
/**
* If the mouse has been draggend (from this docuemnt) to another
* window, copy the selection and add it as a new equation in the
* (other) desination document.
*/
private boolean dragToAnotherDocument(Component component, MouseButton mouseButton, Position p) { // Drag to another document.
KetPanel toPanel = getLastDestination();
if (toPanel==null || toPanel==component) {
// Edit within the current ket panel...
// Clear (not needed, but just being careful).
setDestination(null, null); // i.e. clear
return false;
} else if (mouseButton!=MouseButton.LEFT) {
// TODO: Extend.
return true; // Done [nothing].
}
Document toDocument = toPanel.getDocument();
Selection toSelection = toDocument.getSelection();
// Copy argument or equation accross (non-destructively).
Position to = getLastPosition();
Equation toEquation = toPanel.pointToEquation(to);
if (toEquation!=null) {
toSelection.setCurrent(toEquation.getRoot());
}
// Clone and add as a new equation below the selection.
Equation equationCopy = getSelection().getCurrent().getEquation().cloneEquation(); // Include the entire equation: root and label etc.?
toSelection.appendEquation(equationCopy);
setDestination(null, null); // i.e. clear
getKetPanel().updateAndRepaint();
toPanel.updateAndRepaint();
getMathCollection().updateUndoStack();
toDocument.getMathCollection().updateUndoStack();
return true;
}
public void setHold(boolean hold) {
Ket.out.println("Hold: " + hold);
this.hold = hold;
}
public boolean getHold() {
return hold;
}
@Override
public void mouseReleased(MouseEvent e) {
if (hold) {
// Limited to a single click.
hold = false;
return;
}
getKetPanel().requestFocusInWindow();
//- getKetPanel().setDragLocation(null); // Clear the potential drag image.
getKetPanel().clearDragIcon();
Position p = new Position(e.getX(), e.getY());
boolean done = dragToAnotherDocument(e.getComponent(), MouseButton.getMouseButton(e), p);
if (done) return;
// TODO: The current approach to click and drag does not work:- fix it.
// Note: Don't confuse this with other, working click-and-drag stuff.
//- Box.displayBorders = false;
boolean userClicked = p.equals(initialPosition);
MouseButton mouseButton = MouseButton.getMouseButton(e);
Responder responder = getModes().getResponder();
if (userClicked) {
boolean singleClick = e.getClickCount()==1;
responder.respondToMouseClick(mouseButton, singleClick, p);
} else {
responder.respondToMouseDrag(mouseButton, initialPosition, p);
}
getKetPanel().updateForAnimatedDisplay(); // <---------------- animate.
getKetPanel().updateAndRepaint();
getMathCollection().updateUndoStack();
}
public Position getInitialPosition() {
return initialPosition;
}
public Position getCurrentPosition() {
return currentPosition;
}
/**
* Record the initial mouse-click position.
*/
private void setInitialPosition(double initialX, double initialY) {
this.initialPosition = new Position(initialX, initialY);
}
/**
* Record the initial mouse-click position.
*/
private void setCurrentPosition(double initialX, double initialY) {
this.currentPosition = new Position(initialX, initialY);
}
private Modes getModes() {
return document.getModes();
}
/**
* Return the current selection model which is stored within
* mathCollection.
*/
private Selection getSelection() {
return getMathCollection().getSelection();
}
///////////////////////////////////
// MOUSE MOTION LISTENER METHODS //
///////////////////////////////////
/**
* Invoked when a mouse button is pressed on a component and then dragged.
*/
@Override
public void mouseDragged(MouseEvent e) {
mouseLoop.clear();
//- document.getKetPanel().repaint();
//- Modes mode = document.getModes();
boolean left = SwingUtilities.isLeftMouseButton(e); // HACK: e.getButton() is 0 rather than BUTTON1 | BUTTON2.
if (left) {
KetPanel ketPanel = document.getKetPanel();
Position p = new Position(e.getX(), e.getY());
ketPanel.setDragLocation(p);
}
}
/**
* Invoked when the mouse cursor has been moved onto a component but no
* buttons have been pushed.
*/
@Override
public void mouseMoved(MouseEvent e) {
KetPanel ketPanel = document.getKetPanel();
Modes mode = document.getModes();
Responder responder = mode.getResponder();
setCurrentPosition(e.getX(), e.getY());
// Border what the mouse is currently over.
boolean normalMode = document.getModes().getDocumentState()==DocumentState.NORMAL;
if (normalMode) {
Position p = new Position(e.getX(), e.getY());
Box deepest = ketPanel.findDeepestBox(p);
ketPanel.markBorder(deepest);
ketPanel.repaint();
} else {
if (responder==mode.getMouseResponder()) {
mouseLoop.clear();
} else if (mode.getDocumentState().isTracked() || responder==mode.getNormalResponder()) {
mouseLoop.append(e.getX(), e.getY());
}
}
if (ketPanel.isPerspectiveDisplayMode()) { // Move this somewhere more appropriate?
ketPanel.repaint();
}
}
public MouseLoop getMouseLoop() {
return mouseLoop;
}
public Position getLastPosition() {
DocumentManager dm = getDocumentManager();
if (dm==null) return null;
return dm.getLastPosition();
}
public KetPanel getLastDestination() {
DocumentManager dm = getDocumentManager();
if (dm==null) return null;
return dm.getLastDestination();
}
public void setDestination(Component destination, Position p) {
DocumentManager dm = getDocumentManager();
if (dm==null) return;
dm.setDestination(destination, p);
}
private DocumentManager getDocumentManager() {
return document.getDocumentManager();
}
}