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