/*
* Freeplane - mind map editor
* Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is modified by Dimitry Polivaev in 2008.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.view.swing.ui.mindmapmode;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.util.Collection;
import javax.swing.JScrollPane;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.ui.DoubleClickTimer;
import org.freeplane.core.ui.IMouseListener;
import org.freeplane.core.ui.IEditHandler.FirstAction;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.Compat;
import org.freeplane.features.map.FreeNode;
import org.freeplane.features.map.MapController;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.map.SummaryNode;
import org.freeplane.features.map.mindmapmode.MMapController;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.ModeController;
import org.freeplane.features.nodelocation.LocationController;
import org.freeplane.features.nodelocation.LocationModel;
import org.freeplane.features.nodelocation.mindmapmode.MLocationController;
import org.freeplane.features.text.mindmapmode.MTextController;
import org.freeplane.view.swing.map.MainView;
import org.freeplane.view.swing.map.MapView;
import org.freeplane.view.swing.map.MouseArea;
import org.freeplane.view.swing.map.NodeView;
import org.freeplane.view.swing.ui.DefaultNodeMouseMotionListener;
/**
* The MouseMotionListener which belongs to every NodeView
*/
public class MNodeMotionListener extends DefaultNodeMouseMotionListener implements IMouseListener {
private Point dragStartingPoint = null;
private int originalHGap;
private int originalParentVGap;
private int originalShiftY;
private static final String EDIT_ON_DOUBLE_CLICK = "edit_on_double_click";
public MNodeMotionListener() {
}
Point getDragStartingPoint() {
return dragStartingPoint;
}
/**
*/
private int getHGapChange(final Point dragNextPoint, final NodeModel node) {
final Controller controller = Controller.getCurrentController();
final MapView mapView = ((MapView) controller.getViewController().getMapView());
int hGapChange = (int) ((dragNextPoint.x - dragStartingPoint.x) / mapView.getZoom());
if (node.isLeft()) {
hGapChange = -hGapChange;
}
return hGapChange;
}
/**
*/
private int getNodeShiftYChange(final Point dragNextPoint, final NodeModel node) {
final Controller controller = Controller.getCurrentController();
final MapView mapView = ((MapView) controller.getViewController().getMapView());
final int shiftYChange = (int) ((dragNextPoint.y - dragStartingPoint.y) / mapView.getZoom());
return shiftYChange;
}
/**
*/
private NodeView getNodeView(final MouseEvent e) {
return ((MainView) e.getSource()).getNodeView();
}
/**
*/
private int getVGapChange(final Point dragNextPoint, final NodeModel node) {
final Controller controller = Controller.getCurrentController();
final MapView mapView = ((MapView) controller.getViewController().getMapView());
final int vGapChange = (int) ((dragNextPoint.y - dragStartingPoint.y) / mapView.getZoom());
return vGapChange;
}
public boolean isDragActive() {
return dragStartingPoint != null;
}
@Override
public void mouseClicked(final MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2
&& doubleClickTimer.getDelay() > 0) {
final MainView mainView = (MainView) e.getComponent();
if (mainView.getMouseArea().equals(MouseArea.MOTION)) {
final Controller controller = Controller.getCurrentController();
MLocationController locationController = (MLocationController) LocationController
.getController(controller.getModeController());
if (e.getModifiersEx() == 0) {
final NodeView nodeV = getNodeView(e);
final NodeModel node = nodeV.getModel();
locationController.moveNodePosition(node, LocationModel.getModel(node).getVGap(),
LocationModel.HGAP, 0);
return;
}
if (Compat.isCtrlEvent(e)) {
final NodeView nodeV = getNodeView(e);
final NodeModel node = nodeV.getModel();
locationController.moveNodePosition(node, LocationModel.VGAP, LocationModel.getModel(node)
.getHGap(), LocationModel.getModel(node).getShiftY());
return;
}
}
else {
if (Compat.isPlainEvent(e) && !isInFoldingRegion(e)) {
final MTextController textController = (MTextController) MTextController.getController();
textController.getEventQueue().activate(e);
textController.edit(FirstAction.EDIT_CURRENT, false);
}
}
}
super.mouseClicked(e);
}
public void mouseMoved(final MouseEvent e) {
if (isDragActive())
return;
final MainView v = (MainView) e.getSource();
if (v.isInDragRegion(e.getPoint())) {
v.setMouseArea(MouseArea.MOTION);
v.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
return;
}
super.mouseMoved(e);
}
@Override
public void mouseExited(MouseEvent e) {
if (!isDragActive())
super.mouseExited(e);
}
@Override
public void mousePressed(MouseEvent e) {
doubleClickTimer.cancel();
setClickDelay();
if (isInDragRegion(e)) {
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == (InputEvent.BUTTON1_DOWN_MASK)) {
stopTimerForDelayedSelection();
final NodeView nodeV = getNodeView(e);
final Point point = e.getPoint();
findGridPoint(point);
UITools.convertPointToAncestor(nodeV, point, JScrollPane.class);
setDragStartingPoint(point, nodeV.getModel());
}
}
else
super.mousePressed(e);
}
public void mouseDragged(final MouseEvent e) {
if (!isDragActive())
return;
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == (InputEvent.BUTTON1_DOWN_MASK)) {
final MainView mainView = (MainView) e.getSource();
final NodeView nodeV = getNodeView(e);
final MapView mapView = nodeV.getMap();
final Point point = e.getPoint();
findGridPoint(point);
UITools.convertPointToAncestor(nodeV, point, JScrollPane.class);
ModeController c = Controller.getCurrentController().getModeController();
final Point dragNextPoint = point;
if (!Compat.isCtrlEvent(e)) {
final NodeModel node = nodeV.getModel();
final LocationModel locationModel = LocationModel.createLocationModel(node);
final int hGapChange = getHGapChange(dragNextPoint, node);
if(hGapChange != 0){
locationModel.setHGap(originalHGap + hGapChange);
}
final int shiftYChange = getNodeShiftYChange(dragNextPoint, node);
if(shiftYChange != 0){
locationModel.setShiftY(originalShiftY + shiftYChange);
}
if(hGapChange != 0 || shiftYChange != 0)
c.getMapController().nodeRefresh(node);
else
return;
}
else {
final NodeModel parentNode = nodeV.getVisibleParentView().getModel();
final int vGapChange = getVGapChange(dragNextPoint, parentNode);
if(vGapChange != 0){
LocationModel.createLocationModel(parentNode).setVGap(Math.max(0, originalParentVGap - vGapChange));
final MapController mapController = c.getMapController();
mapController.nodeRefresh(parentNode);
mapController.nodeRefresh(nodeV.getModel());
}
else
return;
}
EventQueue.invokeLater(new Runnable() {
public void run() {
final Rectangle r = mainView.getBounds();
UITools.convertRectangleToAncestor(mainView.getParent(), r, mapView);
final boolean isEventPointVisible = mapView.getVisibleRect().contains(r);
if (!isEventPointVisible) {
mapView.scrollRectToVisible(r);
}
}
});
}
}
private void findGridPoint(Point point) {
final int gridSize = ResourceController.getResourceController().getIntProperty("grid_size");
if (gridSize <= 2) {
return;
}
point.x -= point.x % gridSize;
point.y -= point.y % gridSize;
}
@Override
public void mouseReleased(final MouseEvent e) {
final MainView v = (MainView) e.getSource();
if (!v.contains(e.getX(), e.getY())) {
v.setMouseArea(MouseArea.OUT);
}
if (!isDragActive()) {
super.mouseReleased(e);
return;
}
final NodeView nodeV = getNodeView(e);
final NodeModel node = nodeV.getModel();
final ModeController modeController = nodeV.getMap().getModeController();
final NodeModel parentNode = nodeV.getModel().getParentNode();
final int parentVGap = LocationModel.getModel(parentNode).getVGap();
int hgap = LocationModel.getModel(node).getHGap();
final int shiftY = LocationModel.getModel(node).getShiftY();
adjustNodeIndices(nodeV);
resetPositions(node);
final Controller controller = modeController.getController();
MLocationController locationController = (MLocationController) LocationController.getController(controller
.getModeController());
locationController.moveNodePosition(node, parentVGap, hgap, shiftY);
stopDrag();
}
private void adjustNodeIndices(final NodeView nodeV) {
NodeModel[] selectedsBackup = null;
final NodeModel node = nodeV.getModel();
if (FreeNode.isFreeNode(node)) {
selectedsBackup = adjustNodeIndexBackupSelection(nodeV, selectedsBackup);
}
else {
final MapView map = nodeV.getMap();
final NodeModel[] siblingNodes = node.getParentNode().getChildren().toArray(new NodeModel[] {});
for (NodeModel sibling : siblingNodes) {
if (FreeNode.isFreeNode(sibling)) {
final NodeView siblingV = map.getNodeView(sibling);
selectedsBackup = adjustNodeIndexBackupSelection(siblingV, selectedsBackup);
}
}
}
if (selectedsBackup != null) {
final ModeController modeController = nodeV.getMap().getModeController();
final Controller controller = modeController.getController();
controller.getSelection().replaceSelection(selectedsBackup);
}
}
private NodeModel[] adjustNodeIndexBackupSelection(final NodeView nodeV, NodeModel[] selectedsBackup) {
final NodeModel node = nodeV.getModel();
boolean isLeft = nodeV.isLeft();
final int newIndex = calculateNewNodeIndex(nodeV, isLeft, 0, node.getParentNode().getChildCount());
if (newIndex != -1) {
final ModeController modeController = nodeV.getMap().getModeController();
MMapController mapController = (MMapController) modeController.getMapController();
if (selectedsBackup == null) {
final Collection<NodeModel> selecteds = mapController.getSelectedNodes();
selectedsBackup = selecteds.toArray(new NodeModel[selecteds.size()]);
}
mapController.moveNode(node, node.getParentNode(), newIndex, isLeft, false);
}
return selectedsBackup;
}
public int getRefX(final NodeView node) {
return node.getContent().getX() + node.getContent().getWidth() / 2;
}
private int calculateNewNodeIndex(final NodeView nodeV, final boolean left, final int start, final int end) {
final NodeModel node = nodeV.getModel();
if (SummaryNode.isSummaryNode(node))
return -1;
final int nodeY = getRefY(nodeV);
final NodeView parent = nodeV.getParentView();
int newIndex = 0;
int oldIndex = -1;
int wrondSideCount = 0;
for (int i = start; i < end; i++) {
final Component component = parent.getComponent(i);
if (!(component instanceof NodeView))
continue;
NodeView sibling = (NodeView) component;
if (sibling.isLeft() == left && !SummaryNode.isSummaryNode(sibling.getModel()) && getRefY(sibling) > nodeY)
break;
else {
if (sibling != nodeV) {
newIndex++;
if (sibling.isLeft() != left)
wrondSideCount++;
else
wrondSideCount = 0;
}
else {
oldIndex = i;
}
}
}
final int result = newIndex - wrondSideCount;
if (result == oldIndex)
return -1;
return result;
}
private int getRefY(NodeView sibling) {
return sibling.getY() + sibling.getContent().getY();
}
/**
*/
private void resetPositions(final NodeModel node) {
final LocationModel locationModel = LocationModel.getModel(node.getParentNode());
locationModel.setVGap(originalParentVGap);
LocationModel.getModel(node).setHGap(originalHGap);
LocationModel.getModel(node).setShiftY(originalShiftY);
}
void setDragStartingPoint(final Point point, final NodeModel node) {
dragStartingPoint = point;
if (point != null) {
originalParentVGap = LocationModel.getModel(node.getParentNode()).getVGap();
originalHGap = LocationModel.getModel(node).getHGap();
originalShiftY = LocationModel.getModel(node).getShiftY();
}
else {
originalParentVGap = originalHGap = originalShiftY = 0;
}
}
private void stopDrag() {
setDragStartingPoint(null, null);
}
private void setClickDelay() {
if (ResourceController.getResourceController().getBooleanProperty(EDIT_ON_DOUBLE_CLICK))
doubleClickTimer.setDelay(DoubleClickTimer.MAX_TIME_BETWEEN_CLICKS);
else {
doubleClickTimer.setDelay(0);
}
}
}