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