Xcos: fix Split block management
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / model / XcosCell.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2015-2015 - Scilab Enterprises - Clement DAVID
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 package org.scilab.modules.xcos.graph.model;
17
18 import java.util.Collections;
19 import java.util.List;
20
21 import org.scilab.modules.xcos.JavaController;
22 import org.scilab.modules.xcos.Kind;
23 import org.scilab.modules.xcos.ObjectProperties;
24 import org.scilab.modules.xcos.PortKind;
25 import org.scilab.modules.xcos.VectorOfDouble;
26 import org.scilab.modules.xcos.VectorOfScicosID;
27
28 import com.mxgraph.model.mxCell;
29 import com.mxgraph.model.mxGeometry;
30 import com.mxgraph.model.mxICell;
31 import com.mxgraph.util.mxPoint;
32 import java.util.regex.Pattern;
33
34 public class XcosCell extends mxCell {
35     private static final long serialVersionUID = 1L;
36     private static Pattern validCIdentifier = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]+");
37
38     private transient ScicosObjectOwner owner;
39
40     /**
41      * Construct an Xcos graphical object.
42      *
43      * This Java object owns the corresponding MVC object and thus will unrefererence it on GC.
44      *
45      * @param controller
46      *            the shared controller
47      * @param uid
48      *            the associated MVC identifier
49      * @param kind
50      *            the associated MVC kind
51      */
52     public XcosCell(final JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id) {
53         super();
54
55         // defensive programming
56         if (uid == 0l) {
57             throw new IllegalArgumentException();
58         }
59
60         owner = new ScicosObjectOwner(controller, uid, kind);
61         setValue(controller, value);
62         setGeometry(controller, geometry);
63         setStyle(controller, style);
64         setId(controller, id);
65     }
66
67     /**
68      * @return the MVC unique identifier
69      */
70     public long getUID() {
71         return owner.getUID();
72     }
73
74     /**
75      * @return the MVC kind of object
76      */
77     public Kind getKind() {
78         return owner.getKind();
79     }
80
81     /*
82      * Override setters and hierarchy modifiers to propagate changes to the model
83      */
84
85     /*
86      * (non-Javadoc)
87      *
88      * @see com.mxgraph.model.mxCell#setValue(java.lang.Object)
89      */
90     @Override
91     public void setValue(Object value) {
92         setValue(new JavaController(), value);
93     }
94
95     public final void setValue(JavaController controller, Object value) {
96         super.setValue(value);
97         setMVCValue(controller, value);
98     }
99
100     private void setMVCValue(JavaController controller, Object value) {
101         if (value == null) {
102             return;
103         }
104
105         switch (getKind()) {
106             case BLOCK:
107                 if (validCIdentifier.matcher(String.valueOf(value)).matches()) {
108                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.LABEL, String.valueOf(value));
109                 }
110             // no break on purpose
111             case ANNOTATION:
112                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DESCRIPTION, String.valueOf(value));
113                 break;
114             case LINK:
115             case PORT:
116                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.LABEL, String.valueOf(value));
117                 break;
118             default:
119                 break;
120         }
121     }
122
123     /*
124      * (non-Javadoc)
125      *
126      * @see com.mxgraph.model.mxCell#setId(java.lang.String)
127      */
128     @Override
129     public void setId(String id) {
130         setId(new JavaController(), id);
131     }
132
133     public final void setId(JavaController controller, String id) {
134         super.setId(id);
135         setMVCId(controller, id);
136     }
137
138     private void setMVCId(JavaController controller, String id) {
139         if (id == null) {
140             return;
141         }
142
143         switch (getKind()) {
144             case ANNOTATION:
145             case BLOCK:
146             case LINK:
147             case PORT:
148                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.UID, id);
149                 break;
150             default:
151                 break;
152         }
153     }
154
155     /*
156      * (non-Javadoc)
157      *
158      * @see com.mxgraph.model.mxCell#setGeometry(com.mxgraph.model.mxGeometry)
159      */
160     @Override
161     public void setGeometry(mxGeometry geometry) {
162         setGeometry(new JavaController(), geometry);
163     }
164
165     public final void setGeometry(JavaController controller, mxGeometry geometry) {
166         super.setGeometry(geometry);
167         setMVCGeometry(controller, geometry);
168     }
169
170     private void setMVCGeometry(JavaController controller, mxGeometry geometry) {
171         if (geometry == null) {
172             return;
173         }
174
175         switch (getKind()) {
176             case ANNOTATION:
177             case BLOCK: {
178                 VectorOfDouble v = new VectorOfDouble(4);
179                 v.set(0, geometry.getX());
180                 v.set(1, geometry.getY());
181                 v.set(2, geometry.getWidth());
182                 v.set(3, geometry.getHeight());
183                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.GEOMETRY, v);
184                 break;
185             }
186             case LINK: {
187                 /*
188                  * try to find the origin of the source and target accordingly to the JGraphX implementation
189                  */
190                 mxPoint sourcePoint = null;
191                 mxPoint targetPoint = null;
192                 mxICell sourceCell = getSource();
193                 mxICell targetCell = getTarget();
194                 if (sourceCell != null && sourceCell.getGeometry() != null) {
195                     sourcePoint = new mxPoint(sourceCell.getGeometry().getCenterX(), sourceCell.getGeometry().getCenterY());
196                 }
197                 if (targetCell != null && targetCell.getGeometry() != null) {
198                     targetPoint = new mxPoint(targetCell.getGeometry().getCenterX(), targetCell.getGeometry().getCenterY());
199                 }
200                 if (sourcePoint == null) {
201                     sourcePoint = geometry.getSourcePoint();
202                 }
203                 if (targetPoint == null) {
204                     targetPoint = geometry.getTargetPoint();
205                 }
206                 if (sourcePoint == null) {
207                     sourcePoint = new mxPoint();
208                 }
209                 if (targetPoint == null) {
210                     targetPoint = new mxPoint();
211                 }
212                 List<mxPoint> points = geometry.getPoints();
213                 if (points == null) {
214                     points = Collections.emptyList();
215                 }
216
217                 /*
218                 * At that point, the sourcePoint, targetPoint and points are valid values (but may be unknown) encode them to the the CONTROL_POINTS
219                 */
220
221                 // Allocate some space to contains them all
222                 int nbOfPoints = 2 + points.size();
223                 VectorOfDouble v = new VectorOfDouble(2 * nbOfPoints);
224                 int i = 0;
225                 // then fill the allocation space
226                 v.set(2 * i, sourcePoint.getX());
227                 v.set(2 * i + 1, sourcePoint.getY());
228                 i++;
229                 for (; i < nbOfPoints - 1; i++) {
230                     v.set(2 * i, points.get(i - 1).getX());
231                     v.set(2 * i + 1, points.get(i - 1).getY());
232                 }
233                 v.set(2 * i, targetPoint.getX());
234                 v.set(2 * i + 1, targetPoint.getY());
235                 i++;
236                 /*
237                  * Finally push the values to the model
238                  */
239                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CONTROL_POINTS, v);
240                 break;
241             }
242             default:
243                 break;
244         }
245     }
246
247     /*
248      * (non-Javadoc)
249      *
250      * @see com.mxgraph.model.mxCell#setStyle(java.lang.String)
251      */
252     @Override
253     public void setStyle(String style) {
254         setStyle(new JavaController(), style);
255     }
256
257     public final void setStyle(JavaController controller, String style) {
258         super.setStyle(style);
259         setMVCStyle(controller, style);
260     }
261
262     private void setMVCStyle(JavaController controller, String style) {
263         if (style == null) {
264             return;
265         }
266
267         switch (getKind()) {
268             case BLOCK:
269             case LINK:
270             case ANNOTATION:
271             case PORT:
272                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.STYLE, style);
273                 break;
274             default:
275                 break;
276         }
277     }
278
279     /*
280      * (non-Javadoc)
281      *
282      * @see com.mxgraph.model.mxCell#removeFromParent()
283      */
284     @Override
285     public void removeFromParent() {
286         super.removeFromParent();
287
288         // do not remove from parent on SUPER_f diagram creation : there is an MVC parent but no JGraphX one
289         if (parent == null) {
290             return;
291         }
292
293         JavaController controller = new JavaController();
294         switch (getKind()) {
295             case ANNOTATION:
296             case BLOCK:
297             case LINK: {
298                 /*
299                  * Retrieve the parent
300                  */
301                 long[] parent = new long[1];
302                 Kind parentKind = Kind.BLOCK;
303                 ObjectProperties prop = ObjectProperties.PARENT_BLOCK;
304                 controller.getObjectProperty(getUID(), getKind(), prop, parent);
305                 if (parent[0] == 0l) {
306                     parentKind = Kind.DIAGRAM;
307                     prop = ObjectProperties.PARENT_DIAGRAM;
308                     controller.getObjectProperty(getUID(), getKind(), prop, parent);
309                 }
310
311                 /*
312                  * If there is a parent, clear it
313                  */
314                 if (parent[0] != 0l) {
315                     VectorOfScicosID children = new VectorOfScicosID();
316                     controller.getObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
317                     children.remove(getUID());
318                     controller.setObjectProperty(parent[0], parentKind, ObjectProperties.CHILDREN, children);
319
320                     controller.setObjectProperty(getUID(), getKind(), prop, 0l);
321                 }
322                 break;
323             }
324             case PORT: {
325                 long[] parent = new long[1];
326                 Kind parentKind = Kind.BLOCK;
327                 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, parent);
328
329                 int[] portKind = new int[1];
330                 controller.getObjectProperty(getUID(), getKind(), ObjectProperties.PORT_KIND, portKind);
331                 ObjectProperties property = relatedPortKindProperty(portKind[0]);
332
333                 VectorOfScicosID ports = new VectorOfScicosID();
334                 controller.getObjectProperty(parent[0], parentKind, property, ports);
335                 ports.remove(getUID());
336                 controller.setObjectProperty(parent[0], parentKind, property, ports);
337
338                 controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, 0l);
339                 break;
340             }
341             default:
342                 break;
343         }
344     }
345
346     private ObjectProperties relatedPortKindProperty(int portKind) {
347         ObjectProperties property;
348         switch (PortKind.values()[portKind]) {
349             case PORT_IN:
350                 property = ObjectProperties.INPUTS;
351                 break;
352             case PORT_OUT:
353                 property = ObjectProperties.OUTPUTS;
354                 break;
355             case PORT_EIN:
356                 property = ObjectProperties.EVENT_INPUTS;
357                 break;
358             case PORT_EOUT:
359                 property = ObjectProperties.EVENT_OUTPUTS;
360                 break;
361             default:
362                 property = null;
363                 break;
364         }
365         return property;
366     }
367
368     /*
369      * (non-Javadoc)
370      *
371      * @see com.mxgraph.model.mxCell#setParent(com.mxgraph.model.mxICell)
372      */
373     @Override
374     public void setParent(mxICell parent) {
375         super.setParent(parent);
376
377         if (parent instanceof XcosCell) {
378             XcosCell p = (XcosCell) parent;
379             JavaController controller = new JavaController();
380             switch (getKind()) {
381                 case ANNOTATION:
382                 case BLOCK:
383                 case LINK:
384                     if (p.getKind() == Kind.DIAGRAM) {
385                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, p.getUID());
386                     } else {
387                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_BLOCK, p.getUID());
388
389                         long[] root = new long[1];
390                         controller.getObjectProperty(p.getUID(), p.getKind(), ObjectProperties.PARENT_DIAGRAM, root);
391                         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.PARENT_DIAGRAM, root[0]);
392                     }
393                     break;
394                 case PORT:
395                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_BLOCK, p.getUID());
396                     break;
397                 default:
398                     break;
399             }
400         }
401     }
402
403     @Override
404     public mxICell setTerminal(mxICell terminal, boolean isSource) {
405         mxICell cell = super.setTerminal(terminal, isSource);
406         if (cell == null) {
407             return cell;
408         }
409
410         // a terminal of an XcosCell is always another XcosCell
411         final long uid = ((XcosCell) cell).getUID();
412         final Kind kind = ((XcosCell) cell).getKind();
413
414         JavaController controller = new JavaController();
415         switch (getKind()) {
416             case LINK:
417                 if (isSource) {
418                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.SOURCE_PORT, uid);
419                 } else {
420                     controller.setObjectProperty(getUID(), getKind(), ObjectProperties.DESTINATION_PORT, uid);
421                 }
422                 if (uid != 0l) {
423                     controller.setObjectProperty(uid, kind, ObjectProperties.CONNECTED_SIGNALS, getUID());
424                 }
425                 break;
426             default:
427                 break;
428         }
429
430         return cell;
431     }
432
433     /*
434      * (non-Javadoc)
435      *
436      * @see com.mxgraph.model.mxCell#insert(com.mxgraph.model.mxICell, int)
437      */
438     @Override
439     public mxICell insert(mxICell child, int index) {
440         mxICell inserted = super.insert(child, index);
441
442         // the child might not be an XcosCell but just an mxCell label
443         if (child instanceof XcosCell) {
444             XcosCell c = (XcosCell) child;
445             switch (getKind()) {
446                 case BLOCK:
447                     if (c.getKind() == Kind.PORT) {
448                         insertPort(c, index);
449                     } else {
450                         insertChild(c, index);
451                     }
452                     break;
453                 case DIAGRAM:
454                     insertChild(c, index);
455                     break;
456                 default:
457                     break;
458             }
459
460             JavaController controller = new JavaController();
461             controller.referenceObject(c.getUID());
462         }
463
464         return inserted;
465     }
466
467     private void insertPort(XcosCell c, int index) {
468         JavaController controller = new JavaController();
469         int[] v = new int[1];
470         controller.getObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PORT_KIND, v);
471
472         VectorOfScicosID children = new VectorOfScicosID();
473         final ObjectProperties property = relatedPortKindProperty(v[0]);
474
475         if (property != null) {
476             controller.getObjectProperty(getUID(), getKind(), property, children);
477             // do no use the index argument as it is not possible to insert out of order on the MVC (as we have kind of port)
478             children.add(c.getUID());
479             controller.setObjectProperty(getUID(), getKind(), property, children);
480         }
481     }
482
483     private void insertChild(XcosCell c, int index) {
484         JavaController controller = new JavaController();
485         VectorOfScicosID children = new VectorOfScicosID();
486
487         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
488         children.add(index, c.getUID());
489         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
490     }
491
492     /*
493      * (non-Javadoc)
494      *
495      * @see com.mxgraph.model.mxCell#remove(com.mxgraph.model.mxICell)
496      */
497     @Override
498     public mxICell remove(mxICell child) {
499         mxICell removed = super.remove(child);
500
501         // the child might not be an XcosCell but just an mxCell label
502         if (child instanceof XcosCell) {
503             XcosCell c = (XcosCell) child;
504             switch (getKind()) {
505                 case BLOCK:
506                     if (c.getKind() == Kind.PORT) {
507                         removePort(c);
508                     } else {
509                         removeChild(c);
510                     }
511                     break;
512                 case DIAGRAM:
513                     removeChild(c);
514                     break;
515                 default:
516                     break;
517             }
518
519             JavaController controller = new JavaController();
520             controller.deleteObject(c.getUID());
521         }
522         return removed;
523     }
524
525     private void removePort(XcosCell c) {
526         JavaController controller = new JavaController();
527         int[] v = new int[1];
528         controller.getObjectProperty(c.getUID(), c.getKind(), ObjectProperties.PORT_KIND, v);
529
530         VectorOfScicosID children = new VectorOfScicosID();
531         final ObjectProperties property;
532         switch (PortKind.values()[v[0]]) {
533             case PORT_IN:
534                 property = ObjectProperties.INPUTS;
535                 break;
536             case PORT_OUT:
537                 property = ObjectProperties.OUTPUTS;
538                 break;
539             case PORT_EIN:
540                 property = ObjectProperties.EVENT_INPUTS;
541                 break;
542             case PORT_EOUT:
543                 property = ObjectProperties.EVENT_OUTPUTS;
544                 break;
545             default:
546                 property = null;
547                 break;
548         }
549
550         controller.getObjectProperty(getUID(), getKind(), property, children);
551         children.remove(c.getUID());
552         controller.setObjectProperty(getUID(), getKind(), property, children);
553     }
554
555     private void removeChild(XcosCell c) {
556         JavaController controller = new JavaController();
557         VectorOfScicosID children = new VectorOfScicosID();
558
559         controller.getObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
560         children.remove(c.getUID());
561         controller.setObjectProperty(getUID(), getKind(), ObjectProperties.CHILDREN, children);
562
563     }
564
565     /*
566      * Override methods from Object
567      */
568
569     @Override
570     public Object clone() throws CloneNotSupportedException {
571         JavaController controller = new JavaController();
572         XcosCell c = (XcosCell) super.clone();
573
574         c.owner = new ScicosObjectOwner(controller.cloneObject(getUID(), true, false), getKind());
575         return c;
576     }
577 }