/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2001 Joerg Mueller <joergmueller@bigfoot.com>
*See COPYING for Details
*
*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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package freemind.modes.common.listeners;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import freemind.controller.NodeMouseMotionListener.NodeMouseMotionObserver;
import freemind.main.Tools;
import freemind.modes.ModeController;
import freemind.view.mindmapview.MainView;
import freemind.view.mindmapview.NodeView;
/**
* The MouseMotionListener which belongs to every NodeView.
* Handles delayed selection.
*/
public class CommonNodeMouseMotionListener implements NodeMouseMotionObserver {
private final ModeController c;
// Logging:
private static java.util.logging.Logger logger;
/** time in ms, overwritten by property time_for_delayed_selection */
private static Tools.IntHolder timeForDelayedSelection;
/** overwritten by property delayed_selection_enabled */
private static Tools.BooleanHolder delayedSelectionEnabled;
/**
* And a static method to reread this holder. This is used when the
* selection method is changed via the option menu.
*/
public void updateSelectionMethod() {
if (timeForDelayedSelection == null) {
timeForDelayedSelection = new Tools.IntHolder();
}
delayedSelectionEnabled = new Tools.BooleanHolder();
delayedSelectionEnabled.setValue(c.getFrame()
.getProperty("selection_method")
.equals("selection_method_direct") ? false : true);
/*
* set time for delay to infinity, if selection_method equals
* selection_method_by_click.
*/
if (c.getFrame().getProperty("selection_method")
.equals("selection_method_by_click")) {
timeForDelayedSelection.setValue(Integer.MAX_VALUE);
} else {
timeForDelayedSelection.setValue(Integer.parseInt(c.getFrame()
.getProperty("time_for_delayed_selection")));
}
}
private Timer timerForDelayedSelection;
/**
* The mouse has to stay in this region to enable the selection after a
* given time.
*/
private Rectangle controlRegionForDelayedSelection;
private MouseEvent mMousePressedEvent;
public CommonNodeMouseMotionListener(ModeController controller) {
c = controller;
if (logger == null)
logger = c.getFrame().getLogger(this.getClass().getName());
if (delayedSelectionEnabled == null)
updateSelectionMethod();
}
public void mouseMoved(MouseEvent e) {
logger.finest("Event: mouseMoved");
// Invoked when the mouse button has been moved on a component (with no
// buttons down).
MainView node = ((MainView) e.getComponent());
boolean isLink = (node).updateCursor(e.getX());
// links are displayed in the status bar:
if (isLink) {
c.getFrame().out(c.getLinkShortText(node.getNodeView().getModel()));
}
// test if still in selection region:
if (controlRegionForDelayedSelection != null
&& delayedSelectionEnabled.getValue()) {
if (!controlRegionForDelayedSelection.contains(e.getPoint())) {
// point is not in the region. start timer again and adjust
// region to the current point:
createTimer(e);
}
}
}
/** Invoked when a mouse button is pressed on a component and then dragged. */
public void mouseDragged(MouseEvent e) {
logger.fine("Event: mouseDragged");
// first stop the timer and select the node:
stopTimerForDelayedSelection();
NodeView nodeV = ((MainView) e.getComponent()).getNodeView();
// if dragged for the first time, select the node:
if (!c.getView().isSelected(nodeV))
c.extendSelection(e);
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
logger.finest("Event: mouseEntered");
if (!JOptionPane.getFrameForComponent(e.getComponent()).isFocused())
return;
createTimer(e);
// c.select(e);
}
public void mousePressed(MouseEvent e) {
logger.fine("Event: mousePressed");
// for Linux/Mac
mMousePressedEvent = e;
}
public void mouseExited(MouseEvent e) {
logger.finest("Event: mouseExited");
stopTimerForDelayedSelection();
}
public void mouseReleased(MouseEvent e) {
// handling click in mouseReleased rather than in mouseClicked
// provides better interaction. If mouse was slightly moved
// between pressed and released events, the event clicked
// is not triggered.
// The behavior is not tested on Linux.
logger.fine("Event: mouseReleased");
MouseEvent ev = e;
/*
* For Mac see
* https://developer.apple.com/library/mac/#documentation/Java/Conceptual/Java14Development/07-NativePlatformIntegration/NativePlatformIntegration.html
* */
if(Tools.isLinux() || Tools.isMacOsX()) {
ev = mMousePressedEvent;
}
handlePopupMenu(ev);
if (ev.isConsumed()) {
return;
}
if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
// FIXME Dimitry: Double Click comes after Plain Click combining
// (un)folding with editing
// if (e.getClickCount() % 2 == 0) {
// c.doubleClick(e);
// } else {
c.plainClick(e);
// }
e.consume();
}
}
protected void handlePopupMenu(MouseEvent e) {
// first stop the timer and select the node:
stopTimerForDelayedSelection();
logger.fine("Extending selection for " +e);
c.extendSelection(e);
// Right mouse <i>press</i> is <i>not</i> a popup trigger for Windows.
// Only Right mouse release is a popup trigger!
// OK, but Right mouse <i>press</i> <i>is</i> a popup trigger on Linux.
logger.fine("Looking for popup for " +e);
c.showPopupMenu(e);
}
protected Rectangle getControlRegion(Point2D p) {
// Create a small square around the given point.
int side = 8;
return new Rectangle((int) (p.getX() - side / 2),
(int) (p.getY() - side / 2), side, side);
}
public void createTimer(MouseEvent e) {
// stop old timer if present.*/
stopTimerForDelayedSelection();
/* Region to check for in the sequel. */
controlRegionForDelayedSelection = getControlRegion(e.getPoint());
timerForDelayedSelection = new Timer();
timerForDelayedSelection.schedule(
new timeDelayedSelection(c, e),
/*
* if the new selection method is not enabled we put 0 to get
* direct selection.
*/
(delayedSelectionEnabled.getValue()) ? timeForDelayedSelection
.getValue() : 0);
}
protected void stopTimerForDelayedSelection() {
// stop timer.
if (timerForDelayedSelection != null)
timerForDelayedSelection.cancel();
timerForDelayedSelection = null;
controlRegionForDelayedSelection = null;
}
protected class timeDelayedSelection extends TimerTask {
private final ModeController c;
private final MouseEvent e;
timeDelayedSelection(ModeController c, MouseEvent e) {
this.c = c;
this.e = e;
}
/** TimerTask method to enable the selection after a given time. */
public void run() {
/*
* formerly in ControllerAdapter. To guarantee, that point-to-select
* does not change selection if any meta key is pressed.
*/
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (e.getModifiers() == 0 && !c.isBlocked()
&& c.getView().getSelecteds().size() <= 1) {
c.extendSelection(e);
}
}
});
}
}
}