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