* Bug #14587 fixed - Datatip textbox wrong clipping when loaded from *.scg file.
[scilab.git] / scilab / modules / graphic_objects / src / java / org / scilab / modules / graphic_objects / datatip / Datatip.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Pedro Arthur dos S. Souza
4  * Copyright (C) 2012 - Caio Lucas dos S. Souza
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
17 package org.scilab.modules.graphic_objects.datatip;
18
19 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_DISPLAY_COMPONENTS__;
20 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_AUTOORIENTATION__;
21 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_BOX_MODE__;
22 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_DATA__;
23 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_DISPLAY_FNC__;
24 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_INTERP_MODE__;
25 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_LABEL_MODE__;
26 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_ORIENTATION__;
27 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_DATATIP_INDEXES__;
28
29 import java.text.DecimalFormat;
30 import java.text.DecimalFormatSymbols;
31
32 import org.scilab.forge.scirenderer.ruler.graduations.UserDefinedFormat;
33
34 import org.scilab.modules.action_binding.InterpreterManagement;
35 import static org.scilab.modules.graphic_objects.graphicObject.ClippableProperty.ClipStateType;
36 import org.scilab.modules.graphic_objects.PolylineData;
37 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
38 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
39 import org.scilab.modules.graphic_objects.graphicObject.Visitor;
40 import org.scilab.modules.graphic_objects.textObject.Text;
41 import org.scilab.modules.localization.Messages;
42
43 public class Datatip extends Text {
44
45     /** false = datatip text box is hidden*/
46     Boolean tipBoxMode;
47     /** false = datatip label is hidden*/
48     Boolean tipLabelMode;
49     /** selects which coordinates componet to diplay*/
50     String displayComponents;
51     /** false = no interpolation between point segments*/
52     Boolean interpMode;
53     /** Displayed number format*/
54     DecimalFormat tipTextFormat;
55     /** Display function*/
56     String displayFnc;
57     /** For automatic update the datatip orientation*/
58     Boolean autoOrientation;
59     /* index of data in parent object ( polyline, plot3d, fac3d*/
60     /* size = 1 for polyline and 2 for others*/
61     Integer dataIndex;
62     Double ratio;
63
64
65     enum DatatipObjectProperty { TIP_DATA, TIP_BOX_MODE, TIP_LABEL_MODE, TIP_ORIENTATION, TIP_AUTOORIENTATION, TIP_DISPLAY_COMPONENTS, TIP_INTERP_MODE, TIP_DISPLAY_FNC, TIP_INDEXES};
66     enum TipOrientation { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, LEFT, RIGHT, TOP, BOTTOM;
67
68                           /**
69                            * Transform a integer to a TipOrientation enum.
70                            */
71     public static TipOrientation intToEnum(Integer i) {
72         switch (i) {
73             case 0:
74                 return TipOrientation.TOP_LEFT;
75             case 1:
76                 return TipOrientation.TOP_RIGHT;
77             case 2:
78                 return TipOrientation.BOTTOM_LEFT;
79             case 3:
80                 return TipOrientation.BOTTOM_RIGHT;
81             case 4:
82                 return TipOrientation.LEFT;
83             case 5:
84                 return TipOrientation.RIGHT;
85             case 6:
86                 return TipOrientation.TOP;
87             case 7:
88                 return TipOrientation.BOTTOM;
89             default:
90                 return TipOrientation.TOP_RIGHT;
91         }
92     }
93
94                         };
95
96     TipOrientation currentOrientation;
97
98     /**
99      * Initializes the datatip, setup format, orientation and mark.
100      */
101     public Datatip() {
102         super();
103         displayComponents = "xy";
104         autoOrientation = true;
105         setOrientationAsEnum(TipOrientation.TOP_RIGHT);
106         DecimalFormat fb = new DecimalFormat("#.####E00");
107         DecimalFormatSymbols decimalFormatSymbols = fb.getDecimalFormatSymbols();
108         decimalFormatSymbols.setDecimalSeparator('.');
109         decimalFormatSymbols.setExponentSeparator("e");
110         decimalFormatSymbols.setGroupingSeparator('\u00A0');
111         fb.setDecimalFormatSymbols(decimalFormatSymbols);
112         tipTextFormat = new UserDefinedFormat(fb, "%g", 1, 0);
113
114         tipBoxMode = true;
115         tipLabelMode = true;
116         interpMode = true;
117         displayFnc = "";
118         ratio = 0.;
119         dataIndex = Integer.MIN_VALUE;
120         setVisible(true);
121         setBox(true);
122         setLineMode(true);
123         setFillMode(true);
124         setBackground(-2);
125         setClipStateAsEnum(ClipStateType.OFF);
126
127         setMarkMode(true);
128         setMarkSize(8);
129         setMarkBackground(-1);
130         setMarkForeground(-1);
131         setMarkStyle(11);
132     }
133
134     @Override
135     public void accept(Visitor visitor) {
136         visitor.visit(this);
137     }
138
139     /**
140      * Convert the property name to the DatatipObjectProperty enum.
141      */
142     public Object getPropertyFromName(int propertyName) {
143         switch (propertyName) {
144             case __GO_DATATIP_DATA__:
145                 return DatatipObjectProperty.TIP_DATA;
146             case __GO_DATATIP_BOX_MODE__:
147                 return DatatipObjectProperty.TIP_BOX_MODE;
148             case __GO_DATATIP_LABEL_MODE__:
149                 return DatatipObjectProperty.TIP_LABEL_MODE;
150             case __GO_DATATIP_ORIENTATION__:
151                 return DatatipObjectProperty.TIP_ORIENTATION;
152             case __GO_DATATIP_DISPLAY_COMPONENTS__:
153                 return DatatipObjectProperty.TIP_DISPLAY_COMPONENTS;
154             case __GO_DATATIP_AUTOORIENTATION__:
155                 return DatatipObjectProperty.TIP_AUTOORIENTATION;
156             case __GO_DATATIP_INTERP_MODE__:
157                 return DatatipObjectProperty.TIP_INTERP_MODE;
158             case __GO_DATATIP_DISPLAY_FNC__:
159                 return DatatipObjectProperty.TIP_DISPLAY_FNC;
160             case __GO_DATATIP_INDEXES__ :
161                 return DatatipObjectProperty.TIP_INDEXES;
162             default:
163                 return super.getPropertyFromName(propertyName);
164         }
165     }
166
167     /**
168      * @return the datatip property
169      */
170     public Object getProperty(Object property) {
171         if (property instanceof DatatipObjectProperty) {
172             switch ((DatatipObjectProperty) property) {
173                 case TIP_DATA:
174                     return getTipData();
175                 case TIP_BOX_MODE:
176                     return getTipBoxMode();
177                 case TIP_LABEL_MODE:
178                     return getTipLabelMode();
179                 case TIP_ORIENTATION:
180                     return getOrientation();
181                 case TIP_DISPLAY_COMPONENTS:
182                     return getDisplayComponents();
183                 case TIP_AUTOORIENTATION:
184                     return isAutoOrientationEnabled();
185                 case TIP_INTERP_MODE:
186                     return getInterpMode();
187                 case TIP_DISPLAY_FNC:
188                     return getDisplayFunction();
189                 case TIP_INDEXES:
190                     return getIndexes();
191             }
192         }
193
194         return super.getProperty(property);
195     }
196
197     /**
198      * Set the datatip property
199      * @param property the property
200      * @param value the new property value.
201      */
202     public UpdateStatus setProperty(Object property, Object value) {
203         if (property instanceof DatatipObjectProperty) {
204             switch ((DatatipObjectProperty) property) {
205                 case TIP_BOX_MODE:
206                     return setTipBoxMode((Boolean) value);
207                 case TIP_LABEL_MODE:
208                     return setTipLabelMode((Boolean) value);
209                 case TIP_ORIENTATION:
210                     return setOrientation((Integer) value);
211                 case TIP_DISPLAY_COMPONENTS:
212                     return setDisplayComponents((String) value);
213                 case TIP_AUTOORIENTATION:
214                     return setAutoOrientation((Boolean) value);
215                 case TIP_INTERP_MODE:
216                     return setInterpMode((Boolean) value);
217                 case TIP_DISPLAY_FNC:
218                     return setDisplayFunction((String) value);
219                 case TIP_INDEXES:
220                     return setIndexes((Double[]) value);
221             }
222         }
223
224         return super.setProperty(property, value);
225     }
226
227     /**
228      * Calculate the current tip data based on the given array
229      * @param data the data array
230      * @return the tip data
231      */
232     private double calcTipData(final double[] data) {
233         if (data.length < dataIndex + 2) {
234             if (data.length >= 1) {
235                 return data[data.length - 1];
236             } else {
237                 return 0.0;
238             }
239         }
240
241         //get pt0 and pt1 from polyline data
242         final double pt0 = data[dataIndex];
243         final double pt1 = data[dataIndex + 1];
244
245         return pt0 + (pt1 - pt0) * ratio;
246     }
247
248     /**
249      * Get the current tip data X
250      * @return the tip data X
251      */
252     public double getTipDataX() {
253         final double[] dataX = (double[]) PolylineData.getDataX(getParent());
254         return calcTipData(dataX);
255     }
256
257     /**
258      * Get the current tip data Y
259      * @return the tip data Y
260      */
261     public double getTipDataY() {
262         final double[] dataY = (double[]) PolylineData.getDataY(getParent());
263         return calcTipData(dataY);
264     }
265
266     /**
267      * Get the current tip data Z
268      * @return the tip data Z
269      */
270     public double getTipDataZ() {
271         final double[] dataZ = (double[]) PolylineData.getDataZ(getParent());
272         return calcTipData(dataZ);
273     }
274
275     /**
276      * Get the current tip data
277      * @return the tip data
278      */
279     public Double[] getTipData() {
280         return new Double[] {getTipDataX(), getTipDataY(), getTipDataZ()};
281     }
282
283     /**
284      * Get the current textbox orientation as an integer
285      * @return current orientation.
286      */
287     public Integer getOrientation() {
288         return getOrientationAsEnum().ordinal();
289     }
290
291     /**
292      * Get the current oriantation as a enum
293      * @return the current orientation
294      */
295     public TipOrientation getOrientationAsEnum() {
296         return currentOrientation;
297     }
298
299     /**
300      * Set the current orientation (updating the text position)
301      * @param orientation the new orientation (integer).
302      */
303     public UpdateStatus setOrientation(Integer orientation) {
304         currentOrientation = TipOrientation.intToEnum(orientation);
305         return UpdateStatus.Success;
306     }
307
308     /**
309      * Set the current orientation (updating the text position)
310      * @param orientation the new orientation (TipOrientation enum).
311      */
312     public UpdateStatus setOrientationAsEnum(TipOrientation orientation) {
313         currentOrientation = orientation;
314         return UpdateStatus.Success;
315     }
316
317     /**
318      * Get which components should be displayed.
319      * @return the string containing the coordinates to display.
320      */
321     public String getDisplayComponents() {
322         return displayComponents;
323     }
324
325     /**
326      * Checks if a given string is valid for the display components
327      * @return true if it is valid, false otherwise.
328      */
329     private boolean isValidComponent(String value) {
330         if (value.length() < 1 || value.length() > 3) {
331             return false;
332         }
333
334         boolean isXSet = false;
335         boolean isYSet = false;
336         boolean isZSet = false;
337         for (int i = 0; i < value.length(); ++i) {
338             if (value.charAt(i) == 'x' && !isXSet) {
339                 isXSet = true;
340                 continue;
341             }
342             if (value.charAt(i) == 'y' && !isYSet) {
343                 isYSet = true;
344                 continue;
345             }
346             if (value.charAt(i) == 'z' && !isZSet) {
347                 isZSet = true;
348                 continue;
349             }
350             return false;
351         }
352         return true;
353     }
354
355     /**
356      * Set which components to display if @disp is valid.
357      * @param disp The string containing which components to display
358      */
359     public UpdateStatus setDisplayComponents(String disp) {
360         if (disp == null) {
361             return UpdateStatus.Fail;
362         }
363         disp = disp.toLowerCase();
364         if (!isValidComponent(disp)) {
365             return UpdateStatus.Fail;
366         }
367         displayComponents = disp;
368         updateText();
369         return UpdateStatus.Success;
370     }
371
372
373     public Boolean isAutoOrientationEnabled() {
374         return autoOrientation;
375     }
376
377     public UpdateStatus setAutoOrientation(Boolean status) {
378         autoOrientation = status;
379         return UpdateStatus.Success;
380     }
381
382     /**
383      * Get tip formated text for the given index
384      * @param index the component index
385      * @return the formated string
386      */
387     private String getComponentFormatedText(int index) {
388         switch (displayComponents.charAt(index)) {
389             case 'x':
390                 return "X:" + tipTextFormat.format(getTipDataX());
391             case 'y':
392                 return "Y:" + tipTextFormat.format(getTipDataY());
393             case 'z':
394                 return "Z:" + tipTextFormat.format(getTipDataZ());
395             default:
396                 return "";
397         }
398     }
399
400     /**
401      * Update the text from the datatip base on current tipData value.
402      */
403     public void updateText() {
404         //if display function is empty look in parent
405         //if parent is empty too use default print
406
407         String fnc = getDisplayFunction();
408         if (fnc == null || fnc.equals("")) {
409             //look in parent
410             fnc = (String) GraphicController.getController().getProperty(getParent(), GraphicObjectProperties.__GO_DATATIP_DISPLAY_FNC__);
411             if (fnc == null || fnc.equals("")) {
412                 int numCoords = displayComponents.length();
413                 String[] textArray = new String[numCoords];
414                 for (int i = 0; i < numCoords; ++i) {
415                     textArray[i] = getComponentFormatedText(i);
416                 }
417                 Integer[] dim = new Integer[2];
418                 dim[0] = numCoords;
419                 dim[1] = 1;
420                 setTextArrayDimensions(dim);
421                 setTextStrings(textArray);
422                 return;
423             }
424         }
425
426         String errMsg =  Messages.gettext("Wrong value for '%s' property: A valid function name expected.\n");
427         errMsg = errMsg.replace("'", "''");
428         errMsg = errMsg.replace("\n", "\\n");
429         String updateCommand = "try;" +
430                                "GDZa786XBSq7899SHKp=getcallbackobject(" + getIdentifier() + ");" +
431                                "set(GDZa786XBSq7899SHKp,\"text\"," + fnc + "(GDZa786XBSq7899SHKp));" +
432                                "clear(\"GDZa786XBSq7899SHKp\");" +
433                                "catch;" +
434                                "set(GDZa786XBSq7899SHKp,\"display_function\",\"\");" +
435                                "set(GDZa786XBSq7899SHKp.parent,\"display_function\",\"\");" +
436                                "clear(\"GDZa786XBSq7899SHKp\");" +
437                                "error(msprintf(\"" + errMsg + "\", \"display_function\"));" +
438                                "end;";
439         InterpreterManagement.requestScilabExec(updateCommand);
440     }
441
442     public Boolean getTipBoxMode() {
443         return tipBoxMode;
444     }
445
446     public Boolean getTipLabelMode() {
447         return tipLabelMode;
448     }
449
450     public Boolean getInterpMode() {
451         return interpMode;
452     }
453
454     public String getDisplayFunction() {
455         return displayFnc;
456     }
457
458     public UpdateStatus setTipBoxMode(Boolean mode) {
459         tipBoxMode = mode;
460         setBox(tipBoxMode);
461         return UpdateStatus.Success;
462     }
463
464     public UpdateStatus setTipLabelMode(Boolean mode) {
465         tipLabelMode = mode;
466         return UpdateStatus.Success;
467     }
468
469     public UpdateStatus setInterpMode(Boolean mode) {
470         interpMode = mode;
471         return UpdateStatus.Success;
472     }
473
474     public UpdateStatus setDisplayFunction(String fnc) {
475         displayFnc = fnc;
476         updateText();
477         return UpdateStatus.Success;
478     }
479
480     @Override
481     public Double[] getPosition() {
482         return getTipData();
483     }
484
485     public UpdateStatus setIndexes(Double[] value) {
486         if (value[0].intValue() != dataIndex || !value[1].equals(ratio)) {
487             dataIndex = value[0].intValue();
488             ratio = new Double(value[1]);
489             updateText();
490
491             return UpdateStatus.Success;
492         }
493
494         return UpdateStatus.NoChange;
495     }
496
497     public Integer getIndexes() {
498         return dataIndex;
499     }
500
501     /**
502      * @return Type as String
503      */
504     public Integer getType() {
505         return GraphicObjectProperties.__GO_DATATIP__;
506     }
507 }