package bluej.pkgmgr.target;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.PackageEditor;
import bluej.utility.javafx.JavaFXUtil;
import java.util.Properties;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import javafx.scene.Cursor;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.Node;
import threadchecker.OnThread;
import threadchecker.Tag;
| A general target in a package
|
| @author Michael Cahill
|
public abstract class Target
implements Comparable<Target>{
static final int DEF_WIDTH = 80;
static final int DEF_HEIGHT = 50;
static final int ARR_HORIZ_DIST = 5;
static final int ARR_VERT_DIST = 10;
static final int HANDLE_SIZE = 20;
static final int TEXT_HEIGHT = 16;
static final int TEXT_BORDER = 4;
static final int SHAD_SIZE = 4;
private static final double SHADOW_RADIUS = 3.0;
protected static final double RESIZE_CORNER_SIZE = 16;
@OnThread(Tag.FXPlatform)
private int preMoveX;
@OnThread(Tag.FXPlatform)
private int preMoveY;
@OnThread(Tag.FXPlatform)
private int preResizeWidth;
@OnThread(Tag.FXPlatform)
private int preResizeHeight;
@OnThread(Tag.FXPlatform)
private boolean pressIsResize;
@OnThread(Tag.FXPlatform)
private double pressDeltaX;
@OnThread(Tag.FXPlatform)
private double pressDeltaY;
@OnThread(Tag.FXPlatform)
private ContextMenu showingContextMenu;
@OnThread(value = Tag.Any, requireSynchronized = true)
private String identifierName;
private String displayName;
@OnThread(value = Tag.Any, requireSynchronized = true)
private Package pkg;
@OnThread(Tag.Any)
protected boolean selected;
protected boolean queued;
@OnThread(Tag.FXPlatform)
protected BorderPane pane = JavaFXUtil.initFX(BorderPane::new);
@OnThread(Tag.FX)
private boolean resizable = true;
| Create a new target with default size.
|
@OnThread(Tag.FXPlatform)
public Target(Package pkg, String identifierName)
{
pane.setPrefWidth(calculateWidth(new Label(), identifierName));
pane.setPrefHeight(DEF_HEIGHT);
pane.setCursor(Cursor.HAND);
JavaFXUtil.addStyleClass(pane, "target");
pane.setEffect(new DropShadow(SHADOW_RADIUS, SHADOW_RADIUS/2.0, SHADOW_RADIUS/2.0, javafx.scene.paint.Color.GRAY));
pane.setFocusTraversable(true);
JavaFXUtil.addFocusListener(pane, hasFocus -> {
PackageEditor pkgEditor = pkg.getEditor();
if (pkgEditor == null)
{
return;
}
if (hasFocus && !isSelected())
{
pkgEditor.selectOnly(this);
}
if (!hasFocus)
{
pkgEditor.checkForLossOfFocus();
}
});
pane.setOnMouseClicked(e -> {
if (e.getClickCount() > 1 && e.getButton() == MouseButton.PRIMARY && !e.isPopupTrigger() && e.isStillSincePress())
{
doubleClick(e.isShiftDown());
}
else if (e.getClickCount() == 1 && e.getButton() == MouseButton.PRIMARY && !e.isPopupTrigger() && e.isStillSincePress())
{
if (!pkg.getEditor().clickForExtends(this, e.getSceneX(), e.getSceneY()))
{
if (e.isShiftDown() || e.isShortcutDown())
{
pkg.getEditor().toggleSelection(this);
}
else
{
pkg.getEditor().selectOnly(this);
}
}
updateCursor(e, false);
if (isSelected())
pane.requestFocus();
}
e.consume();
});
pane.setOnMouseMoved(e -> {
updateCursor(e, false);
pkg.getEditor().setMouseIn(this);
});
pane.setOnMouseExited(e -> {
pkg.getEditor().setMouseLeft(this);
});
pane.setOnMousePressed(e -> {
if (e.getButton() == MouseButton.PRIMARY)
{
showingMenu(null);
pressDeltaX = e.getX();
pressDeltaY = e.getY();
pressIsResize = isSelected() && cursorAtResizeCorner(e);
if (pressIsResize && isResizable())
pkg.getEditor().startedResize();
else{ pkg.getEditor().startedMove();
}
}
e.consume();
});
pane.setOnMouseDragged(e -> {
if (e.getButton() == MouseButton.PRIMARY && !pkg.getEditor().isCreatingExtends())
{
if (pressIsResize && isResizable())
{
int newWidth = pkg.getEditor().snapToGrid((int) (e.getX() + (preResizeWidth - pressDeltaX)));
int newHeight = pkg.getEditor().snapToGrid((int) (e.getY() + (preResizeHeight - pressDeltaY)));
pkg.getEditor().resizeBy(newWidth - preResizeWidth, newHeight - preResizeHeight);
}
else if (isMoveable())
{
if (!isSelected())
{
pkg.getEditor().selectOnly(this);
pkg.getEditor().startedMove();
}
Point2D p = pkg.getEditor().sceneToLocal(e.getSceneX(), e.getSceneY());
int newX = pkg.getEditor().snapToGrid((int) (p.getX() - pressDeltaX));
int newY = pkg.getEditor().snapToGrid((int) (p.getY() - pressDeltaY));
pkg.getEditor().moveBy(newX - preMoveX, newY - preMoveY);
updateCursor(e, true);
}
}
e.consume();
});
pane.setOnMouseReleased(e -> {
pkg.getEditor().endResize();
});
pane.setOnKeyTyped(e -> {
if (e.getCharacter().length() > 0 && "+-".contains(e.getCharacter()) && isResizable())
{
pkg.getEditor().startedResize();
int delta = e.getCharacter().equals("+") ? PackageEditor.GRID_SIZE : -PackageEditor.GRID_SIZE;
pkg.getEditor().resizeBy(delta, delta);
pkg.getEditor().endResize();
e.consume();
}
});
pane.setOnKeyPressed(e -> {
if (isArrowKey(e))
{
if (e.isControlDown())
{
if (isResizable())
{
pkg.getEditor().startedResize();
int d = PackageEditor.GRID_SIZE;
pkg.getEditor().resizeBy(
e.getCode() == KeyCode.LEFT ? -d : (e.getCode() == KeyCode.RIGHT ? d : 0),
e.getCode() == KeyCode.UP ? -d : (e.getCode() == KeyCode.DOWN ? d : 0));
pkg.getEditor().endResize();
}
}
else if (e.isShiftDown())
{
if (isMoveable())
{
pkg.getEditor().startedMove();
int d = PackageEditor.GRID_SIZE;
pkg.getEditor().moveBy(
e.getCode() == KeyCode.LEFT ? -d : (e.getCode() == KeyCode.RIGHT ? d : 0),
e.getCode() == KeyCode.UP ? -d : (e.getCode() == KeyCode.DOWN ? d : 0));
}
}
else
{
pkg.getEditor().navigate(e);
}
e.consume();
}
else if (e.getCode() == KeyCode.ESCAPE)
{
if (pkg.getEditor()!= null)
{
pkg.getEditor().selectOnly(this);
}
e.consume();
}
else if (e.getCode() == KeyCode.A && !e.isAltDown())
pkg.getEditor().selectAll();
e.consume();
}
});
JavaFXUtil.listenForContextMenu(pane, (x, y) -> {
pkg.getEditor().selectOnly(this);
popupMenu(x.intValue(), y.intValue(), pkg.getEditor());
return true;
}, KeyCode.SPACE, KeyCode.ENTER);
if (pkg == null)
throw new NullPointerException();
this.pkg = pkg;
this.identifierName = identifierName;
this.displayName = identifierName;
}
@OnThread(Tag.FXPlatform)
private void updateCursor(MouseEvent e, boolean moving)
{
if (moving)
{
pane.setCursor(Cursor.MOVE);
}
else if (isSelected() && isResizable() && cursorAtResizeCorner(e))
{
pane.setCursor(Cursor.SE_RESIZE);
}
else
{
pane.setCursor(Cursor.HAND);
}
}
@OnThread(Tag.FXPlatform)
protected boolean cursorAtResizeCorner(MouseEvent e)
{
return e.getX() + e.getY() >= getWidth() + getHeight() - RESIZE_CORNER_SIZE;
}
| Calculate the width of a target depending on the length of its name and
| the font used for displaying the name. The size returned is a multiple of
| 10 (to fit the interactive resizing behaviour).
|
| @param name
| the name of the target (may be null).
| @return the width the target should have to fully display its name.
|
@OnThread(Tag.FX)
protected static int calculateWidth(Labeled node, String name)
{
int width = 0;
if (name != null)
width = (int)JavaFXUtil.measureString(node, name);
if ((width + 20) <= DEF_WIDTH)
return DEF_WIDTH;
else{ return (width + 29) / PackageEditor.GRID_SIZE * PackageEditor.GRID_SIZE;
}
}
| This target has been removed from its package.
|
public synchronized void setRemoved()
{
pkg = null;
}
| Load this target's properties from a properties file. The prefix is an
| internal name used for this target to identify its properties in a
| properties file used by multiple targets.
|
public void load(Properties props, String prefix)
throws NumberFormatException
{
int xpos = 0;
int ypos = 0;
int width = 20;
int height = 10;
try {
xpos = Math.max(Integer.parseInt(props.getProperty(prefix + ".x")), 0);
ypos = Math.max(Integer.parseInt(props.getProperty(prefix + ".y")), 0);
width = Math.max(Integer.parseInt(props.getProperty(prefix + ".width")), 1);
height = Math.max(Integer.parseInt(props.getProperty(prefix + ".height")), 1);
}
catch (NumberFormatException nfe) {
}
setPos(xpos, ypos);
setSize(width, height);
}
| Save the target's properties to 'props'.
|
@OnThread(Tag.FXPlatform)
public void save(Properties props, String prefix)
{
props.put(prefix + ".x", String.valueOf(getX()));
props.put(prefix + ".y", String.valueOf(getY()));
props.put(prefix + ".width", String.valueOf(getWidth()));
props.put(prefix + ".height", String.valueOf(getHeight()));
props.put(prefix + ".name", getIdentifierName());
}
| Return this target's package (ie the package that this target is
| currently shown in)
|
@OnThread(Tag.Any)
public synchronized Package getPackage()
{
return pkg;
}
| Change the text which the target displays for its label
|
public void setDisplayName(String name)
{
displayName = name;
}
| Returns the text which the target is displaying as its label
|
public String getDisplayName()
{
return displayName;
}
@OnThread(Tag.Any)
public synchronized String getIdentifierName()
{
return identifierName;
}
public synchronized void setIdentifierName(String newName)
{
identifierName = newName;
}
|
| Sets the selected status of this target. Do not call directly
| to select us; instead call SelectionController/SelectionSet's methods,
| which will call this after updating the selected set.
|
@OnThread(Tag.FXPlatform)
public void setSelected(boolean selected)
{
this.selected = selected;
JavaFXUtil.setPseudoclass("bj-selected", selected, pane);
redraw();
}
@OnThread(Tag.FXPlatform)
protected void redraw()
{
}
|
| (non-Javadoc)
|
| @see bluej.graph.Selectable#isSelected()
|
@OnThread(Tag.Any)
public boolean isSelected()
{
return selected;
}
@OnThread(Tag.FXPlatform)
public int getX()
{
Double leftAnchor = AnchorPane.getLeftAnchor(pane);
if (leftAnchor == null)
return 0;
else{ return (int)(double)leftAnchor;
}
}
@OnThread(Tag.FXPlatform)
public int getY()
{
Double topAnchor = AnchorPane.getTopAnchor(pane);
if (topAnchor == null)
return 0;
else{ return (int)(double)topAnchor;
}
}
@OnThread(Tag.FXPlatform)
public int getWidth()
{
return (int)pane.getPrefWidth();
}
@OnThread(Tag.FXPlatform)
public int getHeight()
{
return (int)pane.getPrefHeight();
}
public boolean isQueued()
{
return queued;
}
public void setQueued(boolean queued)
{
this.queued = queued;
}
@OnThread(Tag.FX)
public boolean isResizable()
{
return resizable;
}
@OnThread(Tag.FXPlatform)
public void setResizable(boolean resizable)
{
this.resizable = resizable;
}
@OnThread(Tag.Any)
public boolean isSaveable()
{
return true;
}
public boolean isSelectable()
{
return true;
}
@OnThread(Tag.FXPlatform)
public void repaint()
{
Package thePkg = getPackage();
if (thePkg != null)
thePkg.repaint();
}
| We have a notion of equality that relates solely to the identifierName.
| If the identifierNames's are equal then the Target's are equal.
|
@OnThread(Tag.Any)
public boolean equals(Object o)
{
if (o instanceof Target) {
Target t = (Target) o;
return this.identifierName.equals(t.identifierName);
}
return false;
}
@OnThread(Tag.Any)
public synchronized int hashCode()
{
return identifierName.hashCode();
}
@OnThread(value = Tag.FXPlatform, ignoreParent = true)
public int compareTo(Target t)
{
if (equals(t))
return 0;
if (this.getY() < t.getY())
return -1;
else if (this.getY() > t.getY())
return 1;
if (this.getX() < t.getX())
return -1;
else if (this.getX() > t.getX())
return 1;
return this.identifierName.compareTo(t.getIdentifierName());
}
public String toString()
{
return getDisplayName();
}
@OnThread(Tag.FXPlatform)
public void setPos(int x, int y)
{
AnchorPane.setTopAnchor(pane, (double)y);
AnchorPane.setLeftAnchor(pane, (double)x);
repaint();
}
@OnThread(Tag.FXPlatform)
public void setSize(int width, int height)
{
pane.setPrefWidth(width);
pane.setPrefHeight(height);
repaint();
}
@OnThread(Tag.FXPlatform)
public abstract void doubleClick(boolean openInNewWindow);
@OnThread(Tag.FXPlatform)
public abstract void popupMenu(int x, int y, PackageEditor editor);
public abstract void remove();
@OnThread(Tag.FXPlatform)
public Node getNode()
{
return pane;
}
@OnThread(Tag.FXPlatform)
public boolean isMoveable()
{
return false;
}
@OnThread(Tag.FXPlatform)
public void setIsMoveable(boolean b)
{
}
| Is this a front class (true; most of them) or back (false;
| unit-test classes which always appear behind others)
|
@OnThread(Tag.FXPlatform)
public boolean isFront()
{
return true;
}
| Gets the bounds relative to the package editor.
| @return
|
@OnThread(Tag.FXPlatform)
public Bounds getBoundsInEditor()
{
return pane.getBoundsInParent();
}
| Save the current position so that we later know
| how much to move by (i.e. the delta) when dragging.
|
@OnThread(Tag.FXPlatform)
public void savePreMovePosition()
{
preMoveX = getX();
preMoveY = getY();
}
| The X position before we started the move
|
@OnThread(Tag.FXPlatform)
public int getPreMoveX()
{
return preMoveX;
}
| The Y position before we started the move
|
@OnThread(Tag.FXPlatform)
public int getPreMoveY()
{
return preMoveY;
}
| Save the current size so that we later know
| how much to resize by (i.e. the delta).
|
@OnThread(Tag.FXPlatform)
public void savePreResize()
{
preResizeWidth = getWidth();
preResizeHeight = getHeight();
}
@OnThread(Tag.FXPlatform)
public int getPreResizeWidth()
{
return preResizeWidth;
}
@OnThread(Tag.FXPlatform)
public int getPreResizeHeight()
{
return preResizeHeight;
}
@OnThread(Tag.FXPlatform)
protected final void showingMenu(ContextMenu newMenu)
{
if (newMenu != null)
{
requestFocus();
}
if (showingContextMenu != null)
{
showingContextMenu.hide();
}
showingContextMenu = newMenu;
}
@OnThread(Tag.FXPlatform)
private static boolean isArrowKey(KeyEvent evt)
{
return evt.getCode() == KeyCode.UP || evt.getCode() == KeyCode.DOWN
|| evt.getCode() == KeyCode.LEFT || evt.getCode() == KeyCode.RIGHT;
}
@OnThread(Tag.FXPlatform)
public boolean isFocused()
{
return pane.isFocused();
}
@OnThread(Tag.FXPlatform)
public void requestFocus()
{
pane.requestFocus();
}
@OnThread(Tag.FXPlatform)
public void setCreatingExtends(boolean drawingExtends)
{
pane.setEffect(drawingExtends ? new ColorAdjust(0, 0, -0.2, 0): null);
}
}
. Target
. updateCursor
. cursorAtResizeCorner
. calculateWidth
. setRemoved
. load
. save
. getPackage
. setDisplayName
. getDisplayName
. getIdentifierName
. setIdentifierName
. setSelected
. redraw
. isSelected
. getX
. getY
. getWidth
. getHeight
. isQueued
. setQueued
. isResizable
. setResizable
. isSaveable
. isSelectable
. repaint
. equals
. hashCode
. compareTo
. toString
. setPos
. setSize
. doubleClick
. popupMenu
. remove
. getNode
. isMoveable
. setIsMoveable
. isFront
. getBoundsInEditor
. savePreMovePosition
. getPreMoveX
. getPreMoveY
. savePreResize
. getPreResizeWidth
. getPreResizeHeight
. showingMenu
. isArrowKey
. isFocused
. requestFocus
. setCreatingExtends
825 neLoCode
+ 37 LoComm