// $Id: FigEditableCompartment.java 132 2010-09-26 23:32:33Z marcusvnac $
// 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.ui;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
//#if defined(LOGGING)
//@#$LPS-LOGGING:GranularityType:Import
import org.apache.log4j.Logger;
//#endif
import org.argouml.model.InvalidElementException;
import org.argouml.notation.NotationProvider;
import org.argouml.uml.diagram.DiagramSettings;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigLine;
/**
* Presentation logic for a boxed compartment,
* which is common to e.g. an operations
* compartment and an attributes compartment.<p>
*
* This class is adds the possibility to
* make the whole compartment invisible, and
* a NotationProvider is used to handle (generate and parse)
* the texts shown in the compartment, i.e.
* the compartment texts are editable by the user. <p>
*
* This FigGroup shall only contain its bigPort,
* and Figs of type FigSeparator, and CompartmentFigText.
*/
public abstract class FigEditableCompartment extends FigCompartment {
//#if defined(LOGGING)
//@#$LPS-LOGGING:GranularityType:Field
private static final Logger LOG = Logger.getLogger(FigCompartment.class);
//#endif
private static final int MIN_HEIGHT = FigNodeModelElement.NAME_FIG_HEIGHT;
private FigSeperator compartmentSeperator;
/**
* The constructor. <p>
*
* Two figs are added to this FigGroup:
* The bigPort (i.e. a box that encloses all compartments),
* and a separator.
*
* @param x x
* @param y y
* @param w width
* @param h height
* @deprecated for 0.27.3 by tfmorris. Use
* {@link #FigEditableCompartment(Object, Rectangle, DiagramSettings)}.
*/
@SuppressWarnings("deprecation")
@Deprecated
public FigEditableCompartment(int x, int y, int w, int h) {
super(x, y, w, h); // This adds bigPort, i.e. number 1
constructFigs();
}
private void constructFigs() {
compartmentSeperator = new FigSeperator(X0, Y0, 11);
addFig(compartmentSeperator); // number 2
}
/**
* Construct a new FigGroup containing a "bigPort" or rectangle which
* encloses the entire group for use in attaching edges, etc and a
* separator.
* <p>
* NOTE: Subclasses should call populate() when they are fully constructed.
*
* @param owner owning UML element
* @param bounds bounding rectangle of fig
* @param settings render settings
*/
public FigEditableCompartment(Object owner, Rectangle bounds,
DiagramSettings settings) {
super(owner, bounds, settings); // This adds bigPort, i.e. number 1
constructFigs();
// We'd like to call populate here, but our subclasses might not be
// completely built yet, so we defer this to them
}
/**
* @return separator figure
*/
protected FigSeperator getSeperatorFig() {
return compartmentSeperator;
}
/**
* If a boxed compartment is set to invisible then remove all its
* children.
* This is to save on resources and increase efficiency as multiple
* figs need not exist and be resized, moved etc if they are not visible.
* If a compartment is later made visible then its child figs are rebuilt
* from the model.
* {@inheritDoc}
*/
@Override
public void setVisible(boolean visible) {
if (isVisible() == visible) {
return;
}
super.setVisible(visible);
if (visible) {
populate();
} else {
for (int i = getFigs().size() - 1; i >= 0; --i) {
Fig f = getFigAt(i);
if (f instanceof CompartmentFigText) {
removeFig(f);
}
}
}
}
/*
* @see org.tigris.gef.presentation.FigGroup#addFig(org.tigris.gef.presentation.Fig)
*/
@Override
public void addFig(Fig fig) {
if (fig != getBigPort()
&& !(fig instanceof CompartmentFigText)
&& !(fig instanceof FigSeperator)) {
//#if defined(LOGGING)
//@#$LPS-LOGGING:GranularityType:Statement
//@#$LPS-LOGGING:Localization:NestedStatement
LOG.error("Illegal Fig added to a FigEditableCompartment");
//#endif
throw new IllegalArgumentException(
"A FigEditableCompartment can only "
+ "contain CompartmentFigTexts, "
+ "received a " + fig.getClass().getName());
}
super.addFig(fig);
}
/**
* @return the collection of UML objects
* on which this compartment is based
*/
protected abstract Collection getUmlCollection();
/**
* @return the type of the notationProvider
* used to handle the text in the compartment
*/
protected abstract int getNotationType();
/**
* Fills the Fig by adding all figs within.
*/
public void populate() {
if (!isVisible()) {
return;
}
Fig bigPort = this.getBigPort();
int xpos = bigPort.getX();
int ypos = bigPort.getY();
List<Fig> figs = getElementFigs();
// We remove all of them:
for (Fig f : figs) {
removeFig(f);
}
// We are going to add the ones still valid & new ones
// in the right sequence:
FigSingleLineTextWithNotation comp = null;
try {
int acounter = -1;
for (Object umlObject : getUmlCollection()) {
comp = findCompartmentFig(figs, umlObject);
acounter++;
// TODO: Some of these magic numbers probably assume a line
// width of 1. Replace with appropriate constants/variables.
// If we don't have a fig for this UML object, we'll need to add
// one. We set the bounds, but they will be reset later.
if (comp == null) {
comp = createFigText(umlObject, new Rectangle(
xpos + 1 /*?LINE_WIDTH?*/,
ypos + 1 /*?LINE_WIDTH?*/ + acounter
* ROWHEIGHT,
0,
ROWHEIGHT - 2 /*? 2*LINE_WIDTH? */),
getSettings());
} else {
/* This one is still usable, so let's retain it, */
/* but its position may have been changed: */
Rectangle b = comp.getBounds();
b.y = ypos + 1 /*?LINE_WIDTH?*/ + acounter * ROWHEIGHT;
// bounds not relevant here, but I am perfectionist...
comp.setBounds(b);
}
/* We need to set a new notationprovider, since
* the Notation language may have been changed: */
comp.initNotationProviders();
addFig(comp); // add it again (but now in the right sequence)
// Now put the text in
// We must handle the case where the text is null
String ftText = comp.getNotationProvider().toString(umlObject,
comp.getNotationSettings());
if (ftText == null) {
ftText = "";
}
comp.setText(ftText);
comp.setBotMargin(0);
}
} catch (InvalidElementException e) {
// TODO: It would be better here to continue the loop and try to
// build the rest of the compartment. Hence try/catch should be
// internal to the loop.
//#if defined(LOGGING)
//@#$LPS-LOGGING:GranularityType:Statement
LOG.debug("Attempted to populate a FigEditableCompartment"
+ " using a deleted model element - aborting", e);
//#endif
}
if (comp != null) {
comp.setBotMargin(6); // the last one needs extra space below it
}
}
/* Find the compartment fig for this umlObject: */
private CompartmentFigText findCompartmentFig(List<Fig> figs,
Object umlObject) {
for (Fig fig : figs) {
if (fig instanceof CompartmentFigText) {
CompartmentFigText candidate = (CompartmentFigText) fig;
if (candidate.getOwner() == umlObject) {
return candidate;
}
}
}
return null;
}
private List<Fig> getElementFigs() {
List<Fig> figs = new ArrayList<Fig>(getFigs());
// TODO: This is fragile and depends on the behavior of the super class
// not changing
if (figs.size() > 1) {
// Ignore the first 2 figs:
figs.remove(1); // the separator
figs.remove(0); // the bigPort
}
return figs;
}
/**
* @return null
* @deprecated for 0.27.3 by tfmorris. Subclasses must implement
* {@link #createFigText(Object, Rectangle, DiagramSettings,
* NotationProvider)}
* which will become abstract in the future when this deprecated method is
* removed.
*/
@Deprecated
protected FigSingleLineTextWithNotation createFigText(
int x, int y, int w, int h, Fig aFig, NotationProvider np) {
// No longer abstract to allow subclasses to remove, so we provide a
// null default implementation
return null;
}
/**
* Factory method to create a FigSingleLineTextWithNotation
* which must be implemented by all subclasses.
* It will become abstract after the release of 0.28 to
* enforce this requirement.
*
* @param owner owning UML element
* @param bounds position and size
* @param settings render settings
* @param np notation provider
* @return a FigSingleLineText which can be used to display the text.
*/
@SuppressWarnings("deprecation")
protected FigSingleLineTextWithNotation createFigText(Object owner,
Rectangle bounds,
@SuppressWarnings("unused") DiagramSettings settings,
NotationProvider np) {
// If this is not overridden it will revert to the old behavior
// All internal subclasses have been updated, but this if for
// compatibility of non-ArgoUML extensions.
FigSingleLineTextWithNotation comp = createFigText(
bounds.x,
bounds.y,
bounds.width,
bounds.height,
this.getBigPort(),
np);
comp.setOwner(owner);
return comp;
}
/**
* @param owner owning UML element
* @param bounds position and size
* @param settings the render settings
* @return a FigSingleLineText with notation provider
* which can be used to display the text
*/
abstract FigSingleLineTextWithNotation createFigText(Object owner,
Rectangle bounds,
DiagramSettings settings);
/**
* Returns the new size of the FigGroup (either attributes or
* operations) after calculation new bounds for all sub-figs,
* considering their minimal sizes; FigGroup need not be
* displayed; no update event is fired.<p>
*
* This method has side effects that are sometimes used.
*
* @param x x
* @param y y
* @param w w
* @param h h
* @return the new dimension
*/
public Dimension updateFigGroupSize(
int x,
int y,
int w,
int h,
boolean checkSize,
int rowHeight) {
return getMinimumSize();
}
/**
* The minimum width is the minimum width of the widest child element.
* The minimum height is the total minimum height of all child figs but no
* less than MINIMUM_HEIGHT pixels.
* @return the minimum width
*/
@Override
public Dimension getMinimumSize() {
Dimension d = super.getMinimumSize();
if (d.height < MIN_HEIGHT) {
d.height = MIN_HEIGHT;
}
return d;
}
/*
* @see org.tigris.gef.presentation.Fig#setBoundsImpl(int, int, int, int)
*/
@Override
protected void setBoundsImpl(int x, int y, int w, int h) {
int newW = w;
int newH = h;
int fw;
int yy = y;
int lineWidth = getLineWidth();
for (Fig fig : (List<Fig>) getFigs()) {
if (fig.isVisible() && fig != getBigPort()) {
if (fig instanceof FigSeperator) {
fw = w;
} else {
fw = fig.getMinimumSize().width;
}
fig.setBounds(x + lineWidth, yy + lineWidth, fw,
fig.getMinimumSize().height);
if (newW < fw + 2 * lineWidth) {
newW = fw + 2 * lineWidth;
}
yy += fig.getMinimumSize().height;
}
}
getBigPort().setBounds(x + lineWidth, y + lineWidth,
newW - 2 * lineWidth, newH - 2 * lineWidth);
calcBounds();
}
/**
* Fig representing separator for compartment.
* This is a horizontal line.
*/
protected static class FigSeperator extends FigLine {
/**
* Constructor.
* TODO: The line-width is set by the containing group - so
* no need to do that here also.
*
* @param x coordinate
* @param y coordinate
* @param len length of the line
*/
FigSeperator(int x, int y, int len) {
super(x, y, (x + len) - 1, y, LINE_COLOR);
setLineWidth(LINE_WIDTH);
}
/*
* @see org.tigris.gef.presentation.Fig#getSize()
*/
@Override
public Dimension getSize() {
return new Dimension((_x2 - _x1) + 1, getLineWidth());
}
/*
* @see org.tigris.gef.presentation.Fig#getMinimumSize()
*/
@Override
public Dimension getMinimumSize() {
return new Dimension(0, getLineWidth());
}
/*
* @see org.tigris.gef.presentation.Fig#setBoundsImpl(
* int, int, int, int)
*/
@Override
public void setBoundsImpl(int x, int y, int w, int h) {
setX1(x);
setY1(y);
setX2((x + w) - 1);
setY2(y);
}
/**
* The UID.
*/
private static final long serialVersionUID = -2222511596507221760L;
}
}