Merge branch 'development' of
[Mograsim.git] / net.mograsim.logic.model.editor / src / net / mograsim / logic / model / editor / Editor.java
1 package net.mograsim.logic.model.editor;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.Map;
6 import java.util.Optional;
7 import java.util.Set;
8
9 import com.google.gson.JsonElement;
10 import com.google.gson.JsonNull;
11 import com.google.gson.JsonParser;
12 import com.google.gson.JsonSyntaxException;
13
14 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
15 import net.mograsim.logic.model.am2900.Am2900Loader;
16 import net.mograsim.logic.model.editor.handles.ComponentHandle;
17 import net.mograsim.logic.model.editor.handles.Handle;
18 import net.mograsim.logic.model.editor.handles.HandleManager;
19 import net.mograsim.logic.model.editor.handles.PinHandle;
20 import net.mograsim.logic.model.editor.states.StateManager;
21 import net.mograsim.logic.model.editor.ui.DialogManager;
22 import net.mograsim.logic.model.editor.ui.EditorGUI;
23 import net.mograsim.logic.model.model.LogicModelModifiable;
24 import net.mograsim.logic.model.model.components.ModelComponent;
25 import net.mograsim.logic.model.model.wires.ModelWire;
26 import net.mograsim.logic.model.serializing.DeserializedSubmodelComponent;
27 import net.mograsim.logic.model.serializing.IdentifyParams;
28 import net.mograsim.logic.model.serializing.IndirectModelComponentCreator;
29 import net.mograsim.logic.model.snippets.highlevelstatehandlers.DefaultHighLevelStateHandler;
30 import net.mograsim.logic.model.snippets.outlinerenderers.DefaultOutlineRenderer;
31 import net.mograsim.logic.model.snippets.symbolrenderers.DefaultSymbolRenderer;
32
33 public final class Editor
34 {
35         final Selection selection = new Selection();
36         final Set<ComponentInfo> copyBuffer = new HashSet<>();
37         public final DeserializedSubmodelComponent toBeEdited;
38         public final HandleManager handleManager;
39         final static Map<ModelComponent, String> identifierPerComponent = new HashMap<>();
40         public final EditorGUI gui;
41         public final StateManager stateManager;
42         private final SaveLoadManager saveManager;
43         private Snapping snapping = Snapping.ABSOLUTE;
44         private double snapX = 5, snapY = 5;
45         public final DialogManager dialogManager;
46         public final EditorUserInput userInput;
47
48         public Editor(DeserializedSubmodelComponent toBeEdited)
49         {
50                 this.toBeEdited = toBeEdited;
51                 handleManager = new HandleManager(this);
52                 gui = new EditorGUI(this);
53                 userInput = new EditorUserInput(this);
54                 stateManager = new StateManager(this);
55                 handleManager.init();
56                 saveManager = new SaveLoadManager(this);
57                 dialogManager = new DialogManager(gui.shell);
58
59                 toBeEdited.submodel.addComponentRemovedListener(c -> identifierPerComponent.remove(c));
60
61                 gui.open();
62         }
63
64         public LogicModelModifiable getSubmodel()
65         {
66                 return toBeEdited.getSubmodelModifiable();
67         }
68
69         public Selection getSelection()
70         {
71                 return selection;
72         }
73
74         // TODO: Remove this error prone method: Relative offset may change between multiple moves,
75         // because Handles have different ways of responding to reqMove(...), causing strange behaviour
76         @Deprecated
77         public void moveSelection(double x, double y)
78         {
79                 Point ref = selection.getTopLeft();
80                 Point snapped = new Point(x, y);
81                 applySnapping(snapped);
82
83                 for (Handle c : selection)
84                 {
85                         double newX, newY;
86                         newX = snapped.x + c.getPosX() - ref.x;
87                         newY = snapped.y + c.getPosY() - ref.y;
88                         c.reqMove(newX, newY);
89                 }
90         }
91
92         public void moveHandles(double x, double y, Map<Handle, Point> handleOffsetMap)
93         {
94                 Point snapped = new Point(x, y);
95                 applySnapping(snapped);
96
97                 for (Handle c : handleOffsetMap.keySet())
98                 {
99                         Point offset = handleOffsetMap.get(c);
100                         double newX, newY;
101                         newX = snapped.x + offset.x;
102                         newY = snapped.y + offset.y;
103                         c.reqMove(newX, newY);
104                 }
105         }
106
107         public void deleteSelection()
108         {
109                 selection.forEach(h -> h.reqDelete());
110                 selection.clear();
111         }
112
113         public void copy()
114         {
115                 copyBuffer.clear();
116                 Point refPoint = selection.getTopLeft();
117                 for (Handle h : selection)
118                 {
119                         Optional<ComponentInfo> cInfo = h.reqCopy(refPoint);
120                         if (cInfo.isPresent())
121                                 copyBuffer.add(cInfo.get());
122                 }
123         }
124
125         public void paste(double x, double y)
126         {
127                 selection.clear();
128                 for (ComponentInfo info : copyBuffer)
129                 {
130                         ModelComponent comp = addComponent(info.identifier, info.params);
131                         ComponentHandle h = handleManager.getHandle(comp);
132                         h.reqMove(info.relX, info.relY);
133                         selection.add(h);
134                 }
135                 moveSelection(x, y);
136         }
137
138         public void save()
139         {
140                 saveManager.save();
141         }
142
143         public void saveAs()
144         {
145                 saveManager.openSaveAsDialog();
146         }
147
148         public void addComponent(double x, double y)
149         {
150                 boolean successful = false;
151                 JsonElement params = JsonNull.INSTANCE;
152                 outer: while (!successful)
153                 {
154                         String selected = gui.getAddListSelected();
155                         try
156                         {
157                                 ModelComponent c = addComponent(selected, params);
158                                 selection.clear();
159                                 selection.add(handleManager.getHandle(c));
160                                 moveSelection(x, y);
161                                 successful = true;
162                         }
163                         catch (@SuppressWarnings("unused") UnsupportedOperationException | JsonSyntaxException | NumberFormatException
164                                         | NullPointerException e)
165                         {
166                                 String result = DialogManager.openMultiLineTextDialog("Add component", "Create", "Cancel", "Parameters:");
167                                 if (result == null)
168                                         break outer;
169                                 params = new JsonParser().parse(result);
170                         }
171                 }
172         }
173
174         private ModelComponent addComponent(String identifier, JsonElement params)
175         {
176                 ModelComponent comp = IndirectModelComponentCreator.createComponent(toBeEdited.getSubmodelModifiable(), identifier, params);
177                 identifierPerComponent.put(comp, identifier);
178                 return comp;
179         }
180
181         public static String getIdentifier(ModelComponent c)
182         {
183                 if (identifierPerComponent.containsKey(c))
184                         return identifierPerComponent.get(c);
185                 return c.getIDForSerializing(new IdentifyParams());
186         }
187
188         public void duplicate()
189         {
190                 copy();
191                 Point origin = selection.getTopLeft();
192                 paste(origin.x + 20, origin.y + 20);
193         }
194
195         private void applySnapping(Point newP)
196         {
197                 switch (snapping)
198                 {
199                 case OFF:
200                         break;
201                 case ABSOLUTE:
202                         newP.x = (int) (newP.x / snapX + .5) * snapX;
203                         newP.y = (int) (newP.y / snapY + .5) * snapY;
204                         break;
205                 default:
206                         break;
207                 }
208         }
209
210         public static class ComponentInfo
211         {
212                 public final double relX, relY;
213                 public final String identifier;
214                 public final JsonElement params;
215
216                 public ComponentInfo(double relX, double relY, String identifier, JsonElement params)
217                 {
218                         this.relX = relX;
219                         this.relY = relY;
220                         this.identifier = identifier;
221                         this.params = params;
222                 }
223         }
224
225         @SuppressWarnings("unused")
226         public void addWire(PinHandle a, PinHandle b)
227         {
228                 new ModelWire(toBeEdited.getSubmodelModifiable(), a.getPin(), b.getPin(), new Point[0]);
229         }
230
231         public static enum Snapping
232         {
233                 OFF, ABSOLUTE;
234
235                 @Override
236                 public String toString()
237                 {
238                         return super.toString().toLowerCase();
239                 }
240         }
241
242         public static void main(String[] args)
243         {
244                 Am2900Loader.setup();
245                 openNewEditor();
246 //              SaveLoadManager.openLoadDialog();
247         }
248
249         public static void openNewEditor()
250         {
251                 DeserializedSubmodelComponent toBeEdited = new DeserializedSubmodelComponent(new LogicModelModifiable(), null, null, null);
252                 toBeEdited.setOutlineRenderer(new DefaultOutlineRenderer(toBeEdited));
253                 toBeEdited.setSymbolRenderer(new DefaultSymbolRenderer(toBeEdited));
254                 toBeEdited.setHighLevelStateHandler(new DefaultHighLevelStateHandler());
255                 new Editor(toBeEdited);
256         }
257
258         public Snapping getSnapping()
259         {
260                 return snapping;
261         }
262
263         public void setSnapping(Snapping snapping)
264         {
265                 this.snapping = snapping;
266         }
267 }