Merge remote-tracking branch 'origin/development' into development
[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 = newLength - oldLength.getAndSet(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                 if (pointHandlesPerWire.containsKey(owner))
221                 {
222                         List<WirePointHandle> handles = pointHandlesPerWire.get(owner);
223                         int pointIndex = handles.indexOf(h);
224                         handles.remove(pointIndex);
225                         removeHandle(h);
226                         owner.removePathPoint(pointIndex);
227                 }
228         }
229
230         private void removeWirePointHandles(GUIWire owner)
231         {
232                 if (!pointHandlesPerWire.containsKey(owner))
233                         return;
234                 pointHandlesPerWire.get(owner).forEach(h ->
235                 {
236                         wirePointHandles.remove(h);
237                         removeHandle(h);
238                 });
239                 pointHandlesPerWire.remove(owner);
240         }
241
242         private void addWireHandle(GUIWire w)
243         {
244                 WireHandle h = new WireHandle(w);
245                 handlePerWire.put(w, h);
246                 addHandle(h);
247         }
248
249         private void removeWireHandle(GUIWire w)
250         {
251                 WireHandle h = handlePerWire.get(w);
252                 handlePerWire.remove(w);
253                 removeHandle(h);
254         }
255
256         private void addHandle(Handle h)
257         {
258                 handles.add(h);
259                 callHandleAddedListeners(h);
260         }
261
262         private void removeHandle(Handle h)
263         {
264                 handles.remove(h);
265                 callHandleRemovedListeners(h);
266                 h.destroy();
267         }
268
269         public StaticPinHandle getHandle(Pin parent)
270         {
271                 return handlePerPin.get(parent);
272         }
273
274         public ComponentHandle getHandle(GUIComponent parent)
275         {
276                 return handlePerComp.get(parent);
277         }
278
279         public WireHandle getHandle(GUIWire parent)
280         {
281                 return handlePerWire.get(parent);
282         }
283
284         public Handle getInterfacePinHandle(Pin p)
285         {
286                 return handlePerInterfacePin.get(p);
287         }
288
289         /**
290          * @return A Collection of the registered {@link WirePointHandle}s of the specified wire
291          */
292         public Collection<WirePointHandle> getWirePointHandles(GUIWire parent)
293         {
294                 return pointHandlesPerWire.get(parent).stream().collect(Collectors.toSet());
295         }
296
297         /**
298          * @return An unmodifiable view of all registered {@link Handle}s
299          */
300         public Collection<Handle> getHandles()
301         {
302                 return Collections.unmodifiableCollection(handles);
303         }
304
305         /**
306          * @return An unmodifiable view of all registered {@link StaticPinHandle}s
307          */
308         public Collection<StaticPinHandle> getPinHandles()
309         {
310                 return Collections.unmodifiableCollection(handlePerPin.values());
311         }
312
313         /**
314          * @return An unmodifiable view of all registered {@link InterfacePinHandle}s
315          */
316         public Collection<InterfacePinHandle> getInterfacePinHandles()
317         {
318                 return Collections.unmodifiableCollection(handlePerInterfacePin.values());
319         }
320
321         /**
322          * @return An unmodifiable view of all registered {@link ComponentHandle}s
323          */
324         public Collection<ComponentHandle> getComponentHandles()
325         {
326                 return Collections.unmodifiableCollection(handlePerComp.values());
327         }
328
329         /**
330          * @return An unmodifiable view of all registered {@link WireHandle}s
331          */
332         public Collection<WireHandle> getWireHandles()
333         {
334                 return Collections.unmodifiableCollection(handlePerWire.values());
335         }
336
337         /**
338          * @return An unmodifiable view of all registered {@link WirePointHandle}s
339          */
340         public Collection<WirePointHandle> getWirePointHandles()
341         {
342                 return Collections.unmodifiableSet(wirePointHandles);
343         }
344
345         public void click(Point clicked, int stateMask)
346         {
347                 EditorState entryState = editor.stateManager.getState();
348
349                 // TODO: As soon as wires connected to a component being removed also are removed, change priority
350                 if (!cornerHandle.click(clicked.x, clicked.y, stateMask, entryState))
351                         if (!click(handlePerPin.values(), clicked, entryState, stateMask))
352                                 if (!click(handlePerInterfacePin.values(), clicked, entryState, stateMask))
353                                         if (!click(getWirePointHandles(), clicked, entryState, stateMask))
354                                                 if (!click(getWireHandles(), clicked, entryState, stateMask))
355                                                         if (!click(handlePerComp.values(), clicked, entryState, stateMask))
356                                                                 entryState.clickedEmpty(clicked, stateMask);
357                 entryState.clicked(clicked, stateMask);
358         }
359
360         private static boolean click(Collection<? extends Handle> handles, Point clicked, EditorState state, int stateMask)
361         {
362                 for (Handle h : handles)
363                         if (h.click(clicked.x, clicked.y, stateMask, state))
364                                 return true;
365                 return false;
366         }
367
368         public void addHandleAddedListener(Consumer<Handle> c)
369         {
370                 handleAddedListeners.add(c);
371         }
372
373         private void callHandleAddedListeners(Handle added)
374         {
375                 handleAddedListeners.forEach(l -> l.accept(added));
376         }
377
378         public void removeHandleAddedListener(Consumer<Handle> c)
379         {
380                 handleAddedListeners.remove(c);
381         }
382
383         public void addHandleRemovedListener(Consumer<Handle> c)
384         {
385                 handleRemovedListeners.add(c);
386         }
387
388         private void callHandleRemovedListeners(Handle removed)
389         {
390                 handleRemovedListeners.forEach(l -> l.accept(removed));
391         }
392
393         public void removeHandleRemovedListener(Consumer<Handle> c)
394         {
395                 handleRemovedListeners.remove(c);
396         }
397 }