* Bug #13030 fixed - Selection to Super block did not reset the origin
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / graph / SuperBlockDiagram.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2009 - DIGITEO - Antoine ELIAS
4  * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos.graph;
15
16 import java.io.File;
17 import java.io.Serializable;
18 import java.util.Hashtable;
19 import java.util.List;
20 import java.util.logging.Logger;
21
22 import org.scilab.modules.types.ScilabDouble;
23 import org.scilab.modules.xcos.Xcos;
24 import org.scilab.modules.xcos.block.BasicBlock;
25 import org.scilab.modules.xcos.block.SuperBlock;
26 import org.scilab.modules.xcos.block.io.ContextUpdate;
27 import org.scilab.modules.xcos.block.io.ContextUpdate.IOBlocks;
28 import org.scilab.modules.xcos.block.io.EventInBlock;
29 import org.scilab.modules.xcos.block.io.EventOutBlock;
30 import org.scilab.modules.xcos.block.io.ExplicitInBlock;
31 import org.scilab.modules.xcos.block.io.ExplicitOutBlock;
32 import org.scilab.modules.xcos.block.io.ImplicitInBlock;
33 import org.scilab.modules.xcos.block.io.ImplicitOutBlock;
34 import org.scilab.modules.xcos.utils.XcosEvent;
35 import org.scilab.modules.xcos.utils.XcosMessages;
36
37 import com.mxgraph.model.mxICell;
38 import com.mxgraph.util.mxEvent;
39 import com.mxgraph.util.mxEventObject;
40
41 public final class SuperBlockDiagram extends XcosDiagram implements Serializable, Cloneable {
42
43     private static final String PARENT_DIAGRAM_WAS_NULL = "Parent diagram was null";
44     private static final long serialVersionUID = -402918614723713301L;
45
46     private SuperBlock container;
47
48     /**
49      * Constructor
50      */
51     public SuperBlockDiagram() {
52         super();
53     }
54
55     /**
56      * @param superBlock
57      *            parent super block
58      */
59     public SuperBlockDiagram(SuperBlock superBlock) {
60         super();
61         setContainer(superBlock);
62     }
63
64     /**
65      * @return parent super block
66      */
67     public SuperBlock getContainer() {
68         return container;
69     }
70
71     /**
72      * @param container
73      *            parent super block
74      */
75     public void setContainer(SuperBlock container) {
76         this.container = container;
77     }
78
79     @Override
80     public File getSavedFile() {
81         if (getContainer() != null) {
82             XcosDiagram parent = getContainer().getParentDiagram();
83             if (parent != null) {
84                 return parent.getSavedFile();
85             }
86         }
87
88         return super.getSavedFile();
89     }
90
91     /**
92      * Concatenate the context with the parent one
93      *
94      * @return the context
95      * @see org.scilab.modules.xcos.graph.XcosDiagram#getContext()
96      */
97     @Override
98     public String[] getContext() {
99         final SuperBlock block = getContainer();
100         XcosDiagram graph = block.getParentDiagram();
101         if (graph == null) {
102             block.setParentDiagram(Xcos.findParent(block));
103             graph = block.getParentDiagram();
104             Logger.getLogger(SuperBlockDiagram.class.getName()).finest(PARENT_DIAGRAM_WAS_NULL);
105         }
106
107         final String[] parent;
108         if (graph == null) {
109             parent = new String[] {};
110         } else {
111             parent = graph.getContext();
112         }
113
114         final String[] current = super.getContext();
115
116         String[] full = new String[current.length + parent.length];
117         System.arraycopy(parent, 0, full, 0, parent.length);
118         System.arraycopy(current, 0, full, parent.length, current.length);
119         return full;
120     }
121
122     /**
123      * Validate I/O ports.
124      *
125      * /!\ No model modification should be made in this method, this is only a validation method.
126      *
127      * @param cell
128      *            Cell that represents the cell to validate.
129      * @param context
130      *            Hashtable that represents the global validation state.
131      */
132     @SuppressWarnings("unchecked")
133     @Override
134     public String validateCell(final Object cell, final Hashtable<Object, Object> context) {
135         String err = null;
136
137         /*
138          * Only validate I/O blocks
139          */
140
141         // get the key
142         final String key;
143         if (cell instanceof ExplicitInBlock || cell instanceof ImplicitInBlock) {
144             key = IN;
145         } else if (cell instanceof ExplicitOutBlock || cell instanceof ImplicitOutBlock) {
146             key = OUT;
147         } else if (cell instanceof EventInBlock) {
148             key = EIN;
149         } else if (cell instanceof EventOutBlock) {
150             key = EOUT;
151         } else {
152             key = null;
153         }
154
155         final BasicBlock block;
156         if (key != null) {
157             block = (BasicBlock) cell;
158         } else {
159             return err;
160         }
161
162         /*
163          * Prepare validation
164          */
165
166         // fill the context
167         fillContext(context);
168
169         /*
170          * Validate with ipar
171          */
172
173         // get the real index
174         final List <? extends BasicBlock > blocks = (List <? extends BasicBlock > ) context.get(key);
175         final int realIndex = blocks.indexOf(block) + 1;
176
177         // get the user index
178         final ScilabDouble data = (ScilabDouble) block.getIntegerParameters();
179         if (data.getWidth() < 1 || data.getHeight() < 1) {
180             return err;
181         }
182         final int userIndex = (int) data.getRealPart()[0][0];
183
184         // if the indexes are not equals, alert the user.
185         if (realIndex != userIndex) {
186             final StringBuilder str = new StringBuilder();
187             str.append("<html><body><em>");
188             str.append(XcosMessages.WRONG_PORT_NUMBER);
189             str.append("</em><br/>");
190             str.append(String.format(XcosMessages.EXPECTING_NUMBER, realIndex));
191             str.append("</body></html>    ");
192
193             err = str.toString();
194         }
195
196         return err;
197     }
198
199     /**
200      * Listener for SuperBlock diagram events.
201      */
202     @SuppressWarnings(value = { "serial" })
203     private static final class GenericSuperBlockListener implements mxIEventListener, Serializable {
204         private static GenericSuperBlockListener instance;
205
206         /**
207          * Reduce constructor visibility
208          */
209         private GenericSuperBlockListener() {
210             super();
211         }
212
213         /**
214          * Mono-threaded singleton implementation getter
215          *
216          * @return The unique instance
217          */
218         public static GenericSuperBlockListener getInstance() {
219             if (instance == null) {
220                 instance = new GenericSuperBlockListener();
221             }
222             return instance;
223         }
224
225         /**
226          * Update the IOPorts colors and values.
227          *
228          * @param arg0
229          *            the source
230          * @param arg1
231          *            the event data
232          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
233          *      com.mxgraph.util.mxEventObject)
234          */
235         @Override
236         public void invoke(Object arg0, mxEventObject arg1) {
237             final SuperBlockDiagram graph = ((SuperBlockDiagram) arg0);
238             final SuperBlock block = graph.getContainer();
239             if (block != null) {
240                 block.updateExportedPort();
241             }
242
243             // validate display errors
244             graph.getAsComponent().clearCellOverlays();
245             graph.getAsComponent().validateGraph();
246         }
247     }
248
249     /**
250      * Listener for SuperBlock diagram events.
251     @SuppressWarnings(value = { "serial" })
252      */
253     private static final class ContentChangedListener implements mxIEventListener, Serializable {
254         private static ContentChangedListener instance;
255
256         /**
257          * Reduce constructor visibility
258          */
259         private ContentChangedListener() {
260             super();
261         }
262
263         /**
264          * Mono-threaded singleton implementation getter
265          *
266          * @return The unique instance
267          */
268         public static ContentChangedListener getInstance() {
269             if (instance == null) {
270                 instance = new ContentChangedListener();
271             }
272             return instance;
273         }
274
275         /**
276          * Update the IOPorts colors and values.
277          *
278          * @param arg0
279          *            the source
280          * @param arg1
281          *            the event data
282          * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object,
283          *      com.mxgraph.util.mxEventObject)
284          */
285         @Override
286         public void invoke(Object arg0, mxEventObject arg1) {
287             final SuperBlockDiagram graph = ((SuperBlockDiagram) arg0);
288             final SuperBlock block = graph.getContainer();
289             if (block != null) {
290                 block.invalidateRpar();
291             }
292         }
293     }
294
295     /**
296     @SuppressWarnings(value = { "serial" })
297      * Update the diagram labels
298      */
299     private static final class LabelBlockListener implements mxIEventListener, Serializable {
300         private static LabelBlockListener instance;
301
302         /**
303          * Default Constructor
304          */
305         private LabelBlockListener() {
306             super();
307         }
308
309         /**
310          * @return the instance
311          */
312         public static LabelBlockListener getInstance() {
313             if (instance == null) {
314                 instance = new LabelBlockListener();
315             }
316             return instance;
317         }
318
319         /** {@inheritDoc} */
320         @Override
321         public void invoke(Object sender, mxEventObject evt) {
322             final String value = (String) evt.getProperty("value");
323             final Object parent = evt.getProperty("parent");
324             if (parent instanceof ContextUpdate) {
325                 final ContextUpdate block = (ContextUpdate) parent;
326                 final ScilabDouble data = (ScilabDouble) block.getIntegerParameters();
327
328                 final int index;
329                 if (data.getHeight() > 0 && data.getWidth() > 0) {
330                     index = (int) data.getRealPart()[0][0];
331                 } else {
332                     index = 1;
333                 }
334
335                 final SuperBlock container = ((SuperBlockDiagram) sender).getContainer();
336                 if (container == null) {
337                     return;
338                 }
339
340                 container.sortChildren();
341                 final List<mxICell> ports = IOBlocks.getPorts(container, block.getClass());
342
343                 XcosDiagram graph = container.getParentDiagram();
344                 if (graph == null) {
345                     container.setParentDiagram(Xcos.findParent(container));
346                     graph = container.getParentDiagram();
347                     Logger.getLogger(SuperBlockDiagram.class.getName()).finest(PARENT_DIAGRAM_WAS_NULL);
348                 }
349
350                 if (index > 0 && index <= ports.size()) {
351                     container.getParentDiagram().cellLabelChanged(ports.get(index - 1), value, false);
352                 }
353             }
354         }
355     }
356
357     /**
358      * Install the specific listeners for {@link SuperBlockDiagram}.
359      */
360     public void installSuperBlockListeners() {
361         removeListener(GenericSuperBlockListener.getInstance());
362         removeListener(LabelBlockListener.getInstance());
363         removeListener(ContentChangedListener.getInstance());
364
365         addListener(mxEvent.CELLS_ADDED, GenericSuperBlockListener.getInstance());
366         addListener(mxEvent.CELLS_REMOVED, GenericSuperBlockListener.getInstance());
367         addListener(XcosEvent.IO_PORT_VALUE_UPDATED, GenericSuperBlockListener.getInstance());
368         addListener(mxEvent.LABEL_CHANGED, LabelBlockListener.getInstance());
369
370         addListener(mxEvent.CELLS_ADDED, ContentChangedListener.getInstance());
371         addListener(mxEvent.CELLS_MOVED, ContentChangedListener.getInstance());
372         addListener(mxEvent.CELLS_REMOVED, ContentChangedListener.getInstance());
373         addListener(mxEvent.CELLS_RESIZED, ContentChangedListener.getInstance());
374     }
375
376     /**
377      * This function set the SuperBlock diagram and all its parents in a
378      * modified state or not.
379      *
380      * @param modified
381      *            status
382      */
383     @Override
384     public void setModified(boolean modified) {
385         super.setModified(modified);
386
387         if (getContainer() != null && getContainer().getParentDiagram() != null) {
388             getContainer().getParentDiagram().setModified(modified);
389         }
390     }
391
392     /**
393      * This function set the SuperBlock diagram in a modified state or not.
394      *
395      * It doesn't perform recursively on the parent diagrams. If you want such a
396      * behavior use setModified instead.
397      *
398      * @param modified
399      *            status
400      * @see #setModified
401      */
402     public void setModifiedNonRecursively(boolean modified) {
403         super.setModified(modified);
404     }
405
406     /** {@inheritDoc} */
407     // CSOFF: SuperClone
408     @Override
409     public Object clone() throws CloneNotSupportedException {
410         final SuperBlockDiagram clone = new SuperBlockDiagram();
411         clone.installListeners();
412         clone.installSuperBlockListeners();
413
414         clone.setScicosParameters((ScicosParameters) getScicosParameters().clone());
415         clone.addCells(cloneCells(getChildCells(getDefaultParent())), clone.getDefaultParent());
416
417         return clone;
418     }
419     // CSON: SuperClone
420 }