/* 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.Point; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.swing.event.EventListenerList; import javax.swing.undo.UndoableEdit; import net.sf.nmedit.jpatch.CopyOperation; import net.sf.nmedit.jpatch.MoveOperation; import net.sf.nmedit.jpatch.PConnectionManager; import net.sf.nmedit.jpatch.PModule; import net.sf.nmedit.jpatch.PModuleContainer; import net.sf.nmedit.jpatch.PModuleContainerDescriptor; import net.sf.nmedit.jpatch.PModuleDescriptor; import net.sf.nmedit.jpatch.PModuleMetrics; import net.sf.nmedit.jpatch.PPatch; import net.sf.nmedit.jpatch.PUndoableEditFactory; import net.sf.nmedit.jpatch.dnd.ModulesBoundingBox; import net.sf.nmedit.jpatch.event.PModuleContainerEvent; import net.sf.nmedit.jpatch.event.PModuleContainerListener; import net.sf.nmedit.jpatch.history.PUndoableEditSupport; import net.sf.nmedit.nmutils.collections.ArrayMap; /** * The reference implementation of interface {@link PModuleContainer}. * @author Christian Schneider */ public class PBasicModuleContainer extends PBasicComponent<PModuleContainerDescriptor> implements PModuleContainer { private ArrayMap<PModule> modules; private PPatch patch; private PConnectionManager connectionManager; private EventListenerList listenerList = new EventListenerList(); private transient PModuleContainerEvent mcEvent; private transient PModuleMetrics moduleMetrics; public PBasicModuleContainer(PPatch patch, String name, int componentIndex) { this(patch, new PBasicModuleContainerDescriptor(name, componentIndex), componentIndex); } public PBasicModuleContainer(PPatch patch, PModuleContainerDescriptor descriptor, int componentIndex) { super(descriptor, componentIndex); modules = new ArrayMap<PModule>(); modules.setMinKey(1); this.patch = patch; this.connectionManager = createConnectionManager(); } public PUndoableEditSupport getEditSupport() { return patch != null ? patch.getEditSupport() : null; } public void postEdit(UndoableEdit edit) { if (patch != null) patch.postEdit(edit); } public PUndoableEditFactory getUndoableEditFactory() { return patch.getUndoableEditFactory(); } public boolean isUndoableEditSupportEnabled() { return patch != null && patch.isUndoableEditSupportEnabled(); } protected PConnectionManager createConnectionManager() { return new PBasicConnectionManager(this); } public void addModuleContainerListener(PModuleContainerListener l) { listenerList.add(PModuleContainerListener.class, l); } public void removeModuleContainerListener(PModuleContainerListener l) { listenerList.remove(PModuleContainerListener.class, l); } protected void fireModuleAdded(PModule module) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); if (mcEvent != null) mcEvent.moduleAdded(module, module.getComponentIndex()); // 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]==PModuleContainerListener.class) { // Lazily create the event: if (mcEvent == null) { mcEvent = new PModuleContainerEvent(this); mcEvent.moduleAdded(module, module.getComponentIndex()); } ((PModuleContainerListener)listeners[i+1]).moduleAdded(mcEvent); } } } protected void registerModule(PModule module) { // no op } protected void unregisterModule(PModule module) { // no op } protected void fireModuleRemoved(PModule module, int oldIndex) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); if (mcEvent != null) mcEvent.moduleRemoved(module, oldIndex); // 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]==PModuleContainerListener.class) { // Lazily create the event: if (mcEvent == null) { mcEvent = new PModuleContainerEvent(this); mcEvent.moduleRemoved(module, oldIndex); } ((PModuleContainerListener)listeners[i+1]).moduleRemoved(mcEvent); } } } protected void moduleRemoved(PModule module, int index) { if (isUndoableEditSupportEnabled()) { PUndoableEditFactory factory = getUndoableEditFactory(); if (factory != null) { UndoableEdit edit = factory.createRemoveEdit(this, module, index); if (edit != null) postEdit(edit); } } } protected void moduleAdded(PModule module, int index) { if (isUndoableEditSupportEnabled()) { PUndoableEditFactory factory = getUndoableEditFactory(); if (factory != null) { UndoableEdit edit = factory.createAddEdit(this, module, index); if (edit != null) postEdit(edit); } } } public boolean add(PModule module) { return add(modules.generateIndex(), module); } public boolean add(int index, PModule module) { if (!canAdd(index, module)) return false; configure(module, index); modules.put(index, module); registerModule(module); moduleAdded(module, index); fireModuleAdded(module); /* MoveOperation move = createMoveOperation(); if (move != null) { move.setScreenOffset(0, 0); move.add(module); move.move(); }*/ return true; } protected void configure(PModule module, int index) { if (module instanceof PBasicModule) { ((PBasicModule) module).setComponentIndex(index); ((PBasicModule) module).setParent(this); } else throw new IllegalArgumentException("incompatible module: "+module); } protected void revertConfigure(PModule module) { if (module instanceof PBasicModule) { ((PBasicModule) module).setComponentIndex(-1); ((PBasicModule) module).setParent(null); } else throw new IllegalArgumentException("incompatible module: "+module); } protected boolean canAdd(int index, PModule module) { if (modules.get(index)!=null) return false; return canAdd(module.getDescriptor()); } public PConnectionManager getConnectionManager() { return connectionManager; } public PModule getModule(int index) { return modules.get(index); } public int getModuleCount() { return modules.size(); } public Collection<? extends PModule> getModules() { ArrayList<PModule> result = new ArrayList<PModule>(); for (PModule m : modules) if (m != null) result.add(m); return result; } public PPatch getPatch() { return patch; } public boolean contains(PModule module) { return indexOf(module)>=0; } public int indexOf(PModule module) { int index = module.getComponentIndex(); if (index>=0 && modules.get(index) == module) { return index; } index = modules.indexOf(module); if (module instanceof PBasicModule) ((PBasicModule) module).setComponentIndex(index); return index; } public boolean remove(PModule module) { int index = indexOf(module); if (index>=0) { module.removeAllConnections(); modules.remove(index); unregisterModule(module); moduleRemoved(module, index); fireModuleRemoved(module, index); revertConfigure(module); return true; } else { return false; } } public Iterator<PModule> iterator() { return modules.iterator(); } public PModule createModule(PModuleDescriptor d) { return getPatch().createModule(d); } public PModuleMetrics getModuleMetrics() { if (moduleMetrics == null) { PPatch p = getPatch(); if (p != null) moduleMetrics = p.getModuleMetrics(); } return moduleMetrics; } public MoveOperation createMoveOperation() { return null; } public CopyOperation createCopyOperation() { return null; } public PPatch createPatchWithModules(Collection<? extends PModule> modules) { PPatch patch = getPatch(); int mcIdx = patch.getModuleContainerIndex(this); PPatch newPatch = patch.createEmptyPatch(); PModuleContainer dstMc = newPatch.getModuleContainer(mcIdx); CopyOperation copy = createCopyOperation(); copy.setDestination(dstMc); for (PModule module: modules) { copy.add(module); } ModulesBoundingBox bbox = new ModulesBoundingBox(modules, new Point(0, 0)); Rectangle r = bbox.getBoundingBox(); copy.setScreenOffset(-r.x, -r.y); copy.copy(); return newPatch; } public boolean canAdd(PModuleDescriptor descriptor) { int limit = descriptor.getLimit(); if (limit>=0) { for (PModule m: this) { if (m.getDescriptor().equals(descriptor)) if (--limit<=0) break; } return limit>0; } return true; } public Collection<? extends PModule> getModulesWithDescriptor(PModuleDescriptor descriptor) { ArrayList<PModule> result = new ArrayList<PModule>(); for (PModule m : this) { if (m.getDescriptor().equals(descriptor)) result.add(m); } return result; } }