/* * funCKit - functional Circuit Kit * Copyright (C) 2013 Lukas Elsner <open@mindrunner.de> * Copyright (C) 2013 Peter Dahlberg <catdog2@tuxzone.org> * Copyright (C) 2013 Julian Stier <mail@julian-stier.de> * Copyright (C) 2013 Sebastian Vetter <mail@b4sti.eu> * Copyright (C) 2013 Thomas Poxrucker <poxrucker_t@web.de> * Copyright (C) 2013 Alexander Treml <alex.treml@directbox.com> * * 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 de.sep2011.funckit.model.sessionmodel; import de.sep2011.funckit.model.graphmodel.Brick; import de.sep2011.funckit.model.graphmodel.Circuit; import de.sep2011.funckit.model.graphmodel.Component; import de.sep2011.funckit.model.graphmodel.Element; import de.sep2011.funckit.observer.AbstractObservable; import de.sep2011.funckit.observer.EditPanelModelInfo; import de.sep2011.funckit.observer.EditPanelModelObserver; import de.sep2011.funckit.util.Log; import de.sep2011.funckit.view.EditPanel; import java.awt.Cursor; import java.awt.Point; import java.awt.geom.AffineTransform; import java.util.Deque; import java.util.LinkedHashSet; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; /** * Model object that is assigned to every editing tab for storing session * information like current selected elements or the circuit object that is * modeled on. */ public class EditPanelModel extends AbstractObservable<EditPanelModelObserver, EditPanelModelInfo> { private AffineTransform translation; private AffineTransform zoom; private double centerX; private double centerY; private Brick activeBrick; public enum ToolMode { DEFAULT_MODE, CREATE_WIRE_PATH_FROM_AP, CREATE_WIRE_PATH_FROM_WIRE, CREATE_SINGLE_WIRE_MODE, SELECT_RECT_MODE, MOVE_ELEMENT_MODE } private ToolMode toolMode; /** * Associated circuit object, that can be part of a component type and thus * be indirect part of another circuit. */ private Circuit circuit; /** * The path of components the circuit lays in. */ private Deque<Component> componentStack; /** * Current selected elements on edit panel. */ private Set<Element> selectedElements; /** * Elements that are currently moved (copy of selected elements with moved * position). */ private Set<Element> ghosts; /** * Stores the current cursor for the edit panel. */ private Cursor cursor; /** * Current selected brick for the Gridlock! */ private Brick selectedBrick; private Point selectionStart; private Point selectionEnd; private Point dragStartPoint; /** * First Brick selected to connect. Here the Outputs are used. */ private Brick multiConnectBrick1; /** * Second Brick selected to connect. Here the Inputs are used. */ private Brick multiConnectBrick2; /** * Create a new EditPanelModel. * * @param circuit * the associated circuit, not null * @param name * name of the EditPanel. * @param componentStack */ public EditPanelModel(Circuit circuit, Deque<Component> componentStack) { checkNotNull(circuit, "The Circuit is null but should not"); assert componentStack != null; assert circuit != null; initialize(circuit, componentStack); } private void initialize(Circuit circuit, Deque<Component> componentStack) { translation = new AffineTransform(); zoom = new AffineTransform(); this.circuit = circuit; this.componentStack = componentStack; selectedElements = new LinkedHashSet<Element>(); ghosts = new LinkedHashSet<Element>(); toolMode = ToolMode.DEFAULT_MODE; initInfo(EditPanelModelInfo.getInfo()); cursor = Cursor.getDefaultCursor(); } /** * Returns the current {@link Cursor} for the {@link EditPanel}. * * @return the current {@link Cursor} for the {@link EditPanel} */ public Cursor getCursor() { return cursor; } /** * Sets the Cursor. * * @param cursor * the new cursor, if the same do not notify observers. */ public void setCursor(Cursor cursor) { if (!this.cursor.equals(cursor)) { this.cursor = cursor; setChanged(); getInfo().setCursorChanged(true); notifyObserversIfAuto(); } } public void setActiveBrick(Brick activeBrick) { boolean changed = false; if (this.activeBrick != activeBrick) { changed = true; } this.activeBrick = activeBrick; if (changed) { setChanged(); getInfo().setActiveChanged(true); notifyObserversIfAuto(); } } public Brick getActiveBrick() { return activeBrick; } /** * Returns the associated {@link Circuit} of this Model. * * @return the circuit */ public Circuit getCircuit() { return this.circuit; } /** * @return The current Tool mode */ public ToolMode getToolMode() { return toolMode; } /** * @param toolMode * the new Tool Mode, not null */ public void setToolMode(ToolMode toolMode) { Log.gl().debug("Set toolmode to " + toolMode ); this.toolMode = toolMode; } /** * Return the start point of the selection start and end define a rectangle. * * @return the start point of the selection */ public Point getSelectionStart() { return selectionStart; } /** * Sets the start point of the selection start and end define a rectangle. * * @param selectionStart * start point of the selection */ public void setSelectionStart(Point selectionStart) { this.selectionStart = selectionStart; setChanged(); getInfo().setSelectionChanged(); notifyObserversIfAuto(); } /** * Return the end point of the selection start and end define a rectangle. * * @return the end point of the selection */ public Point getSelectionEnd() { return selectionEnd; } /** * Sets the end point of the selection start and end define a rectangle. * * @param selectionEnd * end point of the selection */ public void setSelectionEnd(Point selectionEnd) { this.selectionEnd = selectionEnd; setChanged(); getInfo().setSelectionChanged(); notifyObserversIfAuto(); } /** * Getter method for selected elements. * * @return Set of selected elements. */ public Set<Element> getSelectedElements() { return selectedElements; } /** * Specifies current selected elements. Has to be non null. * * @param selectedElements * set of current selected elements */ public void setSelectedElements(Set<Element> selectedElements) { assert selectedElements != null; this.selectedElements = selectedElements; setChanged(); getInfo().setSelectionChanged(); notifyObserversIfAuto(); } /** * @param b * selected brick */ public void setSelectedBrick(Brick b) { selectedBrick = b; } public Brick getSelectedBrick() { return selectedBrick; } public Brick getMultiConnectBrick1() { return multiConnectBrick1; } public void setMultiConnectBrick1(Brick multiConnectBrick1) { this.multiConnectBrick1 = multiConnectBrick1; } public Brick getMultiConnectBrick2() { return multiConnectBrick2; } public void setMultiConnectBrick2(Brick multiConnectBrick2) { this.multiConnectBrick2 = multiConnectBrick2; } /** * Getter method for current moving ghosts. * * @return current moving ghosts. */ public Set<Element> getGhosts() { return ghosts; } /** * Returns the current ghost Elements to accept. (e.g. on moving Elements) * * @param ghosts * the current ghost Elements to accept */ public void setGhosts(Set<Element> ghosts) { assert ghosts != null; this.ghosts = ghosts; setChanged(); getInfo().setGhostsChanged(); notifyObserversIfAuto(); } /** * Get the path of components the circuit lays in. * * @return parentComponent of the circuit. */ public Deque<Component> getComponentStack() { return componentStack; } /** * Returns a {@link AffineTransform} to control drawing of the * {@link EditPanel}. If you change it, call * {@link #transformationChanged()} * * @return The current {@link AffineTransform} for this {@link EditPanel} * @since implementation */ public AffineTransform getTransformation() { AffineTransform inverseCenter = AffineTransform.getTranslateInstance(-centerX, -centerY); AffineTransform transformation = inverseCenter; // 4. move back to edge // transformation.concatenate(AffineTransform.getShearInstance(0, // -0.2)); transformation.concatenate(zoom); // 3. zoom transformation.concatenate(AffineTransform.getTranslateInstance( centerX, centerY)); // 2. move to center transformation.concatenate(translation); // 1. move to position return transformation; } public void translate(double tx, double ty) { translation.translate(-tx, -ty); transformationChanged(); } public void setTranslationX(double tx) { translation.setToTranslation(-tx, translation.getTranslateY()); transformationChanged(); } public void setTranslationY(double ty) { translation.setToTranslation(translation.getTranslateX(), -ty); transformationChanged(); } public void zoom(double zoom) { this.zoom.scale(zoom, zoom); transformationChanged(); } public void setZoom(double newZoom) { zoom = AffineTransform.getScaleInstance(newZoom, newZoom); transformationChanged(); } public void setCenter(double x, double y) { double zoom_mat[] = new double[6]; double dx = 0; double dy = 0; zoom.getMatrix(zoom_mat); /* * calculate translation correction so that the new center doesn't * affect the current view (the new transformation matrix equals the old * one - be aware that this has a loss of precision!) */ if (zoom_mat[0] != 0) { dx = centerX + x + (-x - centerX) / zoom_mat[0]; } if (zoom_mat[3] != 0) { dy = centerY + y + (-y - centerY) / zoom_mat[3]; } translation.translate(dx, dy); centerX = -x; centerY = -y; transformationChanged(); } public Point getTranslation() { return new Point((int) translation.getTranslateX(), (int) translation.getTranslateY()); } // public void setRotation(...) ?? /** * This method has to be called after doing some change on the * {@link AffineTransform} object. * * @since implementation */ private void transformationChanged() { setChanged(); getInfo().setTransformChanged(true); notifyObserversIfAuto(); } /** * returns the Point where the viewport dragging started. * * @return the Point where the viewport dragging started, null indicates * that dragging is not enabled * @since implementation */ public Point getDragStartPoint() { return dragStartPoint; } /** * set the Point where the viewport dragging started. * * @param dragStartPoint * the Point where the viewport dragging started, null indicates * that dragging is not enabled * @since implementation */ public void setDragStartPoint(Point dragStartPoint) { this.dragStartPoint = dragStartPoint; } public boolean hasMainCircuit() { return componentStack.isEmpty(); } @Override public void notifyObserver(EditPanelModelInfo i, EditPanelModelObserver obs) { obs.editPanelModelChanged(this, i); } }