/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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 2 of the License, or * (at your option) any later version. * * Nomad 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 Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.sf.nmedit.jpatch.impl; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.lang.ref.SoftReference; import java.util.List; import javax.swing.event.EventListenerList; import javax.swing.undo.UndoableEdit; import net.sf.nmedit.jpatch.InvalidDescriptorException; import net.sf.nmedit.jpatch.PComponent; import net.sf.nmedit.jpatch.PConnectionManager; import net.sf.nmedit.jpatch.PConnector; import net.sf.nmedit.jpatch.PConnectorDescriptor; import net.sf.nmedit.jpatch.PDescriptor; import net.sf.nmedit.jpatch.PLight; import net.sf.nmedit.jpatch.PLightDescriptor; import net.sf.nmedit.jpatch.PModule; import net.sf.nmedit.jpatch.PModuleContainer; import net.sf.nmedit.jpatch.PModuleDescriptor; import net.sf.nmedit.jpatch.PModuleMetrics; import net.sf.nmedit.jpatch.PParameter; import net.sf.nmedit.jpatch.PParameterDescriptor; import net.sf.nmedit.jpatch.PPatch; import net.sf.nmedit.jpatch.PUndoableEditFactory; import net.sf.nmedit.jpatch.event.PModuleEvent; import net.sf.nmedit.jpatch.event.PModuleListener; import net.sf.nmedit.jpatch.util.ObjectCache; import net.sf.nmedit.jpatch.util.ObjectFilter; import net.sf.nmedit.jpatch.util.ObjectFilterResult; /** * The reference implementation of interface {@link PModule}. * @author Christian Schneider */ public class PBasicModule extends PBasicComponent<PModuleDescriptor> implements PModule { private static final PBasicParameter[] EMPTYP = new PBasicParameter[0]; private static final PConnector[] EMPTYC = new PConnector[0]; private static final PLight[] EMPTYL = new PLight[0]; private String title; private PBasicParameter[] parameters; private PConnector[] connectors; private PLight[] lights; private PModuleContainer parent; private EventListenerList listenerList = new EventListenerList(); protected int sx = 0; protected int sy = 0; private SoftReference<ParameterCache> paramCacheRef = null; public PBasicModule(PModuleDescriptor descriptor) { super(descriptor, -1); this.title = descriptor.getName(); createComponents(); } public List<PParameter> getParameters(ObjectFilter<PParameter> filter) { ParameterCache cache; if (paramCacheRef == null || (cache = paramCacheRef.get()) == null) paramCacheRef = new SoftReference<ParameterCache>(cache = new ParameterCache()); return cache.getItems(filter); } private class ParameterCache extends ObjectCache<PParameter> { @Override protected List<PParameter> applyFilter(ObjectFilter<PParameter> filter) { return ObjectFilterResult.filter(parameters, filter); } } void setComponentIndex(int index) { this.componentIndex = index; } public String getTitle() { return title; } protected UndoableEdit createRenameEdit(String oldtitle, String newtitle) { PUndoableEditFactory factory = getUndoableEditFactory(); if (factory != null) return factory.createRenameEdit(this, oldtitle, newtitle); return null; } protected UndoableEdit createMoveEdit(int oldScreenX, int oldScreenY, int newScreenX, int newScreenY) { PUndoableEditFactory factory = getUndoableEditFactory(); if (factory != null) return factory.createMoveEdit(this, oldScreenX, oldScreenY, newScreenX, newScreenY); return null; } public void setTitle(String title) { String oldtitle = this.title; String newtitle = title; if (!(oldtitle == newtitle || (oldtitle!=null && title.equals(oldtitle)))) { this.title = newtitle; if (isUndoableEditSupportEnabled()) { UndoableEdit edit = createRenameEdit(oldtitle, newtitle); if (edit != null) postEdit(edit); } fireModuleRenamed(oldtitle, newtitle); } } public void addModuleListener(PModuleListener l) { listenerList.add(PModuleListener.class, l); } public void removeModuleListener(PModuleListener l) { listenerList.remove(PModuleListener.class, l); } protected void fireModuleRenamed(String oldTitle, String newTitle) { PModuleEvent moduleEvent = null; // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==PModuleListener.class) { // Lazily create the event: if (moduleEvent == null) { moduleEvent = new PModuleEvent(this); moduleEvent.moduleRenamed(oldTitle); } ((PModuleListener)listeners[i+1]).moduleRenamed(moduleEvent); } } } protected void fireModuleMoved(int oldScreenX, int oldScreenY) { PModuleEvent moduleEvent = null; // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==PModuleListener.class) { // Lazily create the event: if (moduleEvent == null) { moduleEvent = new PModuleEvent(this); moduleEvent.moduleMoved(oldScreenX, oldScreenY); } ((PModuleListener)listeners[i+1]).moduleMoved(moduleEvent); } } } /** * Creates a connector component for this module using the specified descriptor. * Overwrite this to use a different connector implementation. * * @param descriptor describes the connector that will be created * @return connector */ protected PConnector createConnector(PConnectorDescriptor descriptor, int componentIndex) { return new PBasicConnector(this, descriptor, componentIndex); } /** * Creates a parameter component for this module using the specified descriptor. * Overwrite this to use a different parameter implementation. * * @param descriptor describes the parameter that will be created * @return parameter */ protected PBasicParameter createParameter(PParameterDescriptor descriptor, int componentIndex) { return new PBasicParameter(descriptor, this, componentIndex); } protected PLight createLight(PLightDescriptor descriptor, int componentIndex) { return new PBasicLight(descriptor, this, componentIndex); } /** * Creates the components of this module: connectors and parameters. * * @see #createConnector(PConnectorDescriptor,int) * @see #createParameter(PParameterDescriptor,int) */ protected void createComponents() { PModuleDescriptor d = getDescriptor(); if (d.getConnectorDescriptorCount()>0) { connectors = new PConnector[d.getConnectorDescriptorCount()]; for (int i = connectors.length-1; i>=0; i--) connectors[i] = createConnector(d.getConnectorDescriptor(i), i); } else { connectors = EMPTYC; } int offset = connectors.length; if (d.getParameterDescriptorCount()>0) { boolean extensions = false; parameters = new PBasicParameter[d.getParameterDescriptorCount()]; for (int i = parameters.length-1; i>=0; i--) { PParameterDescriptor p = d.getParameterDescriptor(i); parameters[i] = createParameter(p, offset+i); extensions |= p.getExtensionDescriptor()!=null; } if (extensions) { // link extensions for (int i=parameters.length-1;i>=0;i--) { PBasicParameter p = parameters[i]; PParameterDescriptor ext = p.getDescriptor().getExtensionDescriptor(); if (ext!=null) p.setExtensionParameter(parameters[ext.getDescriptorIndex()]); } } } else { parameters = EMPTYP; } offset+=parameters.length; if (d.getLightDescriptorCount()>0) { lights = new PLight[d.getLightDescriptorCount()]; for (int i = lights.length-1; i>=0; i--) lights[i] = createLight(d.getLightDescriptor(i), offset+i); } else { lights = EMPTYL; } } public PComponent getComponent(int index) { if (index<connectors.length) { return connectors[index]; } else { index -= connectors.length; if (index<parameters.length) { return parameters[index]; } else { index -= parameters.length; return lights[index]; } } } public int getComponentCount() { return connectors.length+parameters.length+lights.length; } public PConnector getConnector(PConnectorDescriptor descriptor) { PDescriptor parent = descriptor.getParentDescriptor(); PDescriptor thisDescriptor = getDescriptor(); if (!(thisDescriptor == parent || thisDescriptor.equals(parent))) throw new InvalidDescriptorException(descriptor, "parents are not equal "+descriptor+" in "+this); int index = descriptor.getDescriptorIndex(); if (index<0 || index>=getConnectorCount()) throw new InvalidDescriptorException(descriptor, "invalid connector index: "+index); return connectors[index]; } public PConnector getConnector(int index) { return connectors[index]; } public int getConnectorCount() { return connectors.length; } public PParameter getParameter(PParameterDescriptor descriptor) { PDescriptor parent = descriptor.getParentDescriptor(); if (!(getDescriptor() == parent || getDescriptor().equals(parent))) throw new InvalidDescriptorException(descriptor, "parents are not equal "+descriptor+" in "+this); int index = descriptor.getDescriptorIndex(); if (index<0 || index>=getParameterCount()) throw new InvalidDescriptorException(descriptor, "invalid parameter index: "+index); return parameters[index]; } public PLight getLight(PLightDescriptor descriptor) { PDescriptor parent = descriptor.getParentDescriptor(); if (!(getDescriptor() == parent || getDescriptor().equals(parent))) throw new InvalidDescriptorException(descriptor, "parents are not equal "+descriptor+" in "+this); int index = descriptor.getDescriptorIndex(); if (index<0 || index>=getLightCount()) throw new InvalidDescriptorException(descriptor, "invalid light index: "+index); return lights[index]; } public PParameter getParameter(int index) { return parameters[index]; } public PLight getLight(int index) { return lights[index]; } public int getLightCount() { return lights.length; } public int getParameterCount() { return parameters.length; } public PModuleContainer getParentComponent() { return parent; } public void setParent(PModuleContainer parent) { PModuleContainer oldParent = this.parent; if (oldParent != parent) { this.parent = parent; } } public int getComponentIndex() { return componentIndex; } public void removeAllConnections() { PModuleContainer c = getParentComponent(); if (c == null) return; PConnectionManager m = c.getConnectionManager(); if (m != null) m.removeAllConnections(m.connections(this)); } public PComponent getComponentByComponentId(Object id) { PDescriptor d = getDescriptor().getComponentByComponentId(id); if (d instanceof PConnectorDescriptor) return getConnector((PConnectorDescriptor)d); else if (d instanceof PParameterDescriptor) return getParameter((PParameterDescriptor)d); else return null; } public PConnector getConnectorByComponentId(Object id) { PConnectorDescriptor d = getDescriptor().getConnectorByComponentId(id); if (d != null) return getConnector(d); else return null; } public PParameter getParameterByComponentId(Object id) { PParameterDescriptor d = getDescriptor().getParameterByComponentId(id); if (d != null) return getParameter(d); else return null; } public PLight getLightByComponentId(Object id) { PLightDescriptor d = getDescriptor().getLightDescriptorByComponentId(id); if (d != null) return getLight(d); else return null; } public Point getScreenLocation() { return new Point(getScreenX(), getScreenY()); } public int getScreenX() { return sx; } public int getScreenY() { return sy; } public void setScreenLocation(int x, int y) { PModuleMetrics m = getModuleMetrics(); if (m != null) { x = m.alignScreenX(x); y = m.alignScreenY(y); x = Math.max(0, Math.min(x, m.getMaxScreenX())); y = Math.max(0, Math.min(y, m.getMaxScreenY())); } int oldx = this.sx; int oldy = this.sy; if (oldx!=x||oldy!=y) { this.sx = x; this.sy = y; if (isUndoableEditSupportEnabled()) { UndoableEdit edit = createMoveEdit(oldx, oldy, x, y); if (edit != null) postEdit(edit); } fireModuleMoved(oldx, oldy); } } public void setScreenLocation(Point location) { setScreenLocation(location.x, location.y); } public PPatch getPatch() { PModuleContainer mc = getParentComponent(); return mc == null ? null : mc.getPatch(); } public PModuleMetrics getModuleMetrics() { return getDescriptor().getModules().getMetrics(); } public int getInternalX() { PModuleMetrics m = getModuleMetrics(); return m == null ? sx : m.screenToInternalX(sx); } public int getInternalY() { PModuleMetrics m = getModuleMetrics(); return m == null ? sy : m.screenToInternalY(sy); } public Rectangle getInternalBounds(Rectangle rv) { if (rv == null) { return new Rectangle(getInternalX(), getInternalY(), getInternalWidth(), getInternalHeight()); } else { rv.setBounds(getInternalX(), getInternalY(), getInternalWidth(), getInternalHeight()); return rv; } } public Point getInternalLocation() { return new Point(getInternalX(), getInternalY()); } public void setInternalLocation(int x, int y) { PModuleMetrics m = getModuleMetrics(); if (m == null) setScreenLocation(x, y); else setScreenLocation(m.internalToScreenX(x), m.internalToScreenY(y)); } public void setInternalLocation(Point location) { setInternalLocation(location.x, location.y); } public int getInternalHeight() { PModuleMetrics m = getModuleMetrics(); return m == null ? getIntAttribute("height", 1) : m.getInternalHeight(this); } public int getInternalWidth() { PModuleMetrics m = getModuleMetrics(); return m == null ? getIntAttribute("width", 1) : m.getInternalWidth(this); } public int getScreenWidth() { PModuleMetrics m = getModuleMetrics(); return m == null ? getIntAttribute("width", 1) : m.getScreenWidth(this); } public int getScreenHeight() { PModuleMetrics m = getModuleMetrics(); return m == null ? getIntAttribute("height", 1) : m.getScreenHeight(this); } public Rectangle getScreenBounds(Rectangle rv) { if (rv == null) { return new Rectangle(getScreenX(), getScreenY(), getScreenWidth(), getScreenHeight()); } else { rv.setBounds(getScreenX(), getScreenY(), getScreenWidth(), getScreenHeight()); return rv; } } public void setInternalSize(int width, int height) { // ignore } public void setInternalSize(Dimension size) { setInternalSize(size.width, size.height); } public void setScreenSize(int width, int height) { PModuleMetrics m = getModuleMetrics(); if (m != null) setInternalSize(m.screenToInternalX(width), m.screenToInternalY(height)); } public void setScreenSize(Dimension size) { setScreenSize(size.width, size.height); } public String toString() { return getClass().getName()+"[name="+getName()+",component-id="+getComponentId()+",component-index="+getComponentIndex()+",title="+title+"]"; } public PModule cloneModule() { PBasicModule newM = new PBasicModule(getDescriptor()); newM.cloneFromModule(this); return newM; } public void cloneFromModule(PModule m) { for (int i = 0; i < m.getParameterCount(); i++) { PParameter src = m.getParameter(i); PParameter dst = getParameter(i); dst.setDoubleValue(src.getDoubleValue()); dst.setMorphGroup(src.getMorphGroup()); } title = m.getTitle(); sx = m.getScreenX(); sy = m.getScreenY(); } }