package holeg.ui.controller; import holeg.model.*; import holeg.preferences.ImagePreference; import holeg.preferences.PreferenceKeys; import holeg.serialize.CategoryAdapter; import holeg.ui.model.GuiSettings; import holeg.ui.model.IdCounter; import holeg.ui.model.UndoHistory; import holeg.ui.view.category.Category; import holeg.ui.view.dialog.CreateTemplatePopUp; import holeg.utility.events.Action; import holeg.utility.events.Event; import holeg.utility.math.vector.Vec2i; import javax.swing.*; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.*; import java.util.logging.Logger; import java.util.prefs.Preferences; import java.util.stream.Collectors; import static holeg.serialize.ModelDeserializer.gson; /** * The Class represents the controller. */ public class Control { private static final Logger log = Logger.getLogger(Control.class.getName()); private static final Preferences prefs = Preferences.userNodeForPackage(Control.class); private final CanvasController canvasController; private final SimulationManager simulationManager; public Event OnCategoryChanged = new Event(); public Event OnSelectionChanged = new Event(); public Event OnCanvasUpdate = new Event(); public Action OnGuiSetEnabled = new Action<>(); public Action OnShowGroupNode = new Action<>(); public Action OnRemoveGroupNode = new Action<>(); public Event OnModelChanged = new Event(); private Model model; public Control(Model model) { this.model = model; this.canvasController = new CanvasController(this); this.simulationManager = new SimulationManager(this); OnCanvasUpdate.addListener(() -> UndoHistory.addSave(saveModelToJsonString())); UndoHistory.addSave(saveModelToJsonString()); } /* Operations for Categories and Objects */ /** * init default category and objects. */ public void resetCategories() { GuiSettings.getCategories().clear(); initCategories(); saveCategories(); } /** * Gives all Category as String * * @return a array of strings from all Categorys */ public String[] getCategoriesStrings() { return GuiSettings.getCategories().stream().map(Category::getName).toArray(String[]::new); } /** * Add new Holon Object to a Category. */ public void addObject(Category cat, String obj, List list, String img) { addNewHolonObject(cat, obj, list, img); OnCategoryChanged.broadcast(); saveCategories(); } /** * Add new Holon Switch to a Category. * * @param cat Category * @param obj New Object Name */ public void addSwitch(Category cat, String obj) { addNewHolonSwitch(cat, obj); OnCategoryChanged.broadcast(); saveCategories(); } public void deleteCategory(Category category) { removeCategory(category); saveCategories(); } /** * removes a selectedObject from selection. */ public void removeObjectsFromSelection(Collection objects) { if (GuiSettings.getSelectedObjects().removeAll(objects)) { OnSelectionChanged.broadcast(); } } /** * removes a selectedObject from selection. * * @param obj Cpsobject */ public void removeObjectFromSelection(AbstractCanvasObject obj) { if (GuiSettings.getSelectedObjects().remove(obj)) { OnSelectionChanged.broadcast(); } } public void addObjectToSelection(AbstractCanvasObject obj) { if (GuiSettings.getSelectedObjects().add(obj)) { OnSelectionChanged.broadcast(); } } public void addObjectsToSelection(Collection objects) { if (GuiSettings.getSelectedObjects().addAll(objects)) { OnSelectionChanged.broadcast(); } } public void setSelection(Collection objects) { GuiSettings.getSelectedObjects().clear(); addObjectsToSelection(objects); } public void clearSelection() { if (!GuiSettings.getSelectedObjects().isEmpty()) { GuiSettings.getSelectedObjects().clear(); OnSelectionChanged.broadcast(); } } public void addObjectOnCanvas(GroupNode node, AbstractCanvasObject object) { canvasController.addObject(node, object); updateStateForIteration(model.getCurrentIteration()); } /** * Deletes an CpsObject on the Canvas and its connections. * * @param obj AbstractCpsObject */ public void deleteCanvasObject(AbstractCanvasObject obj) { canvasController.deleteObject(obj); if (obj instanceof GroupNode groupnode) { canvasController.deleteAllObjectsInGroupNode(groupnode); } calculateStateForCurrentIteration(); OnCanvasUpdate.broadcast(); } public void deleteCanvasObjects(Collection objects) { canvasController.deleteObjects(objects); calculateStateForCurrentIteration(); OnCanvasUpdate.broadcast(); } /** * Replaces {@code toBeReplaced} by {@code by} on the canvas * * @param toBeReplaced the object that will be replaced * @param by the object that will replace it */ public void replaceCanvasObject(AbstractCanvasObject toBeReplaced, AbstractCanvasObject by) { canvasController.replaceObject(toBeReplaced, by); OnCanvasUpdate.broadcast(); } /** * Add an edge to the Canvas. * * @param edge the edge */ public boolean addEdgeOnCanvas(Edge edge) { if (doesEdgeExist(edge)) { return false; } canvasController.addEdgeOnCanvas(edge); OnCanvasUpdate.broadcast(); return true; } private boolean doesEdgeExist(Edge edge){ boolean connectsToSelf = edge.getA() == edge.getB(); return !connectsToSelf && model.getEdgesOnCanvas().stream().anyMatch(e -> (e.getA() == edge.getA() && e.getB() == edge.getB()) || (e.getB() == edge.getA() && e.getA() == edge.getB())); } /** * Add an edge to the Canvas. * * @param edge the edge */ public void addEdgeOnCanvasOrRemoveExisting(Edge edge) { boolean connectsToSelf = edge.getA() == edge.getB(); if(connectsToSelf){ return; } Optional edgeToRemove = model.getEdgesOnCanvas().stream().filter(e -> (e.getA() == edge.getA() && e.getB() == edge.getB()) || (e.getB() == edge.getA() && e.getA() == edge.getB())).findAny(); edgeToRemove.ifPresentOrElse(canvasController::removeEdgesOnCanvas, () -> canvasController.addEdgeOnCanvas(edge)); updateStateForCurrentIteration(); } /** * Removes an Edge from the Canvas. * * @param edge the edge to remove */ public void removeEdgesOnCanvas(Edge edge) { canvasController.removeEdgesOnCanvas(edge); OnCanvasUpdate.broadcast(); } public void calculateStateForCurrentIteration() { simulationManager.calculateStateForIteration(model.getCurrentIteration()); } public void updateStateForCurrentIteration() { simulationManager.calculateStateForIteration(model.getCurrentIteration()); OnCanvasUpdate.broadcast(); } /** * calculates the flow of the edges and the supply for objects. * * @param x current Iteration */ public void updateStateForIteration(int x) { simulationManager.calculateStateForIteration(x); OnCanvasUpdate.broadcast(); } /** * resets the whole State of the simulation including a reset of all Edges to * the default "is working" state */ public void resetSimulation() { model.reset(); } /** * Getter for Model. * * @return the Model */ public Model getModel() { return model; } public void replaceObject(AbstractCanvasObject toBeReplaced, AbstractCanvasObject by) { canvasController.replaceObject(toBeReplaced, by); } /** * Copy all Selected Objects. */ public void copy() { GuiSettings.getClipboardObjects().clear(); GuiSettings.getClipboardObjects().addAll(GuiSettings.getSelectedObjects()); } public void paste(GroupNode groupNode, Vec2i offset) { if (GuiSettings.getClipboardObjects().isEmpty()) { return; } Set copies = GuiSettings.getClipboardObjects().stream().map(AbstractCanvasObject::copy).collect(Collectors.toSet()); copies.forEach(obj -> { obj.getPosition().addAssign(offset); obj.getPosition().clampX(0, GuiSettings.canvasSize.getX()); obj.getPosition().clampY(0, GuiSettings.canvasSize.getY()); }); groupNode.addAll(copies); updateStateForIteration(model.getCurrentIteration()); OnSelectionChanged.broadcast(); } public void cut() { copy(); deleteCanvasObjects(GuiSettings.getSelectedObjects()); clearSelection(); } public void createTemplate(HolonObject cps, JFrame parentFrame) { CreateTemplatePopUp t = new CreateTemplatePopUp(cps, model, parentFrame, this); t.setVisible(true); } public void guiSetEnabled(boolean state) { OnGuiSetEnabled.broadcast(state); } /** * init default category and objects. */ private void initCategories() { Category energy = createCategoryWithName("Energy"); Category building = createCategoryWithName("Building"); Category component = createCategoryWithName("Component"); HolonObject powerPlant = addNewHolonObject(energy, "Power Plant", new ArrayList<>(), ImagePreference.Canvas.DefaultObject.PowerPlant); HolonObject house = addNewHolonObject(building, "House", new ArrayList<>(), ImagePreference.Canvas.DefaultObject.House); addNewHolonSwitch(component, "Switch"); powerPlant.add(new HolonElement(null, "Power", 10000)); energy.getObjects().add(powerPlant); house.add(new HolonElement(null, "TV", -250)); house.add(new HolonElement(null, "TV", -250)); house.add(new HolonElement(null, "Fridge", -500)); house.add(new HolonElement(null, "Radio", -100)); house.add(new HolonElement(null, "PC", -250)); house.add(new HolonElement(null, "PC", -250)); house.add(new HolonElement(null, "PC", -250)); house.add(new HolonElement(null, "Light", -50)); house.add(new HolonElement(null, "Light", -50)); house.add(new HolonElement(null, "Light", -50)); house.add(new HolonElement(null, "Light", -50)); house.add(new HolonElement(null, "Light", -50)); house.add(new HolonElement(null, "Solar Panel", 300)); building.getObjects().add(house); OnCategoryChanged.broadcast(); } public Category createCategoryWithName(String categoryName) { Optional category = findCategoryWithName(categoryName); if (category.isEmpty()) { Category cat = new Category(categoryName); GuiSettings.getCategories().add(cat); OnCategoryChanged.broadcast(); return cat; } else { return category.get(); } } /** * remove a Category from Model. * * @param c Category */ public void removeCategory(Category c) { GuiSettings.getCategories().remove(c); OnCategoryChanged.broadcast(); } /** * Add Object into a Category. * * @param category Category * @param object Object */ public void addObject(Category category, AbstractCanvasObject object) { int i = 0; //TODO(Tom2021-12-1) remove/redo this search while (category.findObjectWithName(object.getName()).isPresent()) { if (object.getName().contains("_")) object.setName(object.getName().substring(0, object.getName().indexOf('_'))); object.setName(object.getName() + "_" + i); i++; } category.getObjects().add(object); } /** * Add new Holon Object to a Category. * * @param category Category * @param object New Object Name * @param list Array of Elements * @param image the image Path */ public HolonObject addNewHolonObject(Category category, String object, List list, String image) { HolonObject obj = new HolonObject(object); obj.setImagePath(image); obj.clearElements(); obj.add(list); addObject(category, obj); return obj; } public HolonSwitch addNewHolonSwitch(Category cat, String objName) { HolonSwitch holonSwitch = new HolonSwitch(objName); addObject(cat, holonSwitch); return holonSwitch; } public Optional findCategoryWithName(String categoryName) { return GuiSettings.getCategories().stream().filter(cat -> cat.getName().equals(categoryName)).findAny(); } public void loadFile(File file) { log.info("load" + file); try { FileReader reader = new FileReader(file); Model model = gson.fromJson(reader, Model.class); reader.close(); this.model = model; calculateStateForCurrentIteration(); OnModelChanged.broadcast(); } catch (IOException e) { log.warning(e.getLocalizedMessage()); } clearSelection(); GuiSettings.setActualSaveFile(file); } public void loadModelFromJsonString(String json){ this.model = gson.fromJson(json, Model.class); calculateStateForCurrentIteration(); OnModelChanged.broadcast(); } public String saveModelToJsonString(){ return gson.toJson(model); } public void toggleSelectedObjects(Collection objects) { Set intersection = new HashSet<>(objects); intersection.retainAll(GuiSettings.getSelectedObjects()); GuiSettings.getSelectedObjects().addAll(objects); GuiSettings.getSelectedObjects().removeAll(intersection); OnSelectionChanged.broadcast(); } public void saveFile(File file) { log.info("save" + file); try { FileWriter writer = new FileWriter(file); gson.toJson(model, writer); writer.close(); } catch (IOException e) { log.warning(e.getLocalizedMessage()); } GuiSettings.setActualSaveFile(file); } public void group() { canvasController.group(GuiSettings.getSelectedObjects()); clearSelection(); OnCanvasUpdate.broadcast(); } public void ungroup() { canvasController.ungroup(GuiSettings.getSelectedObjects()); clearSelection(); OnCanvasUpdate.broadcast(); } public void undo(){ UndoHistory.undo().ifPresent(this::loadModelFromJsonString); } public void redo(){ UndoHistory.redo().ifPresent(this::loadModelFromJsonString); } public void clearModel() { model.clear(); clearSelection(); model.setCurrentIteration(0); IdCounter.reset(); calculateStateForCurrentIteration(); OnModelChanged.broadcast(); GuiSettings.setActualSaveFile(null); } public void showGroupNode(GroupNode groupNode) { OnShowGroupNode.broadcast(groupNode); clearSelection(); } public void loadCategory(){ String json = prefs.get(PreferenceKeys.Category.DefaultCategory, "Init"); if(json.equals("Init")){ initCategories(); return; } GuiSettings.getCategories().clear(); GuiSettings.getCategories().addAll(CategoryAdapter.Gson.fromJson(json, CategoryAdapter.CategorySet)); OnCategoryChanged.broadcast(); } public void saveCategories(){ String json = CategoryAdapter.Gson.toJson(GuiSettings.getCategories()); prefs.put(PreferenceKeys.Category.DefaultCategory, json); } }