/* $Id: FigPackage.java 18728 2010-09-10 09:29:47Z mvw $ ***************************************************************************** * Copyright (c) 2009-2010 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bob Tarling * Michiel van der Wulp ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 1996-2009 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.uml.diagram.static_structure.ui; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.util.List; import java.util.Vector; import javax.swing.JOptionPane; import org.apache.log4j.Logger; import org.argouml.application.helpers.ResourceLoaderWrapper; import org.argouml.i18n.Translator; import org.argouml.kernel.Project; import org.argouml.model.Model; import org.argouml.model.RemoveAssociationEvent; import org.argouml.ui.ArgoJMenu; import org.argouml.ui.UndoableAction; import org.argouml.ui.explorer.ExplorerEventAdaptor; import org.argouml.ui.targetmanager.TargetManager; import org.argouml.uml.diagram.ArgoDiagram; import org.argouml.uml.diagram.DiagramFactory; import org.argouml.uml.diagram.DiagramFactory.DiagramType; import org.argouml.uml.diagram.DiagramSettings; import org.argouml.uml.diagram.StereotypeContainer; import org.argouml.uml.diagram.VisibilityContainer; import org.argouml.uml.diagram.ui.ArgoFig; import org.argouml.uml.diagram.ui.ArgoFigText; import org.argouml.uml.diagram.ui.FigNodeModelElement; import org.tigris.gef.base.Editor; import org.tigris.gef.base.Geometry; import org.tigris.gef.base.Globals; import org.tigris.gef.base.LayerPerspective; import org.tigris.gef.presentation.Fig; import org.tigris.gef.presentation.FigPoly; import org.tigris.gef.presentation.FigRect; import org.tigris.gef.presentation.FigText; /** * Class to display graphics for a UML package in a class diagram, * consisting of a "tab" and a "body". <p> * * The tab of the Package Fig is build of 2 pieces: * the stereotypes at the top, and the name below it. <p> * * The name box covers the whole tab, i.e. its size * is always equal to the total size of the tab. * It is not transparent, and has a line border. * Its text sits at the bottom of the fig, to leave room for stereotypes. <p> * * The stereotype fig is transparent, and sits at the top * inside the name fig. It is drawn on top of the name fig box. <p> * * The tab of the Package Fig can only be resized by the user horizontally. * The body can be resized horizontally and vertically by the user. <p> * * Double clicking on the body has a special consequence: * the user is asked if he wants to create a new class diagram * for this package. <p> * * ArgoUML does not support the option of showing the name * of the package in the body, * as described in the UML standard (chapter Notation - Package). <p> * * Neither does ArgoUML currently support showing properties in the tab, * see issue 1214. <p> * * In front of the name, ArgoUML may optionally show the visibility. */ public class FigPackage extends FigNodeModelElement implements StereotypeContainer, VisibilityContainer { private static final Logger LOG = Logger.getLogger(FigPackage.class); /** The minimal height of the name. */ private static final int MIN_HEIGHT = 21; /** The minimal width of the name. */ private static final int MIN_WIDTH = 50; /** The initial width of the outer box. */ private int width = 140; /** The initial height of the outer box. */ private int height = 100; /** The width of the cut out area at the right top corner. */ private int indentX = 50; private int textH = 20; /** * The total height of the tab. */ private int tabHeight = 20; private FigPackageFigText body; private PackageBackground background; private FigPoly figPoly; /** * Flag that indicates if the user wants any stereotype to be shown. * It corresponds to the check-mark on the Presentation tab. * There is no relation with the actual presence of any stereotypes. * This setting has Fig-scope, hence it is saved with the Fig layout data. */ private boolean stereotypeVisible = true; @Override protected Fig createBigPortFig() { PackagePortFigRect ppfr = new PackagePortFigRect(0, 0, width, height, indentX, tabHeight); ppfr.setFilled(false); ppfr.setLineWidth(0); return ppfr; } private void initialize() { body.setEditable(false); background = new PackageBackground(0, 0, width, height, indentX, tabHeight); getNameFig().setBounds(0, 0, width - indentX, textH + 2); getNameFig().setJustification(FigText.JUSTIFY_LEFT); // Set properties of the stereotype box. // Initially not set to be displayed, but this will be changed // when we try to render it, if we find we have a stereotype. getStereotypeFig().setVisible(false); // add Figs to the FigNode in back-to-front order addFig(getBigPort()); addFig(background); addFig(getNameFig()); addFig(getStereotypeFig()); addFig(body); setBlinkPorts(false); //make port invisible unless mouse enters // Make all the parts match the main fig setFilled(true); setFillColor(FILL_COLOR); setLineColor(LINE_COLOR); setLineWidth(LINE_WIDTH); updateEdges(); } /** * Construct a package figure with the given owner, bounds, and rendering * settings. This constructor is used by the PGML parser. * * @param owner owning model element * @param bounds position and size or null if fig hasn't been placed * @param settings rendering settings */ public FigPackage(Object owner, Rectangle bounds, DiagramSettings settings) { super(owner, bounds, settings); // Create a Body that reacts to double-clicks and jumps to a diagram. body = new FigPackageFigText(getOwner(), new Rectangle(0, textH, width, height - textH), getSettings()); initialize(); if (bounds != null) { setLocation(bounds.x, bounds.y); } setBounds(getBounds()); } /* * @see java.lang.Object#clone() */ @Override public Object clone() { FigPackage figClone = (FigPackage) super.clone(); for (Fig thisFig : (List<Fig>) getFigs()) { if (thisFig == body) { figClone.body = (FigPackageFigText) thisFig; } } return figClone; } /** * @return Returns the fig for the symbol. */ protected FigPoly getFigPoly() { return figPoly; } /** * @param figPoly The fig for the symbol to set. */ protected void setFigPoly(FigPoly figPoly) { this.figPoly = figPoly; } /* * @see org.tigris.gef.presentation.Fig#setLineColor(java.awt.Color) */ @Override public void setLineColor(Color col) { super.setLineColor(col); getStereotypeFig().setLineColor(null); getNameFig().setLineColor(col); body.setLineColor(col); if (figPoly != null) { figPoly.setLineColor(col); } } /* * @see org.tigris.gef.presentation.Fig#getLineColor() */ @Override public Color getLineColor() { return body.getLineColor(); } /* * @see org.tigris.gef.presentation.Fig#getFillColor() */ @Override public Color getFillColor() { return background.getFillColor(); } @Override public boolean isFilled() { return background.isFilled(); } /* * @see org.tigris.gef.presentation.Fig#setLineWidth(int) */ @Override public void setLineWidth(int w) { // There are 2 boxes showing lines: the tab and the body. getNameFig().setLineWidth(w); body.setLineWidth(w); } /* * @see org.tigris.gef.presentation.Fig#getLineWidth() */ @Override public int getLineWidth() { return body.getLineWidth(); } /* * @see org.argouml.uml.diagram.ui.FigNodeModelElement#updateStereotypeText() */ @Override protected void updateStereotypeText() { Object modelElement = getOwner(); if (modelElement == null) { return; } Rectangle rect = getBounds(); /* check if any stereotype is defined */ if (Model.getFacade().getStereotypes(modelElement).isEmpty()) { if (getStereotypeFig().isVisible()) { getNameFig().setTopMargin(0); getStereotypeFig().setVisible(false); } // else nothing changed } else { /* we got at least one stereotype */ /* This populates the stereotypes area: */ super.updateStereotypeText(); if (!isStereotypeVisible()) { // the user wants to hide them getNameFig().setTopMargin(0); getStereotypeFig().setVisible(false); } else if (!getStereotypeFig().isVisible()) { getNameFig().setTopMargin( getStereotypeFig().getMinimumSize().height); getStereotypeFig().setVisible(true); } // else nothing changed } forceRepaintShadow(); setBounds(rect.x, rect.y, rect.width, rect.height); } /** * Override ancestor behaviour by always calling setBounds even if the * size hasn't changed. Without this override the Package bounds draw * incorrectly. This is not the best fix but is a workaround until the * true cause is known. See issue 6135. * * @see org.argouml.uml.diagram.ui.FigNodeModelElement#updateBounds() */ protected void updateBounds() { if (!isCheckSize()) { return; } Rectangle bbox = getBounds(); Dimension minSize = getMinimumSize(); bbox.width = Math.max(bbox.width, minSize.width); bbox.height = Math.max(bbox.height, minSize.height); setBounds(bbox.x, bbox.y, bbox.width, bbox.height); } /** * USED BY PGML.tee. * @return the class name and bounds together with compartment * visibility. */ @Override public String classNameAndBounds() { return super.classNameAndBounds() + "stereotypeVisible=" + isStereotypeVisible() + ";" + "visibilityVisible=" + isVisibilityVisible(); } /* * @see org.tigris.gef.presentation.Fig#getUseTrapRect() */ @Override public boolean getUseTrapRect() { return true; } /* * @see org.tigris.gef.presentation.Fig#getMinimumSize() */ @Override public Dimension getMinimumSize() { // Use "aSize" to build up the minimum size. Start with the size of the // name fig and build up. Dimension aSize = new Dimension(getNameFig().getMinimumSize()); if (figPoly != null) { /* The figPoly is located at the right of the name text. * The nameFig size is increased, so that it fits its text, * and the figPoly next to the text, all within the boundaries * of the nameFig. */ Dimension symbol = figPoly.getSize(); aSize.width += symbol.width; aSize.height = Math.max(aSize.height, symbol.height); } aSize.height = Math.max(aSize.height, MIN_HEIGHT); aSize.width = Math.max(aSize.width, MIN_WIDTH); // If we have any number of stereotypes displayed, then allow // some space for that (only width, height is included in nameFig): if (isStereotypeVisible()) { Dimension st = getStereotypeFig().getMinimumSize(); aSize.width = Math.max(aSize.width, st.width); } // take into account the tab is not as wide as the body: aSize.width += indentX + 1; // we want at least some of the package body to be displayed aSize.height += 28 + 2 * getLineWidth(); // And now aSize has the answer return aSize; } /** * Sets the bounds, but the size will be at least the one returned by * {@link #getMinimumSize()}.<p> * * If the required height is bigger, then the additional height is * not distributed among all figs (i.e. compartments), * but goes into the body. Hence, the * accumulated height of all visible figs equals the demanded height<p>. * * Some of this has "magic numbers" hardcoded in. In particular there is * a knowledge that the minimum height of a name compartment is 21 * pixels. This height is needed to be able to display the "Clarifier" * inside the name compartment. * * @param xa Desired X coordinate of upper left corner * * @param ya Desired Y coordinate of upper left corner * * @param w Desired width of the FigClass * * @param h Desired height of the FigClass */ @Override protected void setStandardBounds(int xa, int ya, int w, int h) { // Save our old boundaries (needed later), and get minimum size // info. "aSize" will be used to maintain a running calculation of our // size at various points. Rectangle oldBounds = getBounds(); // The new size can not be smaller than the minimum. Dimension minimumSize = getMinimumSize(); int newW = Math.max(w, minimumSize.width); int newH = Math.max(h, minimumSize.height); // Now resize all sub-figs, including not displayed figs. Start by the // name. We override the getMinimumSize if it is less than our view (21 // pixels hardcoded!). Add in the shared extra, plus in this case the // correction. Dimension nameMin = getNameFig().getMinimumSize(); int minNameHeight = Math.max(nameMin.height, MIN_HEIGHT); // Now sort out the stereotype display. If the stereotype is displayed, // move the upper boundary of the name compartment up and set new // bounds for the name and the stereotype compartments and the // stereoLineBlinder that blanks out the line between the two int currentY = ya; int tabWidth = newW - indentX; if (isStereotypeVisible()) { Dimension stereoMin = getStereotypeFig().getMinimumSize(); getNameFig().setTopMargin(stereoMin.height); getNameFig().setBounds(xa, currentY, tabWidth + 1, minNameHeight); getStereotypeFig().setBounds(xa, ya, tabWidth, stereoMin.height + 1); if (tabWidth < stereoMin.width + 1) { tabWidth = stereoMin.width + 2; } } else { getNameFig().setBounds(xa, currentY, tabWidth + 1, minNameHeight); } // Advance currentY to where the start of the body box is, // remembering that it overlaps the next box by 1 pixel. Calculate the // size of the body box, and update the Y pointer past it if it is // displayed. currentY += minNameHeight - 1; // -1 for 1 pixel overlap body.setBounds(xa, currentY, newW, newH + ya - currentY); tabHeight = currentY - ya; // set bounds of big box getBigPort().setBounds(xa, ya, newW, newH); if (figPoly != null) { /* The figPoly is located at the right edge of the nameFig. * The nameFig size is such that it at least fits its text, * and the figPoly next to the text. * Making the package bigger, causes the figPoly to stick to * the right edge.*/ Rectangle previousBounds = figPoly.getBounds(); Rectangle name = getNameFig().getBounds(); int nx = name.x + name.width - figPoly.getWidth() - getLineWidth() - getNameFig().getRightMargin(); int ny = name.y + getLineWidth() + getNameFig().getTopMargin(); figPoly.translate((nx - previousBounds.x), ny - previousBounds.y); } // Now force calculation of the bounds of the figure, update the edges // and trigger anyone who's listening to see if the "bounds" property // has changed. background.setBounds(xa, ya, w, h); calcBounds(); updateEdges(); firePropChange("bounds", oldBounds, getBounds()); } /** * Build a collection of menu items relevant for a right-click * popup menu on a Package. * * @param me a mouse event * @return a collection of menu items */ @Override public Vector getPopUpActions(MouseEvent me) { Vector popUpActions = super.getPopUpActions(me); // Modifier ... popUpActions.add( popUpActions.size() - getPopupAddOffset(), buildModifierPopUp(ABSTRACT | LEAF | ROOT)); // Visibility ... popUpActions.add( popUpActions.size() - getPopupAddOffset(), buildVisibilityPopUp()); return popUpActions; } @Override protected ArgoJMenu buildShowPopUp() { ArgoJMenu showMenu = super.buildShowPopUp(); /* Only show the menuitems if they make sense: */ Editor ce = Globals.curEditor(); List<Fig> figs = ce.getSelectionManager().getFigs(); boolean sOn = false; boolean sOff = false; boolean vOn = false; boolean vOff = false; for (Fig f : figs) { if (f instanceof StereotypeContainer) { boolean v = ((StereotypeContainer) f).isStereotypeVisible(); if (v) { sOn = true; } else { sOff = true; } v = ((VisibilityContainer) f).isVisibilityVisible(); if (v) { vOn = true; } else { vOff = true; } } } if (sOn) { showMenu.add(new HideStereotypeAction()); } if (sOff) { showMenu.add(new ShowStereotypeAction()); } if (vOn) { showMenu.add(new HideVisibilityAction()); } if (vOff) { showMenu.add(new ShowVisibilityAction()); } return showMenu; } /** * Change the visibility of the stereotypes of all Figs. * * @param value true if it needs to become visible */ private void doStereotype(boolean value) { Editor ce = Globals.curEditor(); List<Fig> figs = ce.getSelectionManager().getFigs(); for (Fig f : figs) { if (f instanceof StereotypeContainer) { ((StereotypeContainer) f).setStereotypeVisible(value); } if (f instanceof FigNodeModelElement) { ((FigNodeModelElement) f).forceRepaintShadow(); ((ArgoFig) f).renderingChanged(); } f.damage(); } } /** * Change the visibility of the Visibility of all Figs. * * @param value true if it needs to become visible */ private void doVisibility(boolean value) { Editor ce = Globals.curEditor(); List<Fig> figs = ce.getSelectionManager().getFigs(); for (Fig f : figs) { if (f instanceof VisibilityContainer) { ((VisibilityContainer) f).setVisibilityVisible(value); } f.damage(); } } /** * A text fig for the body of a a Package * which does not contain any text, * but solely exists to trigger a jump to a diagram for * the named package when double clicked. */ class FigPackageFigText extends ArgoFigText { /** * Construct a text fig for a Package which will jump to diagram for * the named package when double clicked. * * @param owner owning UML element * @param bounds position and size * @param settings render settings */ public FigPackageFigText(Object owner, Rectangle bounds, DiagramSettings settings) { super(owner, bounds, settings, false); } /** * TODO: mvw: Would it not be better if this code * would go in startTextEditor(), not overruling mouseClicked(). * But we made this fig not editable, * to stop it from reacting on key-presses. * Anyhow - this is a hack - abusing a FigText - GEF does * not really support double-clicking on a Fig to trigger some action. */ @Override public void mouseClicked(MouseEvent me) { String lsDefaultName = "main"; // TODO: This code appears to be designed to jump to the diagram // containing the contents of the package that was double clicked // but it looks like it's always searching for the name "main" // instead of the package name. // TODO: But in any case, it should be delegating this work to // to something that knows about the diagrams and they contents -tfm if (me.getClickCount() >= 2) { Object lPkg = FigPackage.this.getOwner(); if (lPkg != null) { Object lNS = lPkg; Project lP = getProject(); List<ArgoDiagram> diags = lP.getDiagramList(); ArgoDiagram lFirst = null; for (ArgoDiagram lDiagram : diags) { Object lDiagramNS = lDiagram.getNamespace(); if ((lNS == null && lDiagramNS == null) || (lNS.equals(lDiagramNS))) { /* save first */ if (lFirst == null) { lFirst = lDiagram; } if (lDiagram.getName() != null && lDiagram.getName().startsWith( lsDefaultName)) { me.consume(); super.mouseClicked(me); TargetManager.getInstance().setTarget(lDiagram); return; } } } /*while*/ /* If we get here then we didn't get the * default diagram. */ if (lFirst != null) { me.consume(); super.mouseClicked(me); TargetManager.getInstance().setTarget(lFirst); return; } /* Try to create a new class diagram. */ me.consume(); super.mouseClicked(me); try { createClassDiagram(lNS, lsDefaultName, lP); } catch (Exception ex) { LOG.error(ex); } return; } /*if package */ } /* if doubleclicks */ super.mouseClicked(me); } public void setFilled(boolean f) { super.setFilled(false); } public void setFillColor(Color c) { super.setFillColor(c); } /** * The UID. */ private static final long serialVersionUID = -1355316218065323634L; } private void createClassDiagram( Object namespace, String defaultName, Project project) throws PropertyVetoException { String namespaceDescr; if (namespace != null && Model.getFacade().getName(namespace) != null) { namespaceDescr = Model.getFacade().getName(namespace); } else { namespaceDescr = Translator.localize("misc.name.anon"); } String dialogText = "Add new class diagram to " + namespaceDescr + "?"; int option = JOptionPane.showConfirmDialog( null, dialogText, "Add new class diagram?", JOptionPane.YES_NO_OPTION); if (option == JOptionPane.YES_OPTION) { ArgoDiagram classDiagram = DiagramFactory.getInstance(). createDiagram(DiagramType.Class, namespace, null); String diagramName = defaultName + "_" + classDiagram.getName(); project.addMember(classDiagram); TargetManager.getInstance().setTarget(classDiagram); /* change prefix */ classDiagram.setName(diagramName); ExplorerEventAdaptor.getInstance().structureChanged(); } } /* * @see org.argouml.uml.diagram.ui.StereotypeContainer#isStereotypeVisible() */ public boolean isStereotypeVisible() { return stereotypeVisible; } /* * @see org.argouml.uml.diagram.ui.StereotypeContainer#setStereotypeVisible(boolean) */ public void setStereotypeVisible(boolean isVisible) { stereotypeVisible = isVisible; renderingChanged(); damage(); } /* * @see org.argouml.uml.diagram.ui.VisibilityContainer#isVisibilityVisible() */ public boolean isVisibilityVisible() { return getNotationSettings().isShowVisibilities(); } /* * @see org.argouml.uml.diagram.ui.VisibilityContainer#setVisibilityVisible(boolean) */ public void setVisibilityVisible(boolean isVisible) { getNotationSettings().setShowVisibilities(isVisible); renderingChanged(); damage(); } /* * @see org.argouml.uml.diagram.ui.FigNodeModelElement#textEditStarted(org.tigris.gef.presentation.FigText) */ @Override protected void textEditStarted(FigText ft) { if (ft == getNameFig()) { showHelp("parsing.help.fig-package"); } } /* * @see org.tigris.gef.presentation.Fig#getClosestPoint(java.awt.Point) */ @Override public Point getClosestPoint(Point anotherPt) { Rectangle r = getBounds(); int[] xs = { r.x, r.x + r.width - indentX, r.x + r.width - indentX, r.x + r.width, r.x + r.width, r.x, r.x, }; int[] ys = { r.y, r.y, r.y + tabHeight, r.y + tabHeight, r.y + r.height, r.y + r.height, r.y, }; Point p = Geometry.ptClosestTo( xs, ys, 7, anotherPt); return p; } @Override protected void modelChanged(PropertyChangeEvent mee) { if (mee instanceof RemoveAssociationEvent && "ownedElement".equals(mee.getPropertyName()) && mee.getSource() == getOwner()) { // A model element has been removed from this packages namespace // If the Fig representing that model element is on the same // diagram as this package then make sure it is not enclosed by // this package. // TODO: In my view the Fig representing the model element should be // removed from the diagram. Yet to be agreed. Bob. if (LOG.isInfoEnabled() && mee.getNewValue() == null) { LOG.info(Model.getFacade().getName(mee.getOldValue()) + " has been removed from the namespace of " + Model.getFacade().getName(getOwner()) + " by notice of " + mee.toString()); } LayerPerspective layer = (LayerPerspective) getLayer(); Fig f = layer.presentationFor(mee.getOldValue()); if (f != null && f.getEnclosingFig() == this) { removeEnclosedFig(f); f.setEnclosingFig(null); } } super.modelChanged(mee); } /** * The UID. */ private static final long serialVersionUID = 3617092272529451041L; private class HideStereotypeAction extends UndoableAction { /** * The key for the action name. */ private static final String ACTION_KEY = "menu.popup.show.hide-stereotype"; /** * Constructor. */ HideStereotypeAction() { super(Translator.localize(ACTION_KEY), ResourceLoaderWrapper.lookupIcon(ACTION_KEY)); } /* * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent ae) { super.actionPerformed(ae); doStereotype(false); } /** * The UID. */ private static final long serialVersionUID = 1999499813643610674L; } private class ShowStereotypeAction extends UndoableAction { /** * The key for the action name. */ private static final String ACTION_KEY = "menu.popup.show.show-stereotype"; /** * Constructor. */ ShowStereotypeAction() { super(Translator.localize(ACTION_KEY), ResourceLoaderWrapper.lookupIcon(ACTION_KEY)); } /* * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent ae) { super.actionPerformed(ae); doStereotype(true); } /** * The UID. */ private static final long serialVersionUID = -4327161642276705610L; } private class HideVisibilityAction extends UndoableAction { /** * The key for the action name. */ private static final String ACTION_KEY = "menu.popup.show.hide-visibility"; /** * Constructor. */ HideVisibilityAction() { super(Translator.localize(ACTION_KEY), ResourceLoaderWrapper.lookupIcon(ACTION_KEY)); } /* * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent ae) { super.actionPerformed(ae); doVisibility(false); } /** * The UID. */ private static final long serialVersionUID = 8574809709777267866L; } private class ShowVisibilityAction extends UndoableAction { /** * The key for the action name. */ private static final String ACTION_KEY = "menu.popup.show.show-visibility"; /** * Constructor. */ ShowVisibilityAction() { super(Translator.localize(ACTION_KEY), ResourceLoaderWrapper.lookupIcon(ACTION_KEY)); } /* * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent ae) { super.actionPerformed(ae); doVisibility(true); } /** * The UID. */ private static final long serialVersionUID = 7722093402948975834L; } private class PackageBackground extends FigPoly { int indentX; int tabHeight; /** * The constructor. * * @param x The x. * @param y The y. * @param w The width. * @param h The height. * @param ix The indent. * @param th The tab height. */ public PackageBackground(int x, int y, int w, int h, int ix, int th) { super(x, y); addPoint(x + ix - 1, y); addPoint(x + ix - 1, y + th); addPoint(x + w - 1, y + th); addPoint(x + w - 1, y + h); addPoint(x, y + h); addPoint(x, y); setFilled(true); this.indentX = ix; tabHeight = 30; } /* * @see org.tigris.gef.presentation.Fig#getClosestPoint(java.awt.Point) */ @Override public Point getClosestPoint(Point anotherPt) { Rectangle r = getBounds(); int[] xs = { r.x, r.x + r.width - indentX, r.x + r.width - indentX, r.x + r.width, r.x + r.width, r.x, r.x, }; int[] ys = { r.y, r.y, r.y + tabHeight, r.y + tabHeight, r.y + r.height, r.y + r.height, r.y, }; Point p = Geometry.ptClosestTo( xs, ys, 7, anotherPt); return p; }; public void setBoundsImpl( final int x, final int y, final int w, final int h) { final int labelWidth = getNameBounds().width; final int labelHeight = getNameBounds().height; final int xs[] = new int[7]; final int ys[] = new int[7]; xs[0] = x; ys[0] = y; xs[1] = x + labelWidth - 1; ys[1] = y; xs[2] = x + labelWidth - 1; ys[2] = y + labelHeight - 1; xs[3] = x + w - 1; ys[3] = y + labelHeight - 1; xs[4] = x + w - 1; ys[4] = y + h - 1; xs[5] = x; ys[5] = y + h - 1; xs[6] = x; ys[6] = y; Polygon p = new Polygon(xs, ys, 7); super.setPolygon(p); setFilled(true); setLineWidth(0); } public void setLineWidth(int w) { super.setLineWidth(0); } } } /* end class FigPackage */ /** * The bigport needs to overrule the getClosestPoint, * because it is the port of this FigNode. * * @author mvw@tigris.org */ class PackagePortFigRect extends FigRect { private int indentX; private int tabHeight; /** * The constructor. * * @param x The x. * @param y The y. * @param w The width. * @param h The height. * @param ix The indent. * @param th The tab height. */ public PackagePortFigRect(int x, int y, int w, int h, int ix, int th) { super(x, y, w, h, null, null); this.indentX = ix; tabHeight = th; } /* * @see org.tigris.gef.presentation.Fig#getClosestPoint(java.awt.Point) */ @Override public Point getClosestPoint(Point anotherPt) { Rectangle r = getBounds(); int[] xs = { r.x, r.x + r.width - indentX, r.x + r.width - indentX, r.x + r.width, r.x + r.width, r.x, r.x, }; int[] ys = { r.y, r.y, r.y + tabHeight, r.y + tabHeight, r.y + r.height, r.y + r.height, r.y, }; Point p = Geometry.ptClosestTo( xs, ys, 7, anotherPt); return p; } public void setFilled(boolean f) { super.setFilled(false); } public void setFillColor(Color c) { super.setFillColor(null); } /** * The UID. */ private static final long serialVersionUID = -7083102131363598065L; }