package holeg.ui.controller; import java.awt.Point; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.swing.JFrame; import org.apache.commons.compress.archivers.ArchiveException; import com.google.gson.JsonParseException; import holeg.model.AbstractCanvasObject; import holeg.model.Edge; import holeg.model.GroupNode; import holeg.model.HolonElement; import holeg.model.HolonObject; import holeg.model.Node; import holeg.ui.model.GuiSettings; import holeg.ui.model.Model; import holeg.ui.view.dialog.CreateTemplatePopUp; import holeg.ui.view.main.Category; import holeg.ui.view.main.GUI; import holeg.utility.events.Action; import holeg.utility.events.Event; /** * The Class represents the controller in the model, controller view Pattern. * * @author Gruppe14 */ public class Control { private static final Logger log = Logger.getLogger(Control.class.getName()); private final CategoryController categoryController; private final CanvasController canvasController; private final SaveController saveController; private final LoadController loadController; private final AutoSaveController autoSaveController; private final NodeController nodeController; private final ClipboardController clipboardController; private Model model; private SimulationManager simulationManager; private String autosaveDir = ""; private String categoryDir = ""; private String otherDir = ""; private String dimensionsFileName = "dimensions"; private int rand; public Event OnCategoryChanged = new Event(); public Event OnSelectionChanged = new Event(); public Event OnCanvasUpdate = new Event(); public Action OnGuiSetEnabled = new Action<>(); /** * Constructor. * * @param model the Model */ public Control(Model model) { this.model = model; this.categoryController = new CategoryController(this); this.canvasController = new CanvasController(model); this.saveController = new SaveController(model); this.nodeController = new NodeController(model, canvasController); this.loadController = new LoadController(model, categoryController, canvasController, nodeController); this.simulationManager = new SimulationManager(model); this.autoSaveController = new AutoSaveController(); this.clipboardController = new ClipboardController(model, saveController, loadController, canvasController, nodeController, this.simulationManager); autosaveDir = System.getProperty("user.home") + "/.config/HolonGUI/Autosave/"; categoryDir = System.getProperty("user.home") + "/.config/HolonGUI/Category/"; otherDir = System.getProperty("user.home") + "/.config/HolonGUI/Other/"; File autoSave = new File(autosaveDir); File category = new File(categoryDir); File other = new File(otherDir); // deleteDirectory(dest); autoSave.mkdirs(); category.mkdirs(); other.mkdirs(); createAutoRandom(); tryAutoSave(); } /** * Generate random number, so that every instance of the program has unique save * files */ private void createAutoRandom() { rand = (int) (Math.random() * 1000); while (new File(autosaveDir + rand + (GuiSettings.autoSaveNr)).exists()) { rand = (int) Math.random() * 1000; } } /** * Delete a Directory. * * @param path to delete */ public void deleteDirectory(File path) { if (path.exists()) { File[] files = path.listFiles(); for (File file : files) { if (file.isDirectory()) { deleteDirectory(file); } else { if (file.getName().contains("" + rand)) file.delete(); } } // path.delete(); } } public Optional findCategoryWithName(String name){ return GuiSettings.getCategories().stream().filter(cat -> cat.getName().equals(name)).findAny(); } /* Operations for Categories and Objects */ /** * init default category and objects. */ public void resetCategories() { categoryController.clearCategories(); categoryController.initCategories(); saveCategory(); } /** * Adds New Category into Model. * * @param cat name of the new Category * @throws IOException */ public void createCategoryWithName(String cat) { categoryController.createCategoryWithName(cat); saveCategory(); } /** * Gives all Category as String * * @return a array of strings from all Categorys */ public String[] getCategoriesStrings() { return GuiSettings.getCategories().stream().map(c -> c.getName()).collect(Collectors.toList()) .toArray(new String[GuiSettings.getCategories().size()]); } /** * Add new Holon Object to a Category. * * @param cat Category * @param obj New Object Name * @param list Array of Elements * @param img the image Path * @throws IOException */ public void addObject(Category cat, String obj, List list, String img){ categoryController.addNewHolonObject(cat, obj, list, img); saveCategory(); } /** * Add new Holon Switch to a Category. * * @param cat Category * @param obj New Object Name * @throws IOException */ public void addSwitch(Category cat, String obj) { categoryController.addNewHolonSwitch(cat, obj); saveCategory(); } /** * delete a given Category. * * @param cat the Category * @throws IOException */ public void deleteCategory(Category category) { categoryController.removeCategory(category); saveCategory(); } /** * removes a selectedObject from selection. * * @param obj Cpsobject */ public void removeObjectsFromSelection(Collection objects) { for (AbstractCanvasObject obj : objects) { GuiSettings.getSelectedObjects().remove(obj); } OnSelectionChanged.broadcast(); } /** * removes a selectedObject from selection. * * @param obj Cpsobject */ public void removeObjectFromSelection(AbstractCanvasObject obj) { GuiSettings.getSelectedObjects().remove(obj); OnSelectionChanged.broadcast(); } /** * add an Object to selectedObject. * * @param obj AbstractCpsobject */ public void addSelectedObject(AbstractCanvasObject obj) { GuiSettings.getSelectedObjects().add(obj); OnSelectionChanged.broadcast(); } public void addSelectedObjects(Collection objects) { GuiSettings.getSelectedObjects().addAll(objects); OnSelectionChanged.broadcast(); } public void clearSelection() { if (!GuiSettings.getSelectedObjects().isEmpty()) { GuiSettings.getSelectedObjects().clear(); OnSelectionChanged.broadcast(); } } /** * This method is primarily for the multi-selection. It adds unselected objects * to the selection and Removes selected objects from the selection. Like the * normal OS Desktop selection. * * @param objects */ public void toggleSelectedObjects(Collection objects) { Set intersection = new HashSet<>(objects); intersection.retainAll(GuiSettings.getSelectedObjects()); GuiSettings.getSelectedObjects().addAll(objects); GuiSettings.getSelectedObjects().removeAll(intersection); OnSelectionChanged.broadcast(); } /* Operations for Canvas */ /** * Add a new Object. * * @param object the Object */ public void addObjectCanvas(AbstractCanvasObject object) { canvasController.addNewObject(object); calculateStateAndVisualForTimeStep(model.getCurrentIteration()); if (!(object instanceof Node)) { tryAutoSave(); } } /** * Deletes an CpsObject on the Canvas and its connections. * * @param obj AbstractCpsObject * @param save */ public void delCanvasObject(AbstractCanvasObject obj, boolean save) { canvasController.deleteObjectOnCanvas(obj); if (obj instanceof GroupNode groupnode) { canvasController.deleteAllObjectsInGroupNode(groupnode); } calculateStateAndVisualForCurrentTimeStep(); if (save) { tryAutoSave(); } } public void delCanvasObjects(Collection objects) { canvasController.deleteObjectsOnCanvas(objects); calculateStateAndVisualForCurrentTimeStep(); tryAutoSave(); } /** * 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.replaceObjectOnCanvas(toBeReplaced, by); tryAutoSave(); } /** * Add an edge to the Canvas. * * @param edge the edge */ public void addEdgeOnCanvas(Edge edge) { canvasController.addEdgeOnCanvas(edge); tryAutoSave(); } /** * Removes an Edge from the Canvas. * * @param edge the edge to remove */ public void removeEdgesOnCanvas(Edge edge) { canvasController.removeEdgesOnCanvas(edge); tryAutoSave(); } /** * Writes the current State of the Modelling into a JSON File which can be * loaded. * * @param path the Path * @throws IOException exception */ public void saveFile(String path) throws IOException, ArchiveException { saveController.writeSave(path); } /** * Reads the the Save File and load the state into the Model. * * @param path the Path * @throws IOException exception * @throws ArchiveException */ public void loadFile(String path) throws IOException, ArchiveException { loadController.readSave(path); saveCategory(); autoSave(); } /** * Reads the Json File from Autosave * * @param path * @throws IOException */ public void loadAutoSave(String path) throws IOException { loadController.readJson(path); } public ArrayList loadSavedWindowDimensionsIfExistent() { try { return loadController.readWindowDimensions(otherDir + dimensionsFileName); } catch (Exception e) { return new ArrayList<>(); } } /** * calculates the flow of the edges and the supply for objects for the current * Timestep. */ public void calculateStateAndVisualForCurrentTimeStep() { calculateStateAndVisualForTimeStep(model.getCurrentIteration()); } public void calculateStateOnlyForCurrentTimeStep() { simulationManager.calculateStateForTimeStep(model.getCurrentIteration(), false); } /** * calculates the flow of the edges and the supply for objects. * * @param x current Iteration */ public void calculateStateAndVisualForTimeStep(int x) { simulationManager.calculateStateForTimeStep(x, true); OnCanvasUpdate.broadcast(); // TODO(Tom2021-12-2): Convert to Events this.updateCanvas(); } /** * resets the whole State of the simulation including a reset of all Edges to * the default "is working" state */ public void resetSimulation() { model.reset(); } /** * make an autosave. * * @throws IOException Exception */ private void autoSave() throws IOException { autoSaveController.increaseAutoSaveNr(); saveController.writeAutosave(autosaveDir + rand + GuiSettings.autoSaveNr); if (autoSaveController.allowed()) { new File(autosaveDir + rand + (GuiSettings.autoSaveNr - GuiSettings.numberOfSaves)).delete(); } } public void tryAutoSave() { try { autoSave(); } catch (IOException e) { log.warning(e.getStackTrace().toString()); } } /** * find all old auto save files (with a file-name, that does not contain the * current rand) * * @return a list of files, that are not from the current run */ public ArrayList filterOldAutoSaveFiles() { File[] files = new File(autosaveDir).listFiles(); ArrayList oldAutoSaves = new ArrayList<>(); for (File file : files) { if (!file.getName().contains(String.valueOf(rand))) oldAutoSaves.add(file); } return oldAutoSaves; } /** * deletes the old autosave files */ public void deleteObsoleteAutoSaveFiles() { for (File file : filterOldAutoSaveFiles()) { file.delete(); } } public void saveCategory() { try { saveController.writeCategory(categoryDir + "Category.json"); } catch (IOException e) { } } public void savePosAndSizeOfWindow(int x, int y, int width, int height) throws IOException, ArchiveException { saveController.writeWindowStatus(otherDir + dimensionsFileName, x, y, width, height); } /** * Returns the undo save. * * @return the undo save */ public String getUndoSave() { autoSaveController.decreaseAutoSaveNr(); if (!new File(autosaveDir + rand + (GuiSettings.autoSaveNr)).exists()) { autoSaveController.increaseAutoSaveNr(); } return autosaveDir + rand + (GuiSettings.autoSaveNr); } /** * Returns the redo save. * * @return the redo save */ public String getRedoSave() { autoSaveController.increaseAutoSaveNr(); if (!new File(autosaveDir + rand + (GuiSettings.autoSaveNr)).exists()) { autoSaveController.decreaseAutoSaveNr(); // if it still does not exist, try old autosaves if (!new File(autosaveDir + rand + (GuiSettings.autoSaveNr)).exists()) { ArrayList oldAutoSaves = filterOldAutoSaveFiles(); if (oldAutoSaves.size() > 0) { return autosaveDir + oldAutoSaves.get(oldAutoSaves.size() - 1).getName(); } } } return autosaveDir + rand + (GuiSettings.autoSaveNr); } /** * Getter for Model. * * @return the Model */ public Model getModel() { return model; } /** * get the Simulation Manager. * * @return the Simulation Manager */ public SimulationManager getSimManager() { return simulationManager; } // ========================== MANAGING TRACKED OBJECTS END ================ /** * Controlling GroupNodes */ public void addGroupNode(String nodeName, GroupNode groupNode, List toGroup) { nodeController.addGroupNode(nodeName, groupNode, toGroup); tryAutoSave(); } public void undoGroupNode(GroupNode node, GroupNode groupNode) { nodeController.undoGroupNode(node, groupNode); tryAutoSave(); } public void addObjectInGroupNode(AbstractCanvasObject object, GroupNode groupNode) { nodeController.addObjectInGroupNode(object, groupNode, true); tryAutoSave(); } public void deleteObjectInGroupNode(AbstractCanvasObject object, GroupNode groupNode) { nodeController.deleteObjectInGroupNode(object, groupNode); if (object instanceof GroupNode groupnode) { canvasController.deleteAllObjectsInGroupNode(groupnode); } tryAutoSave(); } /** * Replaces {@code toBePlaced} by {@code by} in {@code upperNode} * * @param toBeReplaced * @param by * @param upperNode */ public void replaceObjectInGroupNode(AbstractCanvasObject toBeReplaced, AbstractCanvasObject by, GroupNode upperNode) { nodeController.replaceObjectInUpperNode(toBeReplaced, by, upperNode); tryAutoSave(); } /** * Copy all Selected Objects. */ public void copy(GroupNode upperNode) { clipboardController.copy(upperNode); } public void paste(GroupNode upperNode, Point point) throws JsonParseException, UnsupportedFlavorException, IOException { clipboardController.paste(upperNode, point); OnSelectionChanged.broadcast(); tryAutoSave(); } public void cut(GroupNode upperNode) { clipboardController.cut(upperNode); OnSelectionChanged.broadcast(); tryAutoSave(); } /** * creates a new Template for the given cps Object * * @param cps Object, which should become a template * @param parentFrame */ public void createTemplate(HolonObject cps, JFrame parentFrame) { CreateTemplatePopUp t = new CreateTemplatePopUp(cps, model, parentFrame, this); t.setVisible(true); } public void getObjectsInDepth() { clipboardController.getObjectsInDepth(); } public void updateCanvas() { log.info("updateCanvas"); } public void guiSetEnabled(boolean state) { log.info("guiDisabled"); OnGuiSetEnabled.broadcast(state); } }