* bug #14835 fixed: `AFFICH_m` block was not rendered correctly.
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / block / AfficheBlock.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
4  * Copyright (C) 2011-2017 - Scilab Enterprises - Clement DAVID
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16 package org.scilab.modules.xcos.block;
17
18 import com.mxgraph.model.mxGeometry;
19 import com.mxgraph.util.mxConstants;
20 import com.mxgraph.view.mxCellState;
21 import java.awt.GraphicsEnvironment;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.io.Serializable;
25 import java.util.Formatter;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map.Entry;
30 import java.util.TreeMap;
31
32 import javax.swing.Timer;
33 import org.scilab.modules.graph.utils.Font;
34
35 import org.scilab.modules.graph.utils.ScilabExported;
36 import org.scilab.modules.graph.utils.StyleMap;
37 import org.scilab.modules.xcos.JavaController;
38 import org.scilab.modules.xcos.Kind;
39 import org.scilab.modules.xcos.ObjectProperties;
40 import org.scilab.modules.xcos.VectorOfDouble;
41 import org.scilab.modules.xcos.VectorOfInt;
42 import org.scilab.modules.xcos.Xcos;
43 import org.scilab.modules.xcos.graph.XcosDiagram;
44 import org.scilab.modules.xcos.graph.model.XcosGraphModel;
45
46 /**
47  * Implement the AFFICH_m block
48  */
49 public final class AfficheBlock extends BasicBlock {
50     /**
51      * Default refresh rate used on the simulation to update block.
52      */
53     private static final int DEFAULT_TIMER_RATE = 200;
54
55     private static final String NEW_LINE = System.getProperty("line.separator");
56     private static final String SPACE = "  ";
57
58     private static final TreeMap<String, String[][]> values = new TreeMap<>();
59     private static final UpdateValueListener updateAction = new UpdateValueListener();
60     private static final Timer printTimer = new Timer(DEFAULT_TIMER_RATE, updateAction);
61
62     /**
63      * Update the value of the associated block
64      */
65     private static class UpdateValueListener implements ActionListener, Serializable {
66         /**
67          * Default constructor
68          */
69         public UpdateValueListener() {
70         }
71
72         /**
73          * Calculate an print the new String value.
74          *
75          * @param e
76          *            the current event
77          * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
78          */
79         @Override
80         public void actionPerformed(ActionEvent e) {
81             List<XcosDiagram> diags = Xcos.getInstance().openedDiagrams();
82
83             synchronized (values) {
84                 for (Iterator<Entry<String, String[][]>> it = values.entrySet().iterator(); it.hasNext();) {
85                     final Entry<String, String[][]> entry = it.next();
86
87                     update(diags, entry.getKey(), entry.getValue());
88
89                     it.remove();
90                 }
91             }
92         }
93
94         /**
95          * Update and refresh the values
96          */
97         private void update(List<XcosDiagram> diags, final String uid, final String[][] data) {
98             /* Look for the cell uid, keeping its diag Diagram available */
99             class DiagramBlockPair {
100                 final XcosDiagram diag;
101                 final Object block;
102
103                 DiagramBlockPair(final XcosDiagram p, final Object b) {
104                     diag = p;
105                     block = b;
106                 }
107             }
108             DiagramBlockPair found = diags.stream()
109                                      .map(d -> new DiagramBlockPair(d, ((XcosGraphModel)d.getModel()).getCell(uid)))
110                                      .filter(p -> p.block != null)
111                                      .findAny().orElse(null);
112             if (found == null) {
113                 return;
114             }
115
116             /* render and refresh */
117             final String value = getText(data);
118             found.diag.getModel().setValue(found.block, value);
119
120             final mxCellState state = found.diag.getView().getState(found.block);
121             if (state != null) {
122                 state.setLabel(value);
123             }
124
125             found.diag.getAsComponent().redraw(state);
126         }
127     }
128
129     public AfficheBlock(JavaController controller, long uid, Kind kind, Object value, mxGeometry geometry, String style, String id) {
130         super(controller, uid, kind, value, geometry, style, id);
131     }
132
133     @Override
134     protected void updateStyle(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
135         final StyleMap styleMap = new StyleMap(modifiedBlock.getStyle());
136
137         VectorOfInt ipar = new VectorOfInt();
138         controller.getObjectProperty(getUID(), Kind.BLOCK, ObjectProperties.IPAR, ipar);
139
140         // font
141         final int parsedFontInt = ipar.get(0);
142         styleMap.put(mxConstants.STYLE_FONTFAMILY, Font.getFont(parsedFontInt).getName());
143
144         // fontsize
145         final int parsedFontSizeInt = ipar.get(1);
146         styleMap.put(mxConstants.STYLE_FONTSIZE, Integer.toString(Font.getSize(parsedFontSizeInt)));
147
148         // colr
149         final int parsedFontColorInt = ipar.get(2);
150         String color = "#" + Integer.toHexString(Font.getColor(parsedFontColorInt).getRGB());
151         styleMap.put(mxConstants.STYLE_FONTCOLOR, color);
152
153         parent.getModel().setStyle(this, styleMap.toString());
154     }
155
156     @Override
157     protected void updateValue(JavaController controller, XcosDiagram parent, BasicBlock modifiedBlock) {
158
159         VectorOfInt ipar = new VectorOfInt();
160         controller.getObjectProperty(getUID(), Kind.BLOCK, ObjectProperties.IPAR, ipar);
161
162         VectorOfDouble dstate = new VectorOfDouble();
163         controller.getObjectProperty(getUID(), Kind.BLOCK, ObjectProperties.DSTATE, dstate);
164
165         // nt
166         final int width = ipar.get(3);
167
168         // nd
169         final int rational = ipar.get(4);
170
171         // in(1,1)
172         final int rows = ipar.get(5);
173         final int cols = (dstate.size() - 6) / rows;
174
175         final String format = "%" + Integer.toString(width) + "." + Integer.toString(rational) + "f";
176         final StringBuilder sb = new StringBuilder();
177         for (int i = 0; i < rows; i++) {
178             for (int j = 0; j < cols; j++) {
179                 sb.append(new Formatter(Locale.US).format(format, 0.0).toString());
180                 sb.append(SPACE);
181             }
182             sb.append(NEW_LINE);
183         }
184
185         parent.getModel().setValue(this, sb.toString());
186     }
187
188     /**
189      * Construct a String representation of the values.
190      */
191     private static String getText(final String[][] data) {
192         final StringBuilder blockResult = new StringBuilder();
193         final int iRows = data.length;
194         final int iCols = data[0].length;
195
196         for (int i = 0; i < iRows; i++) {
197             for (int j = 0; j < iCols; j++) {
198                 if (iCols != 0) {
199                     blockResult.append(SPACE);
200                 }
201
202                 blockResult.append(data[i][j]);
203             }
204             blockResult.append(NEW_LINE);
205         }
206
207         return blockResult.toString();
208     }
209
210     /**
211      * Assign a value to an AfficheBlock instance
212      *
213      * @param uid
214      *            the block id
215      * @param value
216      *            the value to set.
217      */
218     @ScilabExported(module = "scicos_blocks", filename = "Blocks.giws.xml")
219     public static void setValue(final String uid, final String[][] value) {
220         if (value.length == 0 || value[0].length == 0) {
221             throw new IllegalArgumentException("value is not a non-empty String matrix (String[][])");
222         }
223
224         // update nothing in case of a headless env.
225         if (GraphicsEnvironment.isHeadless()) {
226             return;
227         }
228
229         synchronized (values) {
230             values.put(uid, value);
231
232             if (!printTimer.isRunning()) {
233                 printTimer.start();
234             }
235         }
236     }
237 }