/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue;
import tufts.Util;
import java.awt.Color;
import java.awt.AlphaComposite;
import java.awt.geom.AffineTransform;
/**
* Special map portal.
*
* We need this for now to subclass LWNode just to support the shape property.
* If we were to move the shape key into LWComponent, we could do away with
* this class, and just use an LWComponent with dynamically disabled properies
* as we see fit...
*
* @version $Revision: 1.24 $ / $Date: 2010-02-03 19:17:40 $ / $Author: mike $
*/
public class LWPortal extends LWNode
{
public LWPortal() {
updateCapabilities();
mFillColor.setFixedAlpha(64);
mStrokeColor.setFixedAlpha(64);
}
@Override
public LWPortal duplicate(CopyContext cc) {
LWPortal newPortal = (LWPortal) super.duplicate(cc);
newPortal.updateCapabilities();
return newPortal;
}
public static LWPortal create() {
final LWPortal p = new LWPortal();
p.setStrokeWidth(0);
p.setSize(LWSlide.SlideWidth / 4, LWSlide.SlideHeight / 4);
//setAspect(LWSlide.SlideAspect);
p.setLabel(VueResources.getString("pathways.portal.label"));
return p;
}
@Override
public String getComponentTypeLabel() {
return "Interactive Frame";
}
@Override
protected void addEntryRef(LWPathway.Entry e) {
super.addEntryRef(e);
updateCapabilities();
}
@Override
protected void removeEntryRef(LWPathway.Entry e) {
super.removeEntryRef(e);
updateCapabilities();
}
private void updateCapabilities() {
disablePropertyTypes(KeyType.STYLE);
enableProperty(LWKey.StrokeWidth);
enableProperty(LWKey.StrokeColor);
enableProperty(LWKey.Shape);
if (inPathway()) {
// If a portal is on any pathway, it's fill-color is forced
// null and is always computed at draw time depending on the
// the current pathway.
setFillColor(null);
disableProperty(LWKey.FillColor);
} else {
// If a portal is "free", and not on a pathway,
// users can change the fill color (tho drawing
// either one of stroke or fill is enforced so
// as not to leave the portal invisible)
enableProperty(LWKey.FillColor);
}
}
/* override to do nothing so we aren't constrainted by LWNode's minimum size*/
//@Override protected void layoutImpl(Object triggerKey) {}
//@Override protected void layout(Object triggerKey, Size curSize, Size request) {} // overkill: shrinks to nothing?
/** override to so we aren't constrainted by LWNode's minimum size */
@Override
protected Size getTextSize() { return Size.None; }
@Override
protected void userSetSize(float width, float height, MapMouseEvent e)
{
if (e.isShiftDown()) {
// Allow constraining to slide aspect:
Size newSize = ConstrainToAspect(LWSlide.SlideAspect, width, height);
super.setSize(newSize.width, newSize.height);
} else {
super.setSize(width, height);
}
}
@Override
public boolean supportsUserLabel() {
return false;
}
/** @return false: portals can never have slides of their own */
@Override
public final boolean supportsSlide() {
return false;
}
@Override
public int getFocalMargin() {
return 0;
}
@Override
protected boolean containsImpl(final float x, final float y, PickContext pc) {
if (pc.isZoomRollover)
return false; // allow picking through the portal -- never zoom portals
else
return super.containsImpl(x, y, pc);
}
private static final Color DarkFill = new Color(0,0,0,64);
private static final Color LightFill = new Color(255,255,255,64);
private static final Color DebugFill = new Color(0,255,0,128);
private static final Color DefaultFill = new Color(128,128,128,128);
@Override
public Color getRenderFillColor(DrawContext dc) {
if (mFillColor.isTransparent())
return getMap().mFillColor.brightness() > 0.5 ? DarkFill : LightFill;
else
return getFillColor();
// if (false&&dc != null && dc.focal != null)
// return dc.focal.mFillColor.brightness() > 0.5 ? DarkFill : LightFill;
// else
// return getMap().mFillColor.brightness() > 0.5 ? DarkFill : LightFill;
}
@Override
protected void drawImpl(DrawContext dc)
{
if (dc.skipDraw == this)
return;
boolean printing = !(dc.isInteractive() || dc.isPresenting());
if (dc.focal == this || (printing && dc.focused == this)) {
final AffineTransform zeroTransform = dc.g.getTransform();
if (printing) {
// special case for printing, where we know we're being
// called from a drawFit, and the mapTransform in the
// DrawContext is meaningless.
dc.g.translate(-getX(), -getY());
} else {
dc.setMapDrawing();
}
dc.setDrawPathways(false);
dc.skipDraw = this;
if (DEBUG.CONTAINMENT) {
final DrawContext alphaDC = dc.create();
alphaDC.setAlpha(0.1, AlphaComposite.SRC);
getParent().draw(alphaDC);
alphaDC.dispose();
final DrawContext clipDC = dc.create();
clipDC.setMasterClip(getMapShape());
final LWContainer parent = getParent();
if (parent instanceof LWMap.Layer) {
// VUE-1381: need to draw all layers
parent.getMap().drawChildren(clipDC);
} else {
parent.draw(clipDC);
}
clipDC.dispose();
dc.g.setTransform(zeroTransform);
dc.setAbsoluteStroke(1);
dc.g.setColor(Color.red);
dc.g.draw(getZeroShape());
} else {
final DrawContext clipDC = dc.create();
clipDC.setMasterClip(getMapShape());
final LWContainer parent = getParent();
if (parent instanceof LWMap.Layer) {
// VUE-1381: need to draw all layers
parent.getMap().drawChildren(clipDC);
} else {
parent.draw(clipDC);
}
clipDC.dispose();
dc.g.setTransform(zeroTransform);
if (true || this.stroke == STROKE_ZERO)
dc.setAbsoluteStroke(2);
else
dc.g.setStroke(this.stroke);
dc.g.setColor(getContrastColor(dc.getBackgroundFill()));
dc.g.draw(getZeroShape());
}
} else if (dc.focal instanceof LWPortal) {
// no fill: don't show the portal fill if we, or any
// other portal, is currently the focal
} else if (hasEntries()) {
final Color c = getPriorityPathwayColor(dc);
if (c == null) {
dc.g.setColor(getRenderFillColor(dc));
} else {
final Color fill = new Color(c.getRed(), c.getGreen(), c.getBlue(), 64);
dc.g.setColor(fill);
}
//dc.g.setColor(DefaultFill);
if (dc.zoom > PathwayOnTopZoomThreshold) {
dc.g.setStroke(new java.awt.BasicStroke(LWPathway.PathBorderStrokeWidth));
dc.g.draw(getZeroShape());
} else {
dc.g.fill(getZeroShape());
}
} else {
if (this.stroke == STROKE_ZERO || !mFillColor.isTransparent()) {
// Show the portal region:
dc.g.setColor(getRenderFillColor(dc));
dc.g.fill(getZeroShape());
}
if (this.stroke != STROKE_ZERO) {
dc.g.setStroke(this.stroke);
dc.g.setColor(getStrokeColor());
dc.g.draw(getZeroShape());
}
}
}
/*
private boolean wasVisible = true;
@Override public boolean isVisible() {
// TODO: handle this in LWPathway and actually set a hidden bit...
boolean visible;
final java.util.Collection pathways = getPathways();
if (pathways.size() > 0) {
visible = false;
for (LWPathway p : getPathways())
if (p.isVisible())
visible = true;
} else
visible = true;
if (wasVisible != visible) {
wasVisible = visible;
notify(LWKey.Hidden);
}
return visible;
}
*/
/** @return false so LWNode.super won't draw it */
@Override
public boolean hasLabel() { return false; }
/** @return false -- nothing can be added to a portal */
@Override
public boolean supportsChildren() { return false; }
/** @return false so can't be dropped into anything else (e.g. a node) */
@Override
public boolean supportsReparenting() { return false; }
/** @return false -- doesn't display any icons */
@Override
protected boolean iconShowing() { return false; }
}