Serializing now serializes everything; among many other things:
[Mograsim.git] / net.mograsim.logic.model.editor / src / net / mograsim / logic / model / editor / handles / HandleManager.java
1 package net.mograsim.logic.model.editor.handles;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.concurrent.atomic.AtomicInteger;
12 import java.util.function.Consumer;
13 import java.util.stream.Collectors;
14
15 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
16 import net.mograsim.logic.model.editor.Editor;
17 import net.mograsim.logic.model.editor.states.EditorState;
18 import net.mograsim.logic.model.model.ViewModelModifiable;
19 import net.mograsim.logic.model.model.components.GUIComponent;
20 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
21 import net.mograsim.logic.model.model.wires.GUIWire;
22 import net.mograsim.logic.model.model.wires.MovablePin;
23 import net.mograsim.logic.model.model.wires.Pin;
24
25 public class HandleManager
26 {
27         private final Map<Pin, StaticPinHandle> handlePerPin;
28         private final Map<Pin, InterfacePinHandle> handlePerInterfacePin;
29         private final Map<GUIWire, List<WirePointHandle>> pointHandlesPerWire;
30         private final Map<GUIWire, WireHandle> handlePerWire;
31         private final Set<Handle> handles;
32         private final Set<WirePointHandle> wirePointHandles;
33         private final Map<GUIComponent, ComponentHandle> handlePerComp;
34
35         private final Collection<Consumer<Handle>> handleAddedListeners;
36         private final Collection<Consumer<Handle>> handleRemovedListeners;
37         private final Editor editor;
38         private boolean initialized = false;
39
40         private CornerHandle cornerHandle;
41
42         public HandleManager(Editor editor)
43         {
44                 this.editor = editor;
45                 handlePerPin = new HashMap<>();
46                 handlePerInterfacePin = new HashMap<>();
47                 pointHandlesPerWire = new HashMap<>();
48                 handlePerComp = new HashMap<>();
49                 handles = new HashSet<>();
50                 wirePointHandles = new HashSet<>();
51                 handlePerWire = new HashMap<>();
52
53                 handleAddedListeners = new ArrayList<>();
54                 handleRemovedListeners = new ArrayList<>();
55
56                 ViewModelModifiable model = editor.getSubmodel();
57
58                 model.addComponentAddedListener(c -> registerComponent(c));
59
60                 model.addComponentRemovedListener(c ->
61                 {
62                         removeComponentHandle(c);
63                 });
64
65                 model.addWireAddedListener(w ->
66                 {
67                         registerWire(w);
68                 });
69
70                 model.addWireRemovedListener(w ->
71                 {
72                         removeWireHandle(w);
73                         removeWirePointHandles(w);
74                 });
75         }
76
77         ////////////////////////////////////////
78         // -- Setting up initial handles -- ///
79         //////////////////////////////////////
80
81         public void init()
82         {
83                 if (initialized)
84                         System.err.println("Warning! HandleManager was already initialized.");
85                 else
86                 {
87                         ViewModelModifiable model = editor.getSubmodel();
88                         Map<String, GUIComponent> compsByName = model.getComponentsByName();
89                         Set<GUIComponent> comps = new HashSet<>(compsByName.values());
90                         GUIComponent interfaceComp = compsByName.get(SubmodelComponent.SUBMODEL_INTERFACE_NAME);
91                         comps.remove(interfaceComp);
92                         registerInterfaceComponent(interfaceComp);
93                         comps.forEach(c -> registerComponent(c));
94
95                         model.getWiresByName().values().forEach(w -> registerWire(w));
96                         addHandle(cornerHandle = new CornerHandle(editor.toBeEdited));
97                 }
98         }
99
100         private void registerInterfaceComponent(GUIComponent c)
101         {
102                 c.getPins().values().forEach(p -> addInterfacePinHandle(p));
103                 c.addPinAddedListener(p -> addInterfacePinHandle(p));
104                 c.addPinRemovedListener(p -> removeInterfacePinHandle(p));
105         }
106
107         private void registerComponent(GUIComponent c)
108         {
109                 addComponentHandle(c);
110
111                 c.getPins().values().forEach(p -> addPinHandle(p));
112
113                 c.addPinAddedListener(p -> addPinHandle(p));
114                 c.addPinRemovedListener(p -> removePinHandle(p));
115         }
116
117         private void registerWire(GUIWire wire)
118         {
119                 Point[] path = wire.getPath();
120                 AtomicInteger oldLength = new AtomicInteger(path == null ? 0 : path.length);
121                 wire.addPathChangedListener(w ->
122                 {
123                         Point[] newPath = w.getPath();
124                         int newLength = newPath == null ? 0 : newPath.length;
125                         int diff = oldLength.getAndSet(newLength) - newLength;
126                         if (diff != 0)
127                         {
128                                 if (diff > 0)
129                                 {
130                                         for (int i = 0; i < diff; i++)
131                                                 addWirePointHandle(w);
132                                 }
133
134                                 List<WirePointHandle> wpHandles = pointHandlesPerWire.get(w);
135                                 int size = wpHandles.size();
136                                 for (int i = 0; i < size; i++)
137                                 {
138                                         wpHandles.get(i).setIndex(i);
139                                 }
140                         }
141                         pointHandlesPerWire.get(w).forEach(h -> h.updatePos());
142                 });
143                 addWireHandle(wire);
144                 if (path == null)
145                         return;
146                 for (int i = 0; i < path.length; i++)
147                 {
148                         addWirePointHandle(wire);
149                 }
150         }
151
152         /////////////////////////////////////
153         // -- Adding/Removing handles -- ///
154         ///////////////////////////////////
155
156         private void addComponentHandle(GUIComponent c)
157         {
158                 ComponentHandle h = new ComponentHandle(c);
159                 handlePerComp.put(c, h);
160                 addHandle(h);
161         }
162
163         private void removeComponentHandle(GUIComponent c)
164         {
165                 ComponentHandle h = handlePerComp.get(c);
166                 handlePerComp.remove(c);
167                 removeHandle(h);
168         }
169
170         private void addPinHandle(Pin owner)
171         {
172                 StaticPinHandle h = new StaticPinHandle(owner);
173                 handlePerPin.put(owner, h);
174                 addHandle(h);
175         }
176
177         private void removePinHandle(Pin owner)
178         {
179                 StaticPinHandle h = handlePerPin.get(owner);
180                 handlePerPin.remove(owner);
181                 removeHandle(h);
182         }
183
184         private void addInterfacePinHandle(Pin p)
185         {
186                 // The following is not an alternative to the cast, because the new pin is not yet in the map, when the listener is called
187                 // editor.toBeEdited.getSubmodelMovablePins().get(p.name);
188                 MovablePin pM = (MovablePin) p;
189                 InterfacePinHandle h = new InterfacePinHandle(pM, editor.toBeEdited);
190                 handlePerInterfacePin.put(pM, h);
191                 addHandle(h);
192         }
193
194         private void removeInterfacePinHandle(Pin p)
195         {
196                 InterfacePinHandle h = handlePerInterfacePin.get(p);
197                 handlePerInterfacePin.remove(p);
198                 removeHandle(h);
199         }
200
201         private void addWirePointHandle(GUIWire w)
202         {
203                 List<WirePointHandle> wireHandles = pointHandlesPerWire.get(w);
204                 WirePointHandle h;
205                 if (wireHandles != null)
206                         wireHandles.add(h = new WirePointHandle(this, w, wireHandles.size()));
207                 else
208                 {
209                         wireHandles = new ArrayList<>();
210                         h = new WirePointHandle(this, w, 0);
211                         wireHandles.add(h);
212                         pointHandlesPerWire.put(h.parent, wireHandles);
213                 }
214                 this.wirePointHandles.add(h);
215                 addHandle(h);
216         }
217
218         void destroyWirePointHandle(GUIWire owner, WirePointHandle h)
219         {
220                 List<WirePointHandle> handles = pointHandlesPerWire.get(owner);
221                 int pointIndex = handles.indexOf(h);
222                 handles.remove(pointIndex);
223                 removeHandle(h);
224                 owner.removePathPoint(pointIndex);
225         }
226
227         private void removeWirePointHandles(GUIWire owner)
228         {
229                 if (!pointHandlesPerWire.containsKey(owner))
230                         return;
231                 pointHandlesPerWire.get(owner).forEach(h ->
232                 {
233                         wirePointHandles.remove(h);
234                         removeHandle(h);
235                 });
236                 pointHandlesPerWire.remove(owner);
237         }
238
239         private void addWireHandle(GUIWire w)
240         {
241                 WireHandle h = new WireHandle(w);
242                 handlePerWire.put(w, h);
243                 addHandle(h);
244         }
245
246         private void removeWireHandle(GUIWire w)
247         {
248                 WireHandle h = handlePerWire.get(w);
249                 handlePerWire.remove(w);
250                 removeHandle(h);
251         }
252
253         private void addHandle(Handle h)
254         {
255                 handles.add(h);
256                 callHandleAddedListeners(h);
257         }
258
259         private void removeHandle(Handle h)
260         {
261                 handles.remove(h);
262                 callHandleRemovedListeners(h);
263                 h.destroy();
264         }
265
266         public StaticPinHandle getHandle(Pin parent)
267         {
268                 return handlePerPin.get(parent);
269         }
270
271         public ComponentHandle getHandle(GUIComponent parent)
272         {
273                 return handlePerComp.get(parent);
274         }
275
276         public WireHandle getHandle(GUIWire parent)
277         {
278                 return handlePerWire.get(parent);
279         }
280
281         public Handle getInterfacePinHandle(Pin p)
282         {
283                 return handlePerInterfacePin.get(p);
284         }
285
286         /**
287          * @return A Collection of the registered {@link WirePointHandle}s of the specified wire
288          */
289         public Collection<WirePointHandle> getWirePointHandles(GUIWire parent)
290         {
291                 return pointHandlesPerWire.get(parent).stream().collect(Collectors.toSet());
292         }
293
294         /**
295          * @return An unmodifiable view of all registered {@link Handle}s
296          */
297         public Collection<Handle> getHandles()
298         {
299                 return Collections.unmodifiableCollection(handles);
300         }
301
302         /**
303          * @return An unmodifiable view of all registered {@link StaticPinHandle}s
304          */
305         public Collection<StaticPinHandle> getPinHandles()
306         {
307                 return Collections.unmodifiableCollection(handlePerPin.values());
308         }
309
310         /**
311          * @return An unmodifiable view of all registered {@link InterfacePinHandle}s
312          */
313         public Collection<InterfacePinHandle> getInterfacePinHandles()
314         {
315                 return Collections.unmodifiableCollection(handlePerInterfacePin.values());
316         }
317
318         /**
319          * @return An unmodifiable view of all registered {@link ComponentHandle}s
320          */
321         public Collection<ComponentHandle> getComponentHandles()
322         {
323                 return Collections.unmodifiableCollection(handlePerComp.values());
324         }
325
326         /**
327          * @return An unmodifiable view of all registered {@link WireHandle}s
328          */
329         public Collection<WireHandle> getWireHandles()
330         {
331                 return Collections.unmodifiableCollection(handlePerWire.values());
332         }
333
334         /**
335          * @return An unmodifiable view of all registered {@link WirePointHandle}s
336          */
337         public Collection<WirePointHandle> getWirePointHandles()
338         {
339                 return Collections.unmodifiableSet(wirePointHandles);
340         }
341
342         public void click(Point clicked, int stateMask)
343         {
344                 EditorState entryState = editor.stateManager.getState();
345
346                 // TODO: As soon as wires connected to a component being removed also are removed, change priority
347                 if (!cornerHandle.click(clicked.x, clicked.y, stateMask, entryState))
348                         if (!click(handlePerPin.values(), clicked, entryState, stateMask))
349                                 if (!click(handlePerInterfacePin.values(), clicked, entryState, stateMask))
350                                         if (!click(getWirePointHandles(), clicked, entryState, stateMask))
351                                                 if (!click(getWireHandles(), clicked, entryState, stateMask))
352                                                         if (!click(handlePerComp.values(), clicked, entryState, stateMask))
353                                                                 entryState.clickedEmpty(clicked, stateMask);
354                 entryState.clicked(clicked, stateMask);
355         }
356
357         private static boolean click(Collection<? extends Handle> handles, Point clicked, EditorState state, int stateMask)
358         {
359                 for (Handle h : handles)
360                         if (h.click(clicked.x, clicked.y, stateMask, state))
361                                 return true;
362                 return false;
363         }
364
365         public void addHandleAddedListener(Consumer<Handle> c)
366         {
367                 handleAddedListeners.add(c);
368         }
369
370         private void callHandleAddedListeners(Handle added)
371         {
372                 handleAddedListeners.forEach(l -> l.accept(added));
373         }
374
375         public void removeHandleAddedListener(Consumer<Handle> c)
376         {
377                 handleAddedListeners.remove(c);
378         }
379
380         public void addHandleRemovedListener(Consumer<Handle> c)
381         {
382                 handleRemovedListeners.add(c);
383         }
384
385         private void callHandleRemovedListeners(Handle removed)
386         {
387                 handleRemovedListeners.forEach(l -> l.accept(removed));
388         }
389
390         public void removeHandleRemovedListener(Consumer<Handle> c)
391         {
392                 handleRemovedListeners.remove(c);
393         }
394 }