diff --git a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java index ad15a64a..2891d7bc 100644 --- a/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java +++ b/diylc/diylc-core/src/org/diylc/common/IPlugInPort.java @@ -60,7 +60,8 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP public static final String EXPORT_GRID_KEY = "exportGrid"; public static final String STICKY_POINTS_KEY = "stickyPoints"; public static final String METRIC_KEY = "metric"; - public static final String SNAP_TO_GRID_KEY = "snapToGrid"; + + public static final String SNAP_TO_KEY = "snapTo"; public static final String CONTINUOUS_CREATION_KEY = "continuousCreation"; public static final String AUTO_EDIT_KEY = "autoEdit"; public static final String ABNORMAL_EXIT_KEY = "abnormalExit"; @@ -75,7 +76,12 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP public static final String HIGHLIGHT_CONTINUITY_AREA = "highlightContinuityArea"; public static final String HARDWARE_ACCELERATION = "hardwareAcceleration"; public static final String EXTRA_SPACE_KEY = "extraSpace"; - public static final String FAVORITES_KEY = "favorites"; + public static final String FAVORITES_KEY = "favorites"; + + public static final String SNAP_TO_NONE = "None"; + public static final String SNAP_TO_GRID = "Grid"; + public static final String SNAP_TO_OBJECTS = "Objects"; + public static final String SNAP_TO_DEFAULT = SNAP_TO_GRID; public static final int DND_TOGGLE_STICKY = 0x1; public static final int DND_TOGGLE_SNAP = 0x40000000; diff --git a/diylc/diylc-core/src/org/diylc/presenter/CalcUtils.java b/diylc/diylc-core/src/org/diylc/presenter/CalcUtils.java index 08740d4e..204255e6 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/CalcUtils.java +++ b/diylc/diylc-core/src/org/diylc/presenter/CalcUtils.java @@ -21,8 +21,10 @@ */ package org.diylc.presenter; -import java.awt.Point; - +import java.awt.Point; +import java.util.List; + +import org.diylc.core.IDIYComponent; import org.diylc.core.measures.Size; public class CalcUtils { @@ -42,5 +44,17 @@ public class CalcUtils { int x = roundToGrid(point.x, gridSpacing); int y = roundToGrid(point.y, gridSpacing); point.setLocation(x, y); + } + + public static void snapPointToObjects(Point point, Size gridSpacing, IDIYComponent component, List> components) { + for (IDIYComponent c : components) { + if (c == component) + continue; + for (int i = 0; i < c.getControlPointCount(); i++) + if (c.isControlPointSticky(i) && point.distance(c.getControlPoint(i)) < gridSpacing.convertToPixels() / 2) { + point.setLocation(c.getControlPoint(i)); + return; + } + } } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java index 8a03e74b..b380159f 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java @@ -740,10 +740,12 @@ public class Presenter implements IPlugInPort { return false; } - boolean snapToGrid = ConfigurationManager.getInstance().readBoolean(IPlugInPort.SNAP_TO_GRID_KEY, true); + String snapTo = ConfigurationManager.getInstance().readString(IPlugInPort.SNAP_TO_KEY, IPlugInPort.SNAP_TO_DEFAULT); if (shiftDown) { - snapToGrid = !snapToGrid; + snapTo = IPlugInPort.SNAP_TO_NONE; } + boolean snapToGrid = snapTo.equalsIgnoreCase(IPlugInPort.SNAP_TO_GRID); + boolean snapToObjects = snapTo.equalsIgnoreCase(IPlugInPort.SNAP_TO_OBJECTS); if (altDown) { Project oldProject = null; @@ -804,7 +806,7 @@ public class Presenter implements IPlugInPort { } Project oldProject = currentProject.clone(); - moveComponents(controlPointMap, dx, dy, snapToGrid); + moveComponents(controlPointMap, dx, dy, snapToGrid, snapToObjects); messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, currentProject.clone(), "Move Selection"); messageDispatcher.dispatchMessage(EventType.REPAINT); return true; @@ -1009,7 +1011,7 @@ public class Presenter implements IPlugInPort { int dy = (int) yOffset.convertToPixels(); Project oldProject = currentProject.clone(); - moveComponents(controlPointMap, dx, dy, false); + moveComponents(controlPointMap, dx, dy, false, false); messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, currentProject.clone(), "Move Selection"); messageDispatcher.dispatchMessage(EventType.REPAINT); drawingManager.clearContinuityArea(); @@ -1171,10 +1173,17 @@ public class Presenter implements IPlugInPort { } private boolean isSnapToGrid() { - boolean snapToGrid = ConfigurationManager.getInstance().readBoolean(IPlugInPort.SNAP_TO_GRID_KEY, true); + String snapTo = ConfigurationManager.getInstance().readString(IPlugInPort.SNAP_TO_KEY, IPlugInPort.SNAP_TO_DEFAULT); if (this.dragAction == IPlugInPort.DND_TOGGLE_SNAP) - snapToGrid = !snapToGrid; - return snapToGrid; + return false; + return snapTo.equalsIgnoreCase(IPlugInPort.SNAP_TO_GRID); + } + + private boolean isSnapToObjects() { + String snapTo = ConfigurationManager.getInstance().readString(IPlugInPort.SNAP_TO_KEY, IPlugInPort.SNAP_TO_DEFAULT); + if (this.dragAction == IPlugInPort.DND_TOGGLE_SNAP) + return false; + return snapTo.equalsIgnoreCase(IPlugInPort.SNAP_TO_OBJECTS); } @Override @@ -1188,7 +1197,7 @@ public class Presenter implements IPlugInPort { int dx = (scaledPoint.x - previousDragPoint.x); int dy = (scaledPoint.y - previousDragPoint.y); - Point actualD = moveComponents(this.controlPointMap, dx, dy, isSnapToGrid()); + Point actualD = moveComponents(this.controlPointMap, dx, dy, isSnapToGrid(), isSnapToObjects()); if (actualD == null) return true; @@ -1212,7 +1221,7 @@ public class Presenter implements IPlugInPort { return true; } - private Point moveComponents(Map, Set> controlPointMap, int dx, int dy, boolean snapToGrid) { + private Point moveComponents(Map, Set> controlPointMap, int dx, int dy, boolean snapToGrid, boolean snapToObjects) { // After we make the transfer and snap to grid, calculate actual dx // and dy. We'll use them to translate the previous drag point. int actualDx = 0; @@ -1232,6 +1241,8 @@ public class Presenter implements IPlugInPort { testPoint.translate(dx, dy); if (snapToGrid) { CalcUtils.snapPointToGrid(testPoint, currentProject.getGridSpacing()); + } else if (snapToObjects) { + CalcUtils.snapPointToObjects(testPoint, currentProject.getGridSpacing(), entry.getKey(), currentProject.getComponents()); } actualDx = testPoint.x - firstPoint.x; diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 36d72ae9..5c0ca606 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -2714,6 +2714,10 @@ NEW_FEATURE New app bundle for Mac OS using appbundler instead of javabundler + + NEW_FEATURE + Added "Snap To Objects" feature that snaps control points to control points of the nearest components + NEW_FEATURE New Mac OS app icon diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index 376b0335..3a4a9a1c 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index 376b0335..3a4a9a1c 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java index 554523bd..829ff9e7 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java +++ b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java @@ -241,6 +241,10 @@ public class ActionFactory { String tipKey) { return new ConfigAction(plugInPort, title, configKey, defaultValue, tipKey); } + + public ToggleAction createToggleAction(String title, String configKey, String groupName, String defaultValue) { + return new ToggleAction(title, configKey, groupName, defaultValue); + } public ThemeAction createThemeAction(IPlugInPort plugInPort, Theme theme) { return new ThemeAction(plugInPort, theme); @@ -1575,6 +1579,27 @@ public class ActionFactory { plugInPort.refresh(); } } + + public static class ToggleAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + private String configKey; + + public ToggleAction(String title, String configKey, String groupName, String defaultValue) { + super(); + this.configKey = configKey; + putValue(AbstractAction.NAME, title); + putValue(IView.RADIO_BUTTON_GROUP_KEY, groupName); + putValue(AbstractAction.SELECTED_KEY, ConfigurationManager.getInstance().readString(configKey, defaultValue).equalsIgnoreCase(title)); + } + + @Override + public void actionPerformed(ActionEvent e) { + LOG.info(getValue(AbstractAction.NAME) + " toggle triggered"); + ConfigurationManager.getInstance().writeValue(configKey, getValue(AbstractAction.NAME)); + } + } public static class ThemeAction extends AbstractAction { diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java index 55dc3b1c..fb00ef9d 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java @@ -46,6 +46,7 @@ public class ConfigPlugin implements IPlugIn { private static final String CONFIG_MENU = "Config"; private static final String THEME_MENU = "Theme"; + private static final String SNAP_MENU = "Snap To..."; private static final String COMPONENT_BROWSER_MENU = "Toolbox"; public static final String COMPONENT_BROWSER = "componentBrowser"; public static final String SEARCHABLE_TREE = "Searchable Tree"; @@ -96,12 +97,19 @@ public class ConfigPlugin implements IPlugIn { swingUI.injectMenuAction( ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true), CONFIG_MENU); + swingUI.injectSubmenu(SNAP_MENU, null, CONFIG_MENU); swingUI.injectMenuAction( ActionFactory.getInstance().createConfigAction(plugInPort, "Show Grid", IPlugInPort.SHOW_GRID_KEY, true), CONFIG_MENU); swingUI.injectMenuAction( - ActionFactory.getInstance().createConfigAction(plugInPort, "Snap to Grid", IPlugInPort.SNAP_TO_GRID_KEY, true), - CONFIG_MENU); + ActionFactory.getInstance().createToggleAction("None", IPlugInPort.SNAP_TO_KEY, SNAP_MENU, IPlugInPort.SNAP_TO_DEFAULT), + SNAP_MENU); + swingUI.injectMenuAction( + ActionFactory.getInstance().createToggleAction("Grid", IPlugInPort.SNAP_TO_KEY, SNAP_MENU, IPlugInPort.SNAP_TO_DEFAULT), + SNAP_MENU); + swingUI.injectMenuAction( + ActionFactory.getInstance().createToggleAction("Objects", IPlugInPort.SNAP_TO_KEY, SNAP_MENU, IPlugInPort.SNAP_TO_DEFAULT), + SNAP_MENU); swingUI.injectMenuAction( ActionFactory.getInstance() .createConfigAction(plugInPort, "Sticky Points", IPlugInPort.STICKY_POINTS_KEY, true), CONFIG_MENU);