/** * Dot.java * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org * * 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 . */ package org.expeditee.items; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.expeditee.core.Colour; import org.expeditee.core.Dimension; import org.expeditee.core.Fill; import org.expeditee.core.Point; import org.expeditee.core.bounds.AxisAlignedBoxBounds; import org.expeditee.core.bounds.Bounds; import org.expeditee.core.bounds.PolygonBounds; import org.expeditee.gio.EcosystemManager; import org.expeditee.gio.GraphicsManager; import org.expeditee.gui.DisplayController; import org.expeditee.gui.Frame; /** * Represents a point on the screen. All Point objects are stored as x,y pairs * with an Item ID. Note: Lines and all combinates of lines (rectangles, etc) * are represented only as Points that share line IDs. * * @author jdm18 * */ public class Dot extends Item { // Standard Item variables // private static final int _MINIMUM_DOT_SIZE = 6; private static final int MINIMUM_DOT_SIZE = 2; public Dot(int id) { super(); setID(id); } /** * Constructs a new Point with the given x,y coordinates and Item ID. * * @param x * The x coordinate of this point * @param y * The y coordinate of this point * @param id * The Item ID of this Point */ public Dot(int x, int y, int id) { super(); setID(id); setPosition(x, y); } @Override public void setColor(Colour c) { super.setColor(c); // update the colour of any lines for (Line line : getLines()) line.setColor(c); } @Override public void setAnchorLeft(Integer anchor) { if (!isLineEnd()) { super.setAnchorLeft(anchor); return; } invalidateFill(); invalidateCommonTrait(ItemAppearence.PreMoved); this._anchoring.setLeftAnchor(anchor); int oldX = getX(); if (anchor != null) { float deltaX = anchor - oldX; anchorConnected(AnchorEdgeType.Left, deltaX); } invalidateCommonTrait(ItemAppearence.PostMoved); invalidateFill(); } @Override public void setAnchorRight(Integer anchor) { if (!isLineEnd()) { super.setAnchorRight(anchor); return; } invalidateFill(); invalidateCommonTrait(ItemAppearence.PreMoved); this._anchoring.setRightAnchor(anchor); int oldX = getX(); if (anchor != null) { float deltaX = DisplayController.getFramePaintAreaWidth() - anchor - getBoundsWidth() - oldX; anchorConnected(AnchorEdgeType.Right, deltaX); } invalidateCommonTrait(ItemAppearence.PostMoved); invalidateFill(); } @Override public void setAnchorTop(Integer anchor) { if (!isLineEnd()) { super.setAnchorTop(anchor); return; } invalidateFill(); invalidateCommonTrait(ItemAppearence.PreMoved); this._anchoring.setTopAnchor(anchor); int oldY = getY(); if (anchor != null) { float deltaY = anchor - oldY; anchorConnected(AnchorEdgeType.Top, deltaY); } invalidateCommonTrait(ItemAppearence.PostMoved); invalidateFill(); } @Override public void setAnchorBottom(Integer anchor) { if (!isLineEnd()) { super.setAnchorBottom(anchor); return; } invalidateFill(); invalidateCommonTrait(ItemAppearence.PreMoved); this._anchoring.setBottomAnchor(anchor); int oldY = getY(); if (anchor != null) { float deltaY = DisplayController.getFramePaintAreaHeight() - anchor - getBoundsHeight() - oldY; anchorConnected(AnchorEdgeType.Bottom, deltaY); } invalidateCommonTrait(ItemAppearence.PostMoved); invalidateFill(); } @Override public void paint() { GraphicsManager g = EcosystemManager.getGraphicsManager(); if (isHighlighted() /* && !FreeItems.getInstance().contains(this) */) { Colour highlightColor = getHighlightColor(); Fill fill = new Fill(highlightColor); // g.setStroke() // Draw the highlighting rectangle surrounding the dot // this is drawn even if its part of a rectangle if (isVectorItem()) invalidateBounds(); AxisAlignedBoxBounds bounds = getBoundingBox(); if (_highlightMode == HighlightMode.Enclosed || this.getConnected().size() <= 1) { // Make sure single dots are highlighted filled g.drawRectangle(bounds.getTopLeft(), bounds.getSize(), 0.0f, fill, highlightColor, DOT_STROKE, null); } else if (_highlightMode == HighlightMode.Connected) { g.drawRectangle(bounds.getTopLeft(), bounds.getSize(), 0.0f, null, highlightColor, HIGHLIGHT_STROKE, null); } else if (_highlightMode == HighlightMode.Normal) { g.drawOval(bounds.getCentre(), bounds.getSize(), 0.0f, fill, highlightColor, DOT_STROKE); } // System.out.println(_mode.toString()); } // dots on lines are hidden if (getLines().size() > 0) return; int thick = (int) getThickness(); if (thick < MINIMUM_DOT_SIZE) thick = MINIMUM_DOT_SIZE; Fill fill = _filled ? new Fill(getPaintColor()) : null; // TODO: On testing, this code doesn't seem to work (can't change to any type except square). cts16 switch (_type) { case circle: g.drawOval( new Point(getX(), getY()), new Dimension(thick, thick), 0.0, fill, getPaintColor(), null ); break; case diamond: PolygonBounds diamond = PolygonBounds.getDiamond(thick, thick).translate(getX(), getY()).close(); g.drawPolygon(diamond, null, null, 0.0, fill, getPaintColor(), null); break; case triangle: PolygonBounds triangle = PolygonBounds.getTriangle(thick, thick).translate(getX(), getY()).close(); g.drawPolygon(triangle, null, null, 0.0, fill, getPaintColor(), null); break; case roundSquare: int arc = thick / 4; g.drawRectangle( new Point(getX(), getY()), new Dimension(thick, thick), 0.0, fill, getPaintColor(), null, new Dimension(arc, arc) ); break; default: g.drawRectangle( new Point(getX(), getY()), new Dimension(thick, thick), 0.0, fill, getPaintColor(), null, null ); } } /** * Updates the bounds surrounding this Dot. * TODO: Standardise Dot minimum size. cts16 */ public Bounds updateBounds() { int thick = Math.round(getThickness()); // Sets a minimum size for the dot thick = Math.max(thick, getGravity() * 2); // Include the gravity in the thickness thick += 2 * getGravity(); int x = getX() - thick / 2; int y = getY() - thick / 2; return new AxisAlignedBoxBounds(x, y, thick, thick); } @Override public Item copy() { Dot copy = new Dot(getX(), getY(), getID()); Item.DuplicateItem(this, copy); return copy; } @Override public void setHighlightColorToDefault() { super.setHighlightColorToDefault(); //return Item.DEFAULT_CURSOR; } @Override public void setAnnotation(boolean val) { DisplayController.setCursorPosition(this.getPosition()); Item.replaceDot(this, '@'); } @Override public Item merge(Item merger, int mouseX, int mouseY) { // if the item being merged with is another Dot if (merger instanceof Dot) { if (merger.hasEnclosures() || hasEnclosures()) return merger; Item dot = (Item) merger; merger.setPosition(this.getPosition()); // prevent concurrency issues if removing lines List lines = new ArrayList(); lines.addAll(dot.getLines()); for (Line line : lines) { // remove lines that are in common if (getLines().contains(line)) { dot.removeLine(line); removeLine(line); } else { // check for duplicated lines as a result of merging Item check = (Item) line.getOppositeEnd(dot); boolean dup = false; for (Line l : getLines()) { Item opposite = l.getOppositeEnd(this); if (check == opposite) { line.getStartItem().removeLine(line); line.getEndItem().removeLine(line); dup = true; break; } } if (!dup) line.replaceLineEnd(dot, this); } } setThickness(dot.getThickness()); setColor(dot.getColor()); setFillColor(dot.getFillColor()); return null; } if (merger instanceof Text) { merger.setPosition(this.getPosition()); List lines = new LinkedList(); lines.addAll(getLines()); for (Line line : lines) line.replaceLineEnd(this, merger); this.delete(); return merger; } // if the item being merged with is a Line if (merger instanceof Line) { Line line = (Line) merger; // if this dot is part of the line then keep it, otherwise it // can be removed if (line.getOppositeEnd(this) != null && getLines().contains(line)) return merger; else return null; } return merger; } @Override public String getTypeAndID() { return "P " + getID(); } @Override public void delete() { super.delete(); for (Line l : this.getLines()) l.delete(); } @Override public void anchor() { Frame current = getParentOrCurrentFrame(); // This is to make lines anchored across frames be on one frame for (Line l : getLines()) { Frame parent = l.getOppositeEnd(this).getParent(); if (parent != null && parent != current) { this.setParent(parent); if (DisplayController.getCurrentSide() == DisplayController.TwinFramesSide.LEFT) this.setX(this.getX() - DisplayController.getTwinFramesSeparatorX()); else this.setX(this.getX() + DisplayController.getTwinFramesSeparatorX()); } break; } super.anchor(); // TODO is the code below needed... what for? for (Line line : getLines()) { if (line.getID() < 0 && !current.getItems().contains(line)) { line.setID(current.getNextItemID()); line.setHighlightColorToDefault(); // Mike: Why was this line here? // anchor(line); } } } @Override public void addLine(Line line) { super.addLine(line); line.setColor(getColor()); line.setThickness(getThickness()); line.setLinePattern(getLinePattern()); } @Override public void lineColorChanged(Colour c) { if (getColor() != c) { setColor(c); } } @Override public boolean dontSave() { if (getThickness() <= 1 && (getLines().size() == 0) && getConstraints().size() == 0) { return true; } return super.dontSave(); } @Override public float getSize() { return getThickness(); } }