/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo 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. * * OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.ve.shema; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.util.Observable; import java.util.Observer; import java.util.Vector; import java.util.logging.Logger; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import org.openflexo.fge.DrawingGraphicalRepresentation; import org.openflexo.fge.FGEUtils; import org.openflexo.fge.GraphicalRepresentation; import org.openflexo.fge.ShapeGraphicalRepresentation; import org.openflexo.fge.controller.DrawingController; import org.openflexo.fge.cp.ControlArea; import org.openflexo.fge.geom.FGEGeometricObject.Filling; import org.openflexo.fge.geom.FGEGeometricObject.SimplifiedCardinalDirection; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.geom.FGERoundRectangle; import org.openflexo.fge.geom.FGEShape; import org.openflexo.fge.graphics.BackgroundStyle; import org.openflexo.fge.graphics.BackgroundStyle.ColorGradient.ColorGradientDirection; import org.openflexo.fge.graphics.FGEGraphics; import org.openflexo.fge.graphics.ForegroundStyle; import org.openflexo.fge.notifications.ObjectResized; import org.openflexo.fge.view.DrawingView; import org.openflexo.fge.view.FGEPaintManager; import org.openflexo.fge.view.ShapeView; import org.openflexo.foundation.view.ViewConnector; import org.openflexo.foundation.view.ViewElement; import org.openflexo.foundation.view.ViewObject; import org.openflexo.foundation.view.ViewShape; import org.openflexo.foundation.view.ViewShape.DropAndLinkScheme; import org.openflexo.foundation.view.action.AddConnector; import org.openflexo.foundation.view.action.DropSchemeAction; import org.openflexo.foundation.view.action.LinkSchemeAction; import org.openflexo.foundation.viewpoint.DropScheme; import org.openflexo.foundation.viewpoint.EditionPattern; import org.openflexo.foundation.viewpoint.LinkScheme; import org.openflexo.foundation.viewpoint.LocalizedDictionary; import org.openflexo.localization.FlexoLocalization; public class FloatingPalette extends ControlArea<FGERoundRectangle> implements Observer { private static final Logger logger = Logger.getLogger(FloatingPalette.class.getPackage().getName()); protected static final Color OK = new Color(0, 191, 0); private enum Mode { CREATE_SHAPE_AND_LINK, LINK_ONLY; } private VEShapeGR shapeGR; private ViewObject target; private FGERoundRectangle roleRect; private FGERectangle edgeRect; /** The vertical space between two elements of the palette */ private static final int SPACING = 5; /** The height of an element of the palette */ private static final int ELEMENTS_HEIGHT = 8; /** The width of an element of the palette */ private static final int ELEMENTS_WIDTH = 12; private static final ForegroundStyle NONE = ForegroundStyle.makeNone(); private static final BackgroundStyle DEFAULT = BackgroundStyle.makeColoredBackground(Color.WHITE); private static final ForegroundStyle NODE_FOREGROUND = ForegroundStyle.makeStyle(Color.RED, 1.0f); private static final ForegroundStyle EDGE_FOREGROUND = ForegroundStyle.makeStyle(FGEUtils.NICE_BROWN, 1.0f); private static final BackgroundStyle NODE_BACKGROUND = BackgroundStyle.makeColorGradientBackground(Color.ORANGE, Color.WHITE, ColorGradientDirection.SOUTH_EAST_NORTH_WEST); static { DEFAULT.setUseTransparency(true); DEFAULT.setTransparencyLevel(0.3f); NODE_BACKGROUND.setUseTransparency(true); NODE_BACKGROUND.setTransparencyLevel(0.7f); } private SimplifiedCardinalDirection orientation; public FloatingPalette(VEShapeGR shapeGR, ViewObject target, SimplifiedCardinalDirection orientation) { super(shapeGR, makeRoundRect(shapeGR, orientation)); this.shapeGR = shapeGR; this.target = target; this.orientation = orientation; shapeGR.addObserver(this); updateElements(orientation); } @Override public boolean isDraggable() { return shapeGR.getDrawing().isEditable(); } protected Point currentDraggingLocationInDrawingView = null; protected boolean drawEdge = false; protected boolean isDnd = false; protected VEShapeGR to = null; protected GraphicalRepresentation<?> focusedGR; private VEShemaController controller; private FGEPoint normalizedStartPoint; private Rectangle previousRectangle; private Mode mode; public void paint(Graphics g, VEShemaController controller) { if (drawEdge && currentDraggingLocationInDrawingView != null) { FGEShape<?> fgeShape = shapeGR.getShape().getOutline(); DrawingGraphicalRepresentation<?> drawingGR = controller.getDrawingGraphicalRepresentation(); double scale = controller.getScale(); FGEPoint nearestOnOutline = fgeShape.getNearestPoint(drawingGR.convertLocalViewCoordinatesToRemoteNormalizedPoint( currentDraggingLocationInDrawingView, shapeGR, scale)); /*nodeGR.convertLocalNormalizedPointToRemoteViewCoordinates(this.normalizedStartPoint, controller.getDrawingGraphicalRepresentation(), controller.getScale())*/ Point fromPoint = shapeGR.convertLocalNormalizedPointToRemoteViewCoordinates(nearestOnOutline, drawingGR, scale); Point toPoint = currentDraggingLocationInDrawingView; if (mode == Mode.LINK_ONLY) { if (to != null && isDnd) { // toPoint = drawingGR.convertRemoteNormalizedPointToLocalViewCoordinates(to.getShape().getShape().getCenter(), to, // scale); g.setColor(OK); } else { g.setColor(Color.RED); } } else { if (isDnd) { g.setColor(OK); } else { g.setColor(Color.RED); } } g.drawLine(fromPoint.x, fromPoint.y, toPoint.x, toPoint.y); int x, y, w, h; if (fromPoint.x >= toPoint.x) { x = toPoint.x; w = fromPoint.x - toPoint.x; } else { x = fromPoint.x; w = toPoint.x - fromPoint.x; } if (fromPoint.y >= toPoint.y) { y = toPoint.y; h = fromPoint.y - toPoint.y; } else { y = fromPoint.y; h = toPoint.y - fromPoint.y; } previousRectangle = new Rectangle(x, y, w, h); } } @Override public void startDragging(DrawingController<?> controller, FGEPoint startPoint) { mode = null; if (roleRect.contains(startPoint)) { mode = Mode.CREATE_SHAPE_AND_LINK; } else if (edgeRect.contains(startPoint)) { mode = Mode.LINK_ONLY; } if (mode != null) { drawEdge = true; normalizedStartPoint = startPoint; this.controller = (VEShemaController) controller; ((VEShemaView) controller.getDrawingView()).setFloatingPalette(this); } else { drawEdge = false; } } @Override public boolean dragToPoint(FGEPoint newRelativePoint, FGEPoint pointRelativeToInitialConfiguration, FGEPoint newAbsolutePoint, FGEPoint initialPoint, MouseEvent event) { if (drawEdge) { DrawingView<?> drawingView = controller.getDrawingView(); FGEPaintManager paintManager = drawingView.getPaintManager(); // Attempt to repaint relevant zone only Rectangle oldBounds = previousRectangle; if (oldBounds != null) { oldBounds.x -= 1; oldBounds.y -= 1; oldBounds.width += 2; oldBounds.height += 2; } focusedGR = controller.getDrawingView().getFocusRetriever().getFocusedObject(event); if (focusedGR instanceof VEShapeGR && focusedGR != shapeGR) { to = (VEShapeGR) focusedGR; } else { to = null; } currentDraggingLocationInDrawingView = SwingUtilities.convertPoint((Component) event.getSource(), event.getPoint(), controller.getDrawingView()); if (!isDnd) { isDnd = shapeGR.convertLocalNormalizedPointToRemoteViewCoordinates(normalizedStartPoint, controller.getDrawingGraphicalRepresentation(), controller.getScale()).distance( currentDraggingLocationInDrawingView) > 5; } // Attempt to repaint relevant zone only Rectangle newBounds = getBoundsToRepaint(drawingView); Rectangle boundsToRepaint; if (oldBounds != null) { boundsToRepaint = oldBounds.union(newBounds); } else { boundsToRepaint = newBounds; } paintManager.repaint(drawingView, boundsToRepaint); // Alternative @brutal zone // paintManager.repaint(drawingView); return true; } return false; } // Attempt to repaint relevant zone only private Rectangle getBoundsToRepaint(DrawingView<?> drawingView) { ShapeView<?> fromView = drawingView.shapeViewForObject(shapeGR); Rectangle fromViewBounds = SwingUtilities.convertRectangle(fromView, fromView.getBounds(), drawingView); Rectangle boundsToRepaint = fromViewBounds; if (to != null) { ShapeView<?> toView = drawingView.shapeViewForObject(to); Rectangle toViewBounds = SwingUtilities.convertRectangle(toView, toView.getBounds(), drawingView); boundsToRepaint = fromViewBounds.union(toViewBounds); } if (currentDraggingLocationInDrawingView != null) { Rectangle lastLocationBounds = new Rectangle(currentDraggingLocationInDrawingView); boundsToRepaint = fromViewBounds.union(lastLocationBounds); } // logger.fine("boundsToRepaint="+boundsToRepaint); return boundsToRepaint; } @Override public void stopDragging(DrawingController<?> controller, GraphicalRepresentation<?> focusedGR) { if (drawEdge && currentDraggingLocationInDrawingView != null && isDnd) { try { GraphicalRepresentation<?> targetGR = controller.getGraphicalRepresentation(target); if (targetGR == null) { targetGR = controller.getDrawingGraphicalRepresentation(); } if (focusedGR == null) { focusedGR = controller.getDrawingGraphicalRepresentation(); } SimplifiedCardinalDirection direction = FGEPoint.getSimplifiedOrientation( new FGEPoint(shapeGR.convertLocalNormalizedPointToRemoteViewCoordinates(this.normalizedStartPoint, controller.getDrawingGraphicalRepresentation(), controller.getScale())), new FGEPoint( currentDraggingLocationInDrawingView)); Point dropPoint = currentDraggingLocationInDrawingView; if (dropPoint.x < 0) { dropPoint.x = 0; } if (dropPoint.y < 0) { dropPoint.y = 0; } Point p = GraphicalRepresentation.convertPoint(controller.getDrawingGraphicalRepresentation(), dropPoint, focusedGR, controller.getScale()); FGEPoint dropLocation = new FGEPoint(p.x / controller.getScale(), p.y / controller.getScale()); ViewShape to = null; switch (mode) { case CREATE_SHAPE_AND_LINK: askAndApplyDropAndLinkScheme(dropLocation, focusedGR); break; case LINK_ONLY: if (this.to != null) { to = this.to.getDrawable(); askAndApplyLinkScheme(dropLocation, to); } break; default: logger.warning("Not implemented !!!"); break; } if (to == null) { return; } } finally { resetVariables(); ((VEShemaView) controller.getDrawingView()).resetFloatingPalette(); DrawingView<?> drawingView = controller.getDrawingView(); FGEPaintManager paintManager = drawingView.getPaintManager(); paintManager.invalidate(drawingView.getDrawingGraphicalRepresentation()); paintManager.repaint(drawingView.getDrawingGraphicalRepresentation()); } } else { resetVariables(); } super.stopDragging(controller, focusedGR); } private void askAndApplyDropAndLinkScheme(final FGEPoint dropLocation, GraphicalRepresentation focusedGR) { ViewObject container = null; EditionPattern targetEP = null; if (focusedGR == null || focusedGR instanceof VEShemaGR) { container = target.getShema(); targetEP = null; } else if (focusedGR.getDrawable() instanceof ViewElement) { container = (ViewElement) focusedGR.getDrawable(); targetEP = ((ViewElement) container).getEditionPatternReference().getEditionPattern(); } if (container == null) { return; } if (shapeGR.getOEShape().getAvailableDropAndLinkSchemeFromThisShape(targetEP) == null || shapeGR.getOEShape().getAvailableDropAndLinkSchemeFromThisShape(targetEP).size() == 0) { return; } if (shapeGR.getOEShape().getAvailableDropAndLinkSchemeFromThisShape(targetEP).size() == 1) { applyDropAndLinkScheme(shapeGR.getOEShape().getAvailableDropAndLinkSchemeFromThisShape(targetEP).firstElement(), dropLocation, container); return; } JPopupMenu popup = new JPopupMenu(); for (final DropAndLinkScheme dropAndLinkScheme : shapeGR.getOEShape().getAvailableDropAndLinkSchemeFromThisShape(targetEP)) { LocalizedDictionary localizedDictionary = dropAndLinkScheme.linkScheme.getViewPoint().getLocalizedDictionary(); String linkLabel = dropAndLinkScheme.linkScheme.getLabel() != null ? dropAndLinkScheme.linkScheme.getLabel() : dropAndLinkScheme.linkScheme.getName(); String localizedLinkLabel = localizedDictionary .getLocalizedForKeyAndLanguage(linkLabel, FlexoLocalization.getCurrentLanguage()); if (localizedLinkLabel == null) { localizedLinkLabel = FlexoLocalization.localizedForKey(linkLabel); } String dropLabel = dropAndLinkScheme.dropScheme.getEditionPattern().getName(); String localizedDropLabel = localizedDictionary .getLocalizedForKeyAndLanguage(dropLabel, FlexoLocalization.getCurrentLanguage()); if (localizedDropLabel == null) { localizedDropLabel = FlexoLocalization.localizedForKey(dropLabel); } String withNew = FlexoLocalization.localizedForKey("with_new"); JMenuItem menuItem = new JMenuItem(localizedLinkLabel + " " + withNew + " " + localizedDropLabel); final ViewObject finalContainer = container; menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { applyDropAndLinkScheme(dropAndLinkScheme, dropLocation, finalContainer); } }); menuItem.setToolTipText(dropAndLinkScheme.dropScheme.getDescription()); popup.add(menuItem); } popup.show((Component) controller.getDrawingView().viewForObject(controller.getGraphicalRepresentation(target)), (int) dropLocation.x, (int) dropLocation.y); } private void askAndApplyLinkScheme(final FGEPoint dropLocation, final ViewShape to) { Vector<LinkScheme> availableConnectors = new Vector<LinkScheme>(); // Lets look if we match a CalcPaletteConnector final ViewShape from = shapeGR.getDrawable(); if (from.getShema().getCalc() != null && from.getEditionPattern() != null && to.getEditionPattern() != null) { availableConnectors = from.getShema().getCalc().getConnectorsMatching(from.getEditionPattern(), to.getEditionPattern()); } if (availableConnectors.size() == 1) { LinkSchemeAction action = LinkSchemeAction.actionType.makeNewAction(from.getShema(), null, controller.getVEController() .getEditor()); action.setLinkScheme(availableConnectors.firstElement()); action.setFromShape(from); action.setToShape(to); action.escapeParameterRetrievingWhenValid = true; action.doAction(); } else if (availableConnectors.size() > 1) { JPopupMenu popup = new JPopupMenu(); for (final LinkScheme linkScheme : availableConnectors) { // final CalcPaletteConnector connector = availableConnectors.get(linkScheme); // System.out.println("Available: "+paletteConnector.getEditionPattern().getName()); LocalizedDictionary localizedDictionary = linkScheme.getViewPoint().getLocalizedDictionary(); String label = linkScheme.getLabel() != null ? linkScheme.getLabel() : linkScheme.getName(); String localized = localizedDictionary.getLocalizedForKeyAndLanguage(label, FlexoLocalization.getCurrentLanguage()); if (localized == null) { localized = FlexoLocalization.localizedForKey(label); } JMenuItem menuItem = new JMenuItem(localized); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // System.out.println("Action "+paletteConnector.getEditionPattern().getName()); LinkSchemeAction action = LinkSchemeAction.actionType.makeNewAction(from.getShema(), null, controller .getVEController().getEditor()); action.setLinkScheme(linkScheme); action.setFromShape(from); action.setToShape(to); action.escapeParameterRetrievingWhenValid = true; action.doAction(); } }); menuItem.setToolTipText(linkScheme.getDescription()); popup.add(menuItem); } /*JMenuItem menuItem = new JMenuItem(FlexoLocalization.localizedForKey("graphical_connector_only")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { AddConnector action = AddConnector.actionType.makeNewAction(shapeGR.getDrawable(), null, (controller).getOEController() .getEditor()); action.setToShape(to); action.setAutomaticallyCreateConnector(true); action.doAction(); } }); menuItem.setToolTipText(FlexoLocalization.localizedForKey("draw_basic_graphical_connector_without_ontologic_semantic")); popup.add(menuItem);*/ popup.show((Component) controller.getDrawingView().viewForObject(controller.getGraphicalRepresentation(target)), (int) dropLocation.x, (int) dropLocation.y); } else { AddConnector action = AddConnector.actionType.makeNewAction(shapeGR.getDrawable(), null, controller.getVEController() .getEditor()); } // It is no more possible to create a link without semantic from floating palette /*else { AddConnector action = AddConnector.actionType.makeNewAction(shapeGR.getDrawable(), null, (controller).getVEController() .getEditor()); action.setToShape(to); action.setAutomaticallyCreateConnector(true); action.doAction(); }*/ } protected void applyDropAndLinkScheme(final DropAndLinkScheme dropAndLinkScheme, final FGEPoint dropLocation, ViewObject container) { applyDropAndLinkScheme(dropAndLinkScheme.dropScheme, dropAndLinkScheme.linkScheme, dropLocation, container); } /*protected void applyDropAndLinkScheme(final DropAndLinkScheme dropAndLinkScheme, final FGEPoint dropLocation) { Vector<DropScheme> allDS = findCompatibleDropSchemes(linkScheme); if (allDS.size() == 0) { return; } else if (allDS.size() == 1) { applyDropAndLinkScheme(allDS.firstElement(), linkScheme, dropLocation); return; } else { JPopupMenu popup = new JPopupMenu(); for (final DropScheme dropScheme : allDS) { JMenuItem menuItem = new JMenuItem(FlexoLocalization.localizedForKey(dropScheme.getLabel() != null ? dropScheme.getLabel() : dropScheme.getName())); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { applyDropAndLinkScheme(dropScheme, linkScheme, dropLocation); } }); menuItem.setToolTipText(linkScheme.getDescription()); popup.add(menuItem); } popup.show((Component) controller.getDrawingView().viewForObject(controller.getGraphicalRepresentation(target)), (int) dropLocation.x, (int) dropLocation.y); } }*/ protected void applyDropAndLinkScheme(DropScheme dropScheme, LinkScheme linkScheme, FGEPoint dropLocation, ViewObject container) { logger.info("applyDropAndLinkScheme container=" + container); ViewShape newShape = createNewShape(dropLocation, container, dropScheme); if (newShape != null) { createNewConnector(shapeGR.getDrawable(), newShape, linkScheme); controller.setSelectedObject(controller.getGraphicalRepresentation(newShape)); } } /*private Vector<DropScheme> findCompatibleDropSchemes(LinkScheme linkScheme) { Vector<DropScheme> dropSchemes = new Vector<DropScheme>(); EditionPattern targetEditionPattern = linkScheme.getToTargetEditionPattern(); return targetEditionPattern.getDropSchemes(); }*/ private void resetVariables() { drawEdge = false; isDnd = false; to = null; currentDraggingLocationInDrawingView = null; } private ViewShape createNewShape(FGEPoint dropLocation, ViewObject container, DropScheme dropScheme) { DropSchemeAction dropSchemeAction = DropSchemeAction.actionType.makeNewAction(container, null, controller.getVEController() .getEditor()); dropSchemeAction.setDropScheme(dropScheme); dropSchemeAction.escapeParameterRetrievingWhenValid = true; dropSchemeAction.doAction(); if (dropSchemeAction.getPrimaryShape() != null) { GraphicalRepresentation<?> targetGR = controller.getDrawing().getGraphicalRepresentation(target); ShapeGraphicalRepresentation<?> gr = (ShapeGraphicalRepresentation<?>) controller.getGraphicalRepresentation(dropSchemeAction .getPrimaryShape()); double xOffset = 0; double yOffset = 0; if (gr != null) { if (gr.getBorder() != null) { xOffset -= gr.getBorder().left; yOffset -= gr.getBorder().top; } xOffset -= gr.getWidth() / 2; yOffset -= gr.getHeight() / 2; gr.setX(dropLocation.x + xOffset); gr.setY(dropLocation.y + yOffset); } } return dropSchemeAction.getPrimaryShape(); } private ViewConnector createNewConnector(ViewShape from, ViewShape to, LinkScheme linkScheme) { LinkSchemeAction linkSchemeAction = LinkSchemeAction.actionType.makeNewAction(from.getShema(), null, controller.getVEController() .getEditor()); linkSchemeAction.setLinkScheme(linkScheme); linkSchemeAction.setFromShape(from); linkSchemeAction.setToShape(to); linkSchemeAction.escapeParameterRetrievingWhenValid = true; linkSchemeAction.doAction(); return linkSchemeAction.getNewConnector(); } @Override public Rectangle paint(FGEGraphics drawingGraphics) { // System.out.println("Focused:"+nodeGR.getIsFocused()); if (shapeGR.getIsSelected() && !shapeGR.getIsFocused()) { return null; } if (/*nodeGR.getIsSelected() ||*/shapeGR.isResizing() || shapeGR.isMoving()) { return null; } if (!shapeGR.getDrawing().isEditable()) { return null; } AffineTransform at = GraphicalRepresentation.convertNormalizedCoordinatesAT(shapeGR, drawingGraphics.getGraphicalRepresentation()); Graphics2D oldGraphics = drawingGraphics.cloneGraphics(); drawingGraphics.setDefaultForeground(NONE); drawingGraphics.setDefaultBackground(DEFAULT); FGERoundRectangle paletteRect = (FGERoundRectangle) getArea().transform(at); FGERoundRectangle nodeRect = (FGERoundRectangle) this.roleRect.transform(at); FGERectangle edgeRect = (FGERectangle) this.edgeRect.transform(at); double arrowSize = 4/** drawingGraphics.getScale() */ ; paletteRect.paint(drawingGraphics); // 1. Node drawingGraphics.setDefaultForeground(NODE_FOREGROUND); drawingGraphics.setDefaultBackground(NODE_BACKGROUND); nodeRect.paint(drawingGraphics); // 2. Edge drawingGraphics.setDefaultForeground(EDGE_FOREGROUND); // drawingGraphics.setDefaultBackground(EDGE_BACKGROUND); drawingGraphics.useDefaultForegroundStyle(); // drawingGraphics.useDefaultBackgroundStyle(); FGEPoint eastPt, westPt, northPt, southPt; switch (orientation) { case EAST: eastPt = edgeRect.getEastPt(); westPt = edgeRect.getWestPt(); drawingGraphics.drawLine(westPt.x, westPt.y, eastPt.x - arrowSize, eastPt.y); drawingGraphics.drawLine(eastPt.x - arrowSize, edgeRect.y + 1, eastPt.x - arrowSize, edgeRect.y + ELEMENTS_HEIGHT - 1); drawingGraphics.drawLine(eastPt.x - arrowSize, edgeRect.y + 1, eastPt.x, eastPt.y); drawingGraphics.drawLine(eastPt.x - arrowSize, edgeRect.y + ELEMENTS_HEIGHT - 1, eastPt.x, eastPt.y); break; case WEST: eastPt = edgeRect.getEastPt(); westPt = edgeRect.getWestPt(); drawingGraphics.drawLine(eastPt.x, eastPt.y, edgeRect.x + arrowSize, eastPt.y); drawingGraphics.drawLine(edgeRect.x + arrowSize, edgeRect.y + 1, edgeRect.x + arrowSize, edgeRect.y + ELEMENTS_HEIGHT - 1); drawingGraphics.drawLine(edgeRect.x + arrowSize, edgeRect.y + 1, westPt.x, westPt.y); drawingGraphics.drawLine(edgeRect.x + arrowSize, edgeRect.y + ELEMENTS_HEIGHT - 1, westPt.x, westPt.y); break; case NORTH: northPt = edgeRect.getNorthPt(); southPt = edgeRect.getSouthPt(); drawingGraphics.drawLine(southPt.x, southPt.y, southPt.x, edgeRect.y + arrowSize); drawingGraphics.drawLine(edgeRect.x + 2, edgeRect.y + arrowSize, edgeRect.x + ELEMENTS_WIDTH - 2, edgeRect.y + arrowSize); drawingGraphics.drawLine(edgeRect.x + 2, edgeRect.y + arrowSize, northPt.x, northPt.y); drawingGraphics.drawLine(edgeRect.x + ELEMENTS_WIDTH - 2, edgeRect.y + arrowSize, northPt.x, northPt.y); break; case SOUTH: northPt = edgeRect.getNorthPt(); southPt = edgeRect.getSouthPt(); drawingGraphics.drawLine(northPt.x, northPt.y, northPt.x, southPt.y - arrowSize); drawingGraphics.drawLine(edgeRect.x + 2, southPt.y - arrowSize, edgeRect.x + ELEMENTS_WIDTH - 2, southPt.y - arrowSize); drawingGraphics.drawLine(edgeRect.x + 2, southPt.y - arrowSize, southPt.x, southPt.y); drawingGraphics.drawLine(edgeRect.x + ELEMENTS_WIDTH - 2, southPt.y - arrowSize, southPt.x, southPt.y); break; default: break; } drawingGraphics.releaseClonedGraphics(oldGraphics); return drawingGraphics.getGraphicalRepresentation().convertNormalizedRectangleToViewCoordinates(paletteRect.getBoundingBox(), drawingGraphics.getScale()); } @Override public boolean isClickable() { return false; } @Override public void update(Observable o, Object arg) { if (o == shapeGR) { if (arg instanceof ObjectResized) { updateElements(orientation); } } } private int PALETTE_WIDTH = 16; private int PALETTE_HEIGHT = 2 * ELEMENTS_HEIGHT + 3 * SPACING; private void updateElements(SimplifiedCardinalDirection orientation) { setArea(makeRoundRect(shapeGR, orientation)); AffineTransform at = AffineTransform.getScaleInstance(1 / shapeGR.getWidth(), 1 / shapeGR.getHeight()); if (orientation == SimplifiedCardinalDirection.EAST || orientation == SimplifiedCardinalDirection.WEST) { PALETTE_WIDTH = ELEMENTS_WIDTH + 4; PALETTE_HEIGHT = 2 * ELEMENTS_HEIGHT + 3 * SPACING; } else if (orientation == SimplifiedCardinalDirection.NORTH || orientation == SimplifiedCardinalDirection.SOUTH) { PALETTE_WIDTH = 2 * ELEMENTS_WIDTH + 3 * SPACING; PALETTE_HEIGHT = ELEMENTS_HEIGHT + 4; } switch (orientation) { case EAST: roleRect = (FGERoundRectangle) new FGERoundRectangle(shapeGR.getWidth() + SPACING + (PALETTE_WIDTH - ELEMENTS_WIDTH) / 2 + 0.5, (shapeGR.getHeight() - PALETTE_HEIGHT) / 2 + SPACING, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, 2, 2, Filling.FILLED) .transform(at); edgeRect = (FGERectangle) new FGERectangle(shapeGR.getWidth() + SPACING + (PALETTE_WIDTH - ELEMENTS_WIDTH) / 2, (shapeGR.getHeight() - PALETTE_HEIGHT) / 2 + SPACING + (SPACING + ELEMENTS_HEIGHT), ELEMENTS_WIDTH, ELEMENTS_HEIGHT, Filling.FILLED).transform(at); break; case WEST: roleRect = (FGERoundRectangle) new FGERoundRectangle(-SPACING - ELEMENTS_WIDTH, (shapeGR.getHeight() - PALETTE_HEIGHT) / 2 + SPACING, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, 2, 2, Filling.FILLED).transform(at); edgeRect = (FGERectangle) new FGERectangle(-SPACING - ELEMENTS_WIDTH, (shapeGR.getHeight() - PALETTE_HEIGHT) / 2 + SPACING + (SPACING + ELEMENTS_HEIGHT), ELEMENTS_WIDTH, ELEMENTS_HEIGHT, Filling.FILLED).transform(at); break; case NORTH: roleRect = (FGERoundRectangle) new FGERoundRectangle((shapeGR.getWidth() - PALETTE_WIDTH) / 2 + SPACING, -SPACING - ELEMENTS_HEIGHT, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, 2, 2, Filling.FILLED).transform(at); edgeRect = (FGERectangle) new FGERectangle((shapeGR.getWidth() - PALETTE_WIDTH) / 2 + SPACING + (SPACING + ELEMENTS_WIDTH), -SPACING - ELEMENTS_HEIGHT, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, Filling.FILLED).transform(at); break; case SOUTH: roleRect = (FGERoundRectangle) new FGERoundRectangle((shapeGR.getWidth() - PALETTE_WIDTH) / 2 + SPACING, shapeGR.getHeight() + SPACING + (PALETTE_HEIGHT - ELEMENTS_HEIGHT) / 2 + 0.5, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, 2, 2, Filling.FILLED) .transform(at); edgeRect = (FGERectangle) new FGERectangle((shapeGR.getWidth() - PALETTE_WIDTH) / 2 + SPACING + (SPACING + ELEMENTS_WIDTH), shapeGR.getHeight() + SPACING + (PALETTE_HEIGHT - ELEMENTS_HEIGHT) / 2 + 0.5, ELEMENTS_WIDTH, ELEMENTS_HEIGHT, Filling.FILLED).transform(at); break; default: break; } } private static FGERoundRectangle makeRoundRect(VEShapeGR shapeGR, SimplifiedCardinalDirection orientation) { double x, y, width, height; int PALETTE_WIDTH = 0, PALETTE_HEIGHT = 0; if (orientation == SimplifiedCardinalDirection.EAST || orientation == SimplifiedCardinalDirection.WEST) { PALETTE_WIDTH = ELEMENTS_WIDTH + 4; PALETTE_HEIGHT = 2 * ELEMENTS_HEIGHT + 3 * SPACING; } else if (orientation == SimplifiedCardinalDirection.NORTH || orientation == SimplifiedCardinalDirection.SOUTH) { PALETTE_WIDTH = 2 * ELEMENTS_WIDTH + 3 * SPACING; PALETTE_HEIGHT = ELEMENTS_HEIGHT + 4; } switch (orientation) { case EAST: x = shapeGR.getWidth() + SPACING; y = (shapeGR.getHeight() - PALETTE_HEIGHT) / 2; width = PALETTE_WIDTH; height = PALETTE_HEIGHT; return new FGERoundRectangle(x / shapeGR.getWidth(), y / shapeGR.getHeight(), width / shapeGR.getWidth(), height / shapeGR.getHeight(), 13.0 / shapeGR.getWidth(), 13.0 / shapeGR.getHeight(), Filling.FILLED); case WEST: x = -SPACING - ELEMENTS_WIDTH; y = (shapeGR.getHeight() - PALETTE_HEIGHT) / 2; width = PALETTE_WIDTH; height = PALETTE_HEIGHT; return new FGERoundRectangle(x / shapeGR.getWidth(), y / shapeGR.getHeight(), width / shapeGR.getWidth(), height / shapeGR.getHeight(), 13.0 / shapeGR.getWidth(), 13.0 / shapeGR.getHeight(), Filling.FILLED); case NORTH: x = (shapeGR.getWidth() - PALETTE_WIDTH) / 2; y = -SPACING - ELEMENTS_HEIGHT; width = PALETTE_WIDTH; height = PALETTE_HEIGHT; return new FGERoundRectangle(x / shapeGR.getWidth(), y / shapeGR.getHeight(), width / shapeGR.getWidth(), height / shapeGR.getHeight(), 13.0 / shapeGR.getWidth(), 13.0 / shapeGR.getHeight(), Filling.FILLED); case SOUTH: x = (shapeGR.getWidth() - PALETTE_WIDTH) / 2; y = shapeGR.getHeight() + SPACING; width = PALETTE_WIDTH; height = PALETTE_HEIGHT; return new FGERoundRectangle(x / shapeGR.getWidth(), y / shapeGR.getHeight(), width / shapeGR.getWidth(), height / shapeGR.getHeight(), 13.0 / shapeGR.getWidth(), 13.0 / shapeGR.getHeight(), Filling.FILLED); default: return null; } } }