package net.sourceforge.fidocadj.circuit.controllers;
import java.util.*;
import net.sourceforge.fidocadj.circuit.*;
import net.sourceforge.fidocadj.circuit.model.*;
import net.sourceforge.fidocadj.geom.*;
import net.sourceforge.fidocadj.layers.*;
import net.sourceforge.fidocadj.primitives.*;
import net.sourceforge.fidocadj.graphic.*;
/** CopyPasteActions: contains a controller which can perform handle drag and
move actions on a primitive database
<pre>
This file is part of FidoCadJ.
FidoCadJ 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.
FidoCadJ 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 FidoCadJ. If not,
@see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>.
Copyright 2014-2016 by Davide Bucci
</pre>
@author Davide Bucci
*/
public class HandleActions
{
private final DrawingModel dmp;
private final EditorActions edt;
private final UndoActions ua;
private final SelectionActions sa;
// ******** DRAG & INTERFACE *********
// True if we are at the beginning of a dragging operation.
private boolean firstDrag;
// The graphic primitive being treated.
private GraphicPrimitive primBeingDragged;
// The handle of the active graphic primitive being treated.
private int handleBeingDragged;
// Old cursor position for handle drag.
private int opx;
private int opy;
// True if the primitive has moved while dragging.
private boolean hasMoved;
// Other old cursor position for handle drag...
private int oldpx;
private int oldpy;
/** Standard constructor: provide the database class.
@param pp the drawing model
@param e the editor controller
@param s the selection controller
@param u the undo controller
*/
public HandleActions (DrawingModel pp, EditorActions e,
SelectionActions s,
UndoActions u)
{
dmp=pp;
edt=e;
ua=u;
sa=s;
firstDrag=false;
handleBeingDragged=GraphicPrimitive.NO_DRAG;
}
/** Drag all the selected primitives during a drag operation.
Position the primitives in the given (screen) position
@param cc the element containing the drawing, which can receive
repaint callbacks.
@param px the x position (screen coordinates).
@param py the y position (screen coordinates).
@param cs the coordinate mapping.
*/
public void dragPrimitives(PrimitivesParInterface cc, int px, int py,
MapCoordinates cs)
{
// Check if we are effectively dragging the whole primitive...
if(handleBeingDragged!=GraphicPrimitive.DRAG_PRIMITIVE)
return;
firstDrag=false;
int dx=cs.unmapXsnap(px)-oldpx;
int dy=cs.unmapYsnap(py)-oldpy;
oldpx=cs.unmapXsnap(px);
oldpy=cs.unmapXsnap(py);
if(dx==0 && dy==0)
return;
// Here we adjust the new positions for all selected elements...
for (GraphicPrimitive g : dmp.getPrimitiveVector()){
if(g.getSelected()) {
// This code is needed to ensure that all layer are printed
// when dragging a component (it solves bug #24)
if (g instanceof PrimitiveMacro) {
((PrimitiveMacro)g).setDrawOnlyLayer(-1);
}
for(int j=0; j<g.getControlPointNumber();++j){
g.virtualPoint[j].x+=dx;
g.virtualPoint[j].y+=dy;
// Here we show the new place of the primitive.
}
g.setChanged(true);
}
}
cc.forcesRepaint();
}
/** Start dragging handle. Check if the pointer is on the handle of a
primitive and if it is the case, enter the dragging state.
@param px the (screen) x coordinate of the pointer.
@param py the (screen) y coordinate of the pointer.
@param tolerance the tolerance (screen. i.e. no of pixel).
@param multiple specifies whether multiple selection is active.
@param cs the coordinate mapping to be used.
*/
public void dragHandleStart(int px, int py, int tolerance, boolean multiple,
MapCoordinates cs)
{
int i;
int isel=0;
int mindistance=Integer.MAX_VALUE;
int distance=mindistance;
int layer;
hasMoved=false;
GraphicPrimitive gp;
Vector<LayerDesc> layerV=dmp.getLayers();
oldpx=cs.unmapXnosnap(px);
oldpy=cs.unmapXnosnap(py);
firstDrag=true;
int sptol=Math.abs(cs.unmapXnosnap(px+tolerance)-cs.unmapXnosnap(px));
if (sptol<2) sptol=2;
// Search for the closest primitive to the given point
// Performs a cycle through all primitives and check their
// distance.
for (i=0; i<dmp.getPrimitiveVector().size(); ++i){
gp=(GraphicPrimitive)dmp.getPrimitiveVector().get(i);
layer= gp.getLayer();
// Does not allow for selecting an invisible primitive
if(layer<layerV.size() &&
!((LayerDesc)layerV.get(layer)).isVisible &&
!(gp instanceof PrimitiveMacro))
continue;
if(gp.selectedState){
// Verify if the pointer is on a handle
handleBeingDragged=gp.onHandle(cs, px, py);
if(handleBeingDragged!=GraphicPrimitive.NO_DRAG){
primBeingDragged=gp;
continue;
}
}
distance=gp.getDistanceToPoint(oldpx,oldpy);
if (distance<=mindistance) {
isel=i;
mindistance=distance;
}
}
// Verify if the whole primitive should be drag
if (mindistance<sptol && handleBeingDragged<0){
primBeingDragged=
(GraphicPrimitive)dmp.getPrimitiveVector().get(isel);
if (!multiple && !primBeingDragged.getSelected())
sa.setSelectionAll(false);
if(!multiple) {
primBeingDragged.setSelected(true);
}
handleBeingDragged=GraphicPrimitive.DRAG_PRIMITIVE;
firstDrag=true;
oldpx=cs.unmapXsnap(px);
oldpy=cs.unmapXsnap(py);
} else if (handleBeingDragged<0) {
// We want to select things in a rectangular area
oldpx=cs.unmapXsnap(px);
oldpy=cs.unmapXsnap(py);
handleBeingDragged=GraphicPrimitive.RECT_SELECTION;
}
}
/** End dragging handle.
@param CC the editor object
@param px the (screen) x coordinate of the pointer.
@param py the (screen) y coordinate of the pointer.
@param multiple specifies whether multiple selection is active.
@param cs the coordinate mapping to be used.
*/
public void dragHandleEnd(PrimitivesParInterface CC, int px, int py,
boolean multiple,
MapCoordinates cs)
{
// Check if we are effectively dragging something...
CC.setEvidenceRect(0,0,-1,-1);
if(handleBeingDragged<0){
if(handleBeingDragged==GraphicPrimitive.RECT_SELECTION){
int xa=Math.min(oldpx, cs.unmapXnosnap(px));
int ya=Math.min(oldpy, cs.unmapYnosnap(py));
int xb=Math.max(oldpx, cs.unmapXnosnap(px));
int yb=Math.max(oldpy, cs.unmapYnosnap(py));
if(!multiple) sa.setSelectionAll(false);
edt.selectRect(xa, ya, xb-xa, yb-ya);
}
// Test if we are anyway dragging an entire primitive
if(handleBeingDragged==GraphicPrimitive.DRAG_PRIMITIVE &&
hasMoved && ua!=null)
ua.saveUndoState();
handleBeingDragged=GraphicPrimitive.NO_DRAG;
return;
}
handleBeingDragged=GraphicPrimitive.NO_DRAG;
if(ua!=null) ua.saveUndoState();
}
/** Drag a handle.
@param CC the editor object.
@param px the (screen) x coordinate of the pointer.
@param py the (screen) y coordinate of the pointer.
@param cs the coordinates mapping to be used.
@param isControl true if the control key is held down.
*/
public void dragHandleDrag(PrimitivesParInterface CC,
int px, int py, MapCoordinates cs, boolean isControl)
{
hasMoved=true;
boolean flip=false;
// Check if we are effectively dragging a handle...
if(handleBeingDragged<0){
if(handleBeingDragged==GraphicPrimitive.DRAG_PRIMITIVE)
dragPrimitives(CC, px, py, cs);
// if not, we are performing a rectangular selection
if(handleBeingDragged==GraphicPrimitive.RECT_SELECTION) {
int xa = cs.mapXi(oldpx, oldpy, false);
int ya = cs.mapYi(oldpx, oldpy, false);
int xb = opx;
int yb = opy;
if(opx>xa && px<xa)
flip=true;
if(opy>ya && py<ya)
flip=true;
if(!firstDrag) {
int a,b,c,d;
a = Math.min(xa,xb);
b = Math.min(ya,yb);
c = Math.abs(xb-xa);
d = Math.abs(yb-ya);
xb=px;
yb=py;
opx=px;
opy=py;
CC.setEvidenceRect(Math.min(xa,xb), Math.min(ya,yb),
Math.abs(xb-xa), Math.abs(yb-ya));
a=Math.min(a, Math.min(xa,xb));
b=Math.min(b, Math.min(ya,yb));
c=Math.max(c, Math.abs(xb-xa));
d=Math.max(d, Math.abs(yb-ya));
if (flip)
CC.forcesRepaint();
else
CC.forcesRepaint(a,b,c+10,d+10);
return;
}
xb=px;
yb=py;
opx=px;
opy=py;
firstDrag=false;
}
return;
}
if(!firstDrag) {
CC.forcesRepaint();
}
firstDrag=false;
// Here we adjust the new positions for the handle being drag...
primBeingDragged.virtualPoint[handleBeingDragged].x=cs.unmapXsnap(px);
// If control is hold, trace a square
int ymm;
if(!isControl || !(primBeingDragged instanceof PrimitiveOval ||
primBeingDragged instanceof PrimitiveRectangle))
{
ymm=py;
} else {
// Transform the rectangle in a square, or the oval in a circle.
int hn=0;
if(handleBeingDragged==0) hn=1;
ymm=cs.mapYi(primBeingDragged.virtualPoint[hn].x,
primBeingDragged.virtualPoint[hn].y,false)+px-
cs.mapXi(primBeingDragged.virtualPoint[hn].x,
primBeingDragged.virtualPoint[hn].y,false);
}
primBeingDragged.virtualPoint[handleBeingDragged].y=cs.unmapYsnap(ymm);
primBeingDragged.setChanged(true);
}
}