/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution 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 3 of the License, or * (at your option) any later version. * * logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.file; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import javax.swing.JOptionPane; import com.cburch.logisim.circuit.Circuit; import com.cburch.logisim.circuit.SubcircuitFactory; import com.cburch.logisim.comp.Component; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeSet; import com.cburch.logisim.proj.Action; import com.cburch.logisim.proj.Project; import com.cburch.logisim.proj.ProjectActions; import com.cburch.logisim.tools.AddTool; import com.cburch.logisim.tools.Library; import com.cburch.logisim.tools.LibraryTools; import com.cburch.logisim.tools.Tool; public class LogisimFileActions { private static class AddCircuit extends Action { private Circuit circuit; AddCircuit(Circuit circuit) { this.circuit = circuit; } @Override public void doIt(Project proj) { proj.getLogisimFile().addCircuit(circuit); } @Override public String getName() { return Strings.get("addCircuitAction"); } @Override public void undo(Project proj) { proj.getLogisimFile().removeCircuit(circuit); } } private static class MergeFile extends Action { private ArrayList<Library> MergedLibraries = new ArrayList<Library>(); private ArrayList<Circuit> MergedCircuits = new ArrayList<Circuit>(); MergeFile(LogisimFile mergelib, LogisimFile source) { HashSet<String> LibNames = new HashSet<String>(); HashSet<String> ToolList = new HashSet<String>(); HashSet<Circuit> NotImportedList = new HashSet<Circuit>(); HashMap<String,String> Error = new HashMap<String,String>(); for (Library lib : source.getLibraries()) { LibraryTools.BuildLibraryList(lib,LibNames); } LibraryTools.BuildToolList(source,ToolList); LibraryTools.RemovePresentLibraries(mergelib,LibNames,false); if (LibraryTools.LibraryIsConform(mergelib,new HashSet<String> (),new HashSet<String>(),Error)) { /* Okay the library is now ready for merge */ for (Library lib : mergelib.getLibraries()) { MergedLibraries.add(lib); } /* Okay merged the missing libraries, now add the circuits */ for (Circuit circ : mergelib.getCircuits()) { if (ToolList.contains(circ.getName().toUpperCase())) { Error.put(circ.getName(), Strings.get("CircNotImportedWarning")); NotImportedList.add(circ); } else { MergedCircuits.add(circ); } } if (!Error.isEmpty()) LibraryTools.ShowWarnings(mergelib.getName(),Error); if (!NotImportedList.isEmpty()) { HashMap<String,Circuit> Replacements = new HashMap<String,Circuit>(); /* Some circuits have not been imported, we have to update the circuits */ /* first stage, make a map of circuits in the current set */ for (Circuit nicirc : NotImportedList) { for (Circuit curcirc : source.getCircuits()) { if (curcirc.getName().toUpperCase().equals(nicirc.getName().toUpperCase())) { Replacements.put(curcirc.getName().toUpperCase(), curcirc); } } } /* Second stage, iterate over all subcircuits and replace them when required */ for (Circuit importedCirc : MergedCircuits) { for (Component comp : importedCirc.getNonWires()) { if (comp instanceof SubcircuitFactory) { SubcircuitFactory fact = (SubcircuitFactory) comp; if (Replacements.containsKey(fact.getName().toUpperCase())) { fact.setSubcircuit(Replacements.get(fact.getName().toUpperCase())); } } } } } } else LibraryTools.ShowErrors(mergelib.getName(),Error); } @Override public void doIt(Project proj) { for (Library lib : MergedLibraries) proj.getLogisimFile().addLibrary(lib); for (Circuit circ : MergedCircuits) proj.getLogisimFile().addCircuit(circ); } @Override public String getName() { return Strings.get("mergeFileAction"); } @Override public boolean isModification() { return (MergedLibraries.size() > 0) || (MergedCircuits.size() > 0); } @Override public void undo(Project proj) { for (Library lib : MergedLibraries) proj.getLogisimFile().removeLibrary(lib); for (Circuit circ : MergedCircuits) proj.getLogisimFile().removeCircuit(circ); } } private static class LoadLibraries extends Action { private ArrayList<Library> MergedLibs = new ArrayList<Library>(); LoadLibraries(Library[] libs, LogisimFile source) { HashSet<String> LibNames = new HashSet<String>(); HashSet<String> ToolList = new HashSet<String>(); HashMap<String,String> Error = new HashMap<String,String>(); for (Library lib : source.getLibraries()) { LibraryTools.BuildLibraryList(lib,LibNames); } LibraryTools.BuildToolList(source,ToolList); for (int i = 0; i < libs.length; i++) { if (LibNames.contains(libs[i].getName().toUpperCase())) { JOptionPane.showMessageDialog(null, "\""+libs[i].getName()+"\": "+Strings.get("LibraryAlreadyLoaded"), Strings.get("LibLoadErrors")+" "+libs[i].getName()+" !", JOptionPane.WARNING_MESSAGE); } else { LibraryTools.RemovePresentLibraries(libs[i],LibNames,false); if (LibraryTools.LibraryIsConform(libs[i],new HashSet<String> (),new HashSet<String>(),Error)) { HashSet<String> AddedToolList = new HashSet<String>(); LibraryTools.BuildToolList(libs[i],AddedToolList); for (String tool : AddedToolList) if (ToolList.contains(tool)) Error.put(tool, Strings.get("LibraryMultipleToolError")); if (Error.keySet().isEmpty()) { LibraryTools.BuildLibraryList(libs[i],LibNames); ToolList.addAll(AddedToolList); MergedLibs.add(libs[i]); } else LibraryTools.ShowErrors(libs[i].getName(),Error); } else LibraryTools.ShowErrors(libs[i].getName(),Error); } } } @Override public void doIt(Project proj) { for (Library lib : MergedLibs) proj.getLogisimFile().addLibrary(lib); } @Override public boolean isModification() { return MergedLibs.size() > 0; } @Override public String getName() { if (MergedLibs.size() <= 1) { return Strings.get("loadLibraryAction"); } else { return Strings.get("loadLibrariesAction"); } } @Override public void undo(Project proj) { for (Library lib : MergedLibs) proj.getLogisimFile().removeLibrary(lib); } } private static class MoveCircuit extends Action { private AddTool tool; private int fromIndex; private int toIndex; MoveCircuit(AddTool tool, int toIndex) { this.tool = tool; this.toIndex = toIndex; } @Override public Action append(Action other) { MoveCircuit ret = new MoveCircuit(tool, ((MoveCircuit) other).toIndex); ret.fromIndex = this.fromIndex; return ret.fromIndex == ret.toIndex ? null : ret; } @Override public void doIt(Project proj) { fromIndex = proj.getLogisimFile().getTools().indexOf(tool); proj.getLogisimFile().moveCircuit(tool, toIndex); } @Override public String getName() { return Strings.get("moveCircuitAction"); } @Override public boolean shouldAppendTo(Action other) { return other instanceof MoveCircuit && ((MoveCircuit) other).tool == this.tool; } @Override public void undo(Project proj) { proj.getLogisimFile().moveCircuit(tool, fromIndex); } } private static class RemoveCircuit extends Action { private Circuit circuit; private int index; RemoveCircuit(Circuit circuit) { this.circuit = circuit; } @Override public void doIt(Project proj) { index = proj.getLogisimFile().getCircuits().indexOf(circuit); proj.getLogisimFile().removeCircuit(circuit); } @Override public String getName() { return Strings.get("removeCircuitAction"); } @Override public void undo(Project proj) { proj.getLogisimFile().addCircuit(circuit, index); } } private static class RevertAttributeValue { private AttributeSet attrs; private Attribute<Object> attr; private Object value; RevertAttributeValue(AttributeSet attrs, Attribute<Object> attr, Object value) { this.attrs = attrs; this.attr = attr; this.value = value; } } private static class RevertDefaults extends Action { private Options oldOpts; private ArrayList<Library> libraries = null; private ArrayList<RevertAttributeValue> attrValues; RevertDefaults() { libraries = null; attrValues = new ArrayList<RevertAttributeValue>(); } private void copyToolAttributes(Library srcLib, Library dstLib) { for (Tool srcTool : srcLib.getTools()) { AttributeSet srcAttrs = srcTool.getAttributeSet(); Tool dstTool = dstLib.getTool(srcTool.getName()); if (srcAttrs != null && dstTool != null) { AttributeSet dstAttrs = dstTool.getAttributeSet(); for (Attribute<?> attrBase : srcAttrs.getAttributes()) { @SuppressWarnings("unchecked") Attribute<Object> attr = (Attribute<Object>) attrBase; Object srcValue = srcAttrs.getValue(attr); Object dstValue = dstAttrs.getValue(attr); if (!dstValue.equals(srcValue)) { dstAttrs.setValue(attr, srcValue); attrValues.add(new RevertAttributeValue(dstAttrs, attr, dstValue)); } } } } } @Override public void doIt(Project proj) { LogisimFile src = ProjectActions.createNewFile(proj); LogisimFile dst = proj.getLogisimFile(); copyToolAttributes(src, dst); for (Library srcLib : src.getLibraries()) { Library dstLib = dst.getLibrary(srcLib.getName()); if (dstLib == null) { String desc = src.getLoader().getDescriptor(srcLib); dstLib = dst.getLoader().loadLibrary(desc); proj.getLogisimFile().addLibrary(dstLib); if (libraries == null) libraries = new ArrayList<Library>(); libraries.add(dstLib); } copyToolAttributes(srcLib, dstLib); } Options newOpts = proj.getOptions(); oldOpts = new Options(); oldOpts.copyFrom(newOpts, dst); newOpts.copyFrom(src.getOptions(), dst); } @Override public String getName() { return Strings.get("revertDefaultsAction"); } @Override public void undo(Project proj) { proj.getOptions().copyFrom(oldOpts, proj.getLogisimFile()); for (RevertAttributeValue attrValue : attrValues) { attrValue.attrs.setValue(attrValue.attr, attrValue.value); } if (libraries != null) { for (Library lib : libraries) { proj.getLogisimFile().removeLibrary(lib); } } } } private static class SetMainCircuit extends Action { private Circuit oldval; private Circuit newval; SetMainCircuit(Circuit circuit) { newval = circuit; } @Override public void doIt(Project proj) { oldval = proj.getLogisimFile().getMainCircuit(); proj.getLogisimFile().setMainCircuit(newval); } @Override public String getName() { return Strings.get("setMainCircuitAction"); } @Override public void undo(Project proj) { proj.getLogisimFile().setMainCircuit(oldval); } } private static class UnloadLibraries extends Action { private Library[] libs; UnloadLibraries(Library[] libs) { this.libs = libs; } @Override public void doIt(Project proj) { for (int i = libs.length - 1; i >= 0; i--) { proj.getLogisimFile().removeLibrary(libs[i]); } } @Override public String getName() { if (libs.length == 1) { return Strings.get("unloadLibraryAction"); } else { return Strings.get("unloadLibrariesAction"); } } @Override public void undo(Project proj) { for (int i = 0; i < libs.length; i++) { proj.getLogisimFile().addLibrary(libs[i]); } } } public static Action addCircuit(Circuit circuit) { return new AddCircuit(circuit); } public static Action MergeFile(LogisimFile mergelib, LogisimFile source) { return new MergeFile(mergelib,source); } public static Action loadLibraries(Library[] libs, LogisimFile source) { return new LoadLibraries(libs,source); } public static Action loadLibrary(Library lib, LogisimFile source) { return new LoadLibraries(new Library[] { lib }, source); } public static Action moveCircuit(AddTool tool, int toIndex) { return new MoveCircuit(tool, toIndex); } public static Action removeCircuit(Circuit circuit) { return new RemoveCircuit(circuit); } public static Action revertDefaults() { return new RevertDefaults(); } public static Action setMainCircuit(Circuit circuit) { return new SetMainCircuit(circuit); } public static Action unloadLibraries(Library[] libs) { return new UnloadLibraries(libs); } public static Action unloadLibrary(Library lib) { return new UnloadLibraries(new Library[] { lib }); } private LogisimFileActions() { } }