Add full options management to figure
[scilab.git] / scilab / modules / gui / sci_gateway / c / sci_figure.c
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2014 - Scilab Enterprises - Antoine ELIAS
4  * Copyright (C) 2014 - Scilab Enterprises - Bruno JOFRET
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 #include "gw_gui.h"
15 #include "MALLOC.h"
16 #include "api_scilab.h"
17 #include "localization.h"
18 #include "Scierror.h"
19 #include "createGraphicObject.h"
20 #include "BuildObjects.h"
21 #include "setGraphicObjectProperty.h"
22 #include "getGraphicObjectProperty.h"
23 #include "graphicObjectProperties.h"
24 #include "CurrentFigure.h"
25 #include "CurrentSubwin.h"
26 #include "FigureList.h"
27 #include "HandleManagement.h"
28 #include "SetHashTable.h"
29 #include "stricmp.h"
30 #ifdef _MSC_VER
31 #include "strdup_windows.h"
32 #endif
33
34 #include "sciprint.h"
35
36 #define COLOR_COMPONENT 3
37 /*--------------------------------------------------------------------------*/
38 void setDefaultProperties(int _iFig);
39 int addColor(int _iFig, double* _pdblColor);
40 int cloneGDFWithId(int _iId);
41 /*--------------------------------------------------------------------------*/
42 int sci_figure(char * fname, unsigned long fname_len)
43 {
44     SciErr sciErr;
45     int* piAddr = NULL;
46     int iFig = 0;
47     int iRhs = nbInputArgument(pvApiCtx);
48     int iId = 0;
49     int iPos = 0;
50     int i = 0;
51     int iNewId = -1;
52     int iAxes = 0;
53     int* piAxes = &iAxes;
54     int iPropertyOffset = 0;
55     BOOL bDoCreation = TRUE;
56     BOOL bVisible = TRUE; // Create a visible figure by default
57     BOOL bDockable = TRUE; // Create a dockable figure by default
58     BOOL bDefaultAxes = TRUE; // Create an Axes by default
59     int iMenubarType = 1; // Create a 'figure' menubar by default
60     int iToolbarType = 1; // Create a 'figure' toolbar by default
61     double dblId = 0;
62
63     //figure(num) -> scf(num)
64     //figure() -> scf()
65
66     //figure(x, "...", ...)
67
68     // figure()
69     if (iRhs == 0) // Auto ID
70     {
71         //get highest value of winsid to create the new windows @ + 1
72         int nbFigure = sciGetNbFigure();
73
74         if (nbFigure)
75         {
76             int * ids = (int*)MALLOC(nbFigure * sizeof(int));
77
78             if (ids == NULL)
79             {
80                 Scierror(999, _("%s: No more memory.\n"), fname);
81                 return 0;
82             }
83             sciGetFiguresId(ids);
84
85             //find highest value
86             for (i = 0 ; i < nbFigure ; i++)
87             {
88                 if (ids[i] > iNewId)
89                 {
90                     iNewId = ids[i];
91                 }
92             }
93         }
94         //use next value
95         iNewId = iNewId + 1;
96         iFig = cloneGDFWithId(iNewId);
97         createScalarHandle(pvApiCtx, iRhs + 1, getHandle(iFig));
98         AssignOutputVariable(pvApiCtx, 1) = iRhs + 1;
99         ReturnArguments(pvApiCtx);
100         return 0;
101     }
102
103     if (iRhs == 1)
104     {
105         int iNewId = -1;
106         int iAxes = 0;
107         int* piAxes = &iAxes;
108         //figure(x);
109         sciErr = getVarAddressFromPosition(pvApiCtx, 1, &piAddr);
110         if (sciErr.iErr)
111         {
112             printError(&sciErr, 0);
113             Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, 1);
114             return 0;
115         }
116
117         if (isVarMatrixType(pvApiCtx, piAddr) == 0)
118         {
119             Scierror(999, _("%s: Wrong type for input argument #%d: An integer value expected.\n"), fname, 1);
120             return 0;
121         }
122
123         if (getScalarDouble(pvApiCtx, piAddr, &dblId))
124         {
125             Scierror(999, _("%s: No more memory.\n"), fname);
126             return 0;
127         }
128
129         iId = (int)(dblId + 0.5); //avoid 1.999 -> 1
130
131         //get current fig from id
132         iFig = getFigureFromIndex(iId);
133         if (iFig == 0) // Figure does not exists, create a new one
134         {
135             iFig = cloneGDFWithId(iId);
136         }
137         setCurrentFigure(iFig);
138         getGraphicObjectProperty(iFig, __GO_SELECTED_CHILD__, jni_int,  (void**)&piAxes);
139         setCurrentSubWin(iAxes);
140         createScalarHandle(pvApiCtx, iRhs + 1, getHandle(iFig));
141         AssignOutputVariable(pvApiCtx, 1) = iRhs + 1;
142         ReturnArguments(pvApiCtx);
143         return 0;
144     }
145
146     // Prepare property analysis
147     if (iRhs % 2 == 0)
148     {
149         //get highest value of winsid to create the new windows @ + 1
150         int nbFigure = sciGetNbFigure();
151         iPos = 0;
152
153         if (nbFigure)
154         {
155             int * ids = (int*)MALLOC(nbFigure * sizeof(int));
156
157             if (ids == NULL)
158             {
159                 Scierror(999, _("%s: No more memory.\n"), fname);
160                 return 0;
161             }
162             sciGetFiguresId(ids);
163
164             //find highest value
165             for (i = 0 ; i < nbFigure ; i++)
166             {
167                 if (ids[i] > iNewId)
168                 {
169                     iNewId = ids[i];
170                 }
171             }
172         }
173         //use next value
174         iNewId = iNewId + 1;
175     }
176     else
177     {
178         iPos = 1;
179         //figure(x, ...);
180         sciErr = getVarAddressFromPosition(pvApiCtx, 1, &piAddr);
181         if (sciErr.iErr)
182         {
183             printError(&sciErr, 0);
184             Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, 1);
185             return 0;
186         }
187
188         if (isVarMatrixType(pvApiCtx, piAddr) == 0)
189         {
190             Scierror(999, _("%s: Wrong type for input argument #%d: An integer value expected.\n"), fname, 1);
191             return 0;
192         }
193
194         if (getScalarDouble(pvApiCtx, piAddr, &dblId))
195         {
196             Scierror(999, _("%s: No more memory.\n"), fname);
197             return 0;
198         }
199
200         iNewId = (int)(dblId + 0.5); //avoid 1.999 -> 1
201         //get current fig from id
202         iFig = getFigureFromIndex(iId);
203         if (iFig != 0) // Figure already exists
204         {
205             bDoCreation = TRUE;
206         }
207     }
208
209     if (bDoCreation)
210     {
211         int* piAddrProp = NULL;
212         char* pstProName = NULL;
213         int* piAddrData = NULL;
214         char* pstPropVal = NULL;
215         for (i = iPos + 1 ; i <= iRhs ; i += 2)
216         {
217             //get property name
218             sciErr = getVarAddressFromPosition(pvApiCtx, i, &piAddrProp);
219             if (sciErr.iErr)
220             {
221                 Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, i);
222                 return 1;
223             }
224
225             if (getAllocatedSingleString(pvApiCtx, piAddrProp, &pstProName))
226             {
227                 Scierror(999, _("%s: Wrong size for input argument #%d: A single string expected.\n"), fname, i);
228                 return 1;
229             }
230             if (stricmp(pstProName, "dockable") != 0
231                 && stricmp(pstProName, "toolbar") != 0
232                 && stricmp(pstProName, "menubar") != 0
233                 && stricmp(pstProName, "default_axes") != 0
234                 && stricmp(pstProName, "visible") != 0 )
235             {
236                 freeAllocatedSingleString(pstProName);
237                 continue;
238             }
239             //get address of value on stack
240             sciErr = getVarAddressFromPosition(pvApiCtx, i + 1, &piAddrData);
241             if (sciErr.iErr)
242             {
243                 Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, i + 1);
244                 return 1;
245             }
246             if (getAllocatedSingleString(pvApiCtx, piAddrData, (char**)&pstPropVal))
247             {
248                 Scierror(999, _("%s: Wrong size for input argument #%d: A single string expected.\n"), fname, 3);
249                 return 1;
250             }
251             //check property value to compatibility
252             if (stricmp(pstProName, "dockable") == 0)
253             {
254                 if (stricmp(pstPropVal, "on") == 0)
255                 {
256                     bDockable = TRUE;
257                 }
258                 else if (stricmp(pstPropVal, "off") == 0)
259                 {
260                     bDockable = FALSE;
261                 }
262                 else
263                 {
264                     Scierror(999, _("Wrong value for '%s' property: '%s' or '%s' expected."), "dockable", "on", "off");
265                     freeAllocatedSingleString(pstProName);
266                     freeAllocatedSingleString(pstPropVal);
267                     return 1;
268                 }
269             }
270             else if (stricmp(pstProName, "toolbar") == 0)
271             {
272                 if (stricmp(pstPropVal, "none") == 0)
273                 {
274                     iToolbarType = 0;
275                 }
276                 else if (stricmp(pstPropVal, "figure") == 0)
277                 {
278                     iToolbarType = 1;
279                 }
280                 else
281                 {
282                     Scierror(999, _("Wrong value for '%s' property: '%s' or '%s' expected."), "toolbar", "none", "figure");
283                     freeAllocatedSingleString(pstProName);
284                     freeAllocatedSingleString(pstPropVal);
285                     return 1;
286                 }
287             }
288             else if (stricmp(pstProName, "menubar") == 0)
289             {
290                 if (stricmp(pstPropVal, "none") == 0)
291                 {
292                     iMenubarType = 0;
293                 }
294                 else if (stricmp(pstPropVal, "figure") == 0)
295                 {
296                     iMenubarType = 1;
297                 }
298                 else
299                 {
300                     Scierror(999, _("Wrong value for '%s' property: '%s' or '%s' expected."), "menubar", "none", "figure");
301                     freeAllocatedSingleString(pstProName);
302                     freeAllocatedSingleString(pstPropVal);
303                     return 1;
304                 }
305             }
306             else if (stricmp(pstProName, "default_axes") == 0)
307             {
308                 if (stricmp(pstPropVal, "on") == 0)
309                 {
310                     bDefaultAxes = TRUE;
311                 }
312                 else if (stricmp(pstPropVal, "off") == 0)
313                 {
314                     bDefaultAxes = FALSE;
315                 }
316                 else
317                 {
318                     Scierror(999, _("Wrong value for '%s' property: '%s' or '%s' expected."), "default_axes", "on", "off");
319                     freeAllocatedSingleString(pstProName);
320                     freeAllocatedSingleString(pstPropVal);
321                     return 1;
322                 }
323             }
324             else if (stricmp(pstProName, "visible") == 0)
325             {
326                 if (stricmp(pstPropVal, "on") == 0)
327                 {
328                     bVisible = TRUE;
329                 }
330                 else if (stricmp(pstPropVal, "off") == 0)
331                 {
332                     bVisible = FALSE;
333                 }
334                 else
335                 {
336                     Scierror(999, _("Wrong value for '%s' property: '%s' or '%s' expected."), "visible", "on", "off");
337                     freeAllocatedSingleString(pstProName);
338                     freeAllocatedSingleString(pstPropVal);
339                     return 1;
340                 }
341             }
342             freeAllocatedSingleString(pstPropVal);
343         }
344         iFig = createFigure(bDockable, iMenubarType, iToolbarType, bDefaultAxes, bVisible);
345         setGraphicObjectProperty(iFig, __GO_ID__, &iNewId, jni_int, 1);
346
347     }
348
349     //set(iFig, iPos, iPos + 1)
350     for (i = iPos + 1 ; i <= iRhs ; i += 2)
351     {
352         int isMatrixOfString = 0;
353         int* piAddrProp = NULL;
354         char* pstProName = NULL;
355         int* piAddrData = NULL;
356         int iRows = 0;
357         int iCols = 0;
358         void* _pvData = NULL;
359         int iType = 0;
360
361         //get property name
362         sciErr = getVarAddressFromPosition(pvApiCtx, i, &piAddrProp);
363         if (sciErr.iErr)
364         {
365             Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, i);
366             return 1;
367         }
368
369         if (getAllocatedSingleString(pvApiCtx, piAddrProp, &pstProName))
370         {
371             Scierror(999, _("%s: Wrong size for input argument #%d: A single string expected.\n"), fname, i);
372             return 1;
373         }
374
375         if (bDoCreation &&
376                 (stricmp(pstProName, "dockable") == 0 ||
377                  stricmp(pstProName, "menubar") == 0 ||
378                  stricmp(pstProName, "toolbar") == 0))
379         {
380             // Already set creating new figure
381             // but let the set_ function fail if figure already exists
382             continue;
383         }
384
385         //check property value to compatibility
386         if (stricmp(pstProName, "backgroundcolor") == 0)
387         {
388             freeAllocatedSingleString(pstProName);
389             pstProName = strdup("background");
390         }
391         else if (stricmp(pstProName, "foregroundcolor") == 0)
392         {
393             freeAllocatedSingleString(pstProName);
394             pstProName = strdup("foreground");
395         }
396
397         //get address of value on stack
398         sciErr = getVarAddressFromPosition(pvApiCtx, i + 1, &piAddrData);
399         if (sciErr.iErr)
400         {
401             Scierror(999, _("%s: Can not read input argument #%d.\n"), fname, i + 1);
402             return 1;
403         }
404
405         getVarType(pvApiCtx, piAddrData, &iType);
406
407         if ((strcmp(pstProName, "user_data") == 0) || (stricmp(pstProName, "userdata") == 0))
408         {
409             /* in this case set_user_data_property
410              * directly uses the  third position in the stack
411              * to get the variable which is to be set in
412              * the user_data property (any data type is allowed) S. Steer */
413             _pvData = (void*)piAddrData;         /*position in the stack */
414             iRows = -1;   /*unused */
415             iCols = -1;   /*unused */
416             iType = -1;
417         }
418         else
419         {
420             switch (iType)
421             {
422                 case sci_matrix :
423                     getMatrixOfDouble(pvApiCtx, piAddrData, &iRows, &iCols, (double**)&_pvData);
424                     break;
425                 case sci_boolean :
426                     getMatrixOfBoolean(pvApiCtx, piAddrData, &iRows, &iCols, (int**)&_pvData);
427                     break;
428                 case sci_handles :
429                     getMatrixOfHandle(pvApiCtx, piAddrData, &iRows, &iCols, (long long**)&_pvData);
430                     break;
431                 case sci_strings :
432                     if (   strcmp(pstProName, "tics_labels") != 0 && strcmp(pstProName, "auto_ticks") != 0 &&
433                             strcmp(pstProName, "axes_visible") != 0 && strcmp(pstProName, "axes_reverse") != 0 &&
434                             strcmp(pstProName, "text") != 0 && stricmp(pstProName, "string") != 0 &&
435                             stricmp(pstProName, "tooltipstring") != 0) /* Added for uicontrols */
436                     {
437                         if (getAllocatedSingleString(pvApiCtx, piAddrData, (char**)&_pvData))
438                         {
439                             Scierror(999, _("%s: Wrong size for input argument #%d: A single string expected.\n"), fname, 3);
440                             return 1;
441                         }
442                         iRows = (int)strlen((char*)_pvData);
443                         iCols = 1;
444                     }
445                     else
446                     {
447                         isMatrixOfString = 1;
448                         getAllocatedMatrixOfString(pvApiCtx, piAddrData, &iRows, &iCols, (char***)&_pvData);
449                     }
450                     break;
451                 case sci_list :
452                     iCols = 1;
453                     getListItemNumber(pvApiCtx, piAddrData, &iRows);
454                     _pvData = (void*)piAddrData;         /* In this case l3 is the list position in stack */
455                     break;
456                 default :
457                     _pvData = (void*)piAddrData;         /* In this case l3 is the list position in stack */
458                     break;
459             }
460
461             callSetProperty(pvApiCtx, iFig, _pvData, iType, iRows, iCols, pstProName);
462             if (iType == sci_strings)
463             {
464                 //free allacted data
465                 if (isMatrixOfString == 1)
466                 {
467                     freeAllocatedMatrixOfString(iRows, iCols, (char**)_pvData);
468                 }
469                 else
470                 {
471                     freeAllocatedSingleString((char*)_pvData);
472                 }
473             }
474         }
475     }
476
477     //return new created fig
478     createScalarHandle(pvApiCtx, iRhs + 1, getHandle(iFig));
479     AssignOutputVariable(pvApiCtx, 1) = iRhs + 1;
480     ReturnArguments(pvApiCtx);
481     return 0;
482 }
483 /*--------------------------------------------------------------------------*/
484 int cloneGDFWithId(int _iID)
485 {
486     int iAxes;
487     int *piAxes = &iAxes;
488     //create a new window with id = iNewId
489     int iFig = createNewFigureWithAxes();
490     setGraphicObjectProperty(iFig, __GO_ID__, &_iID, jni_int, 1);
491     setCurrentFigure(iFig);
492
493     getGraphicObjectProperty(iFig, __GO_SELECTED_CHILD__, jni_int,  (void**)&piAxes);
494     setCurrentSubWin(iAxes);
495
496     //setting up new figure
497     setDefaultProperties(iFig);
498
499     return iFig;
500 }
501 /*--------------------------------------------------------------------------*/
502 void setDefaultProperties(int _iFig)
503 {
504     //get figure axes
505     int iAxes = getOrCreateDefaultSubwin();
506     int iDrawing = 0;
507     int iColorIndex = 0;
508     int iFilled = 0;
509     int iAxesVisible = 0;
510     double pdblNewColor[COLOR_COMPONENT] = {0.8, 0.8, 0.8};
511
512     setGraphicObjectProperty(_iFig, __GO_IMMEDIATE_DRAWING__, &iDrawing, jni_bool, 1);
513
514     iColorIndex = addColor(_iFig, pdblNewColor);
515
516     //set background in figure and axes to new ( or existting ) color
517     setGraphicObjectProperty(_iFig, __GO_BACKGROUND__, &iColorIndex, jni_int, 1);
518     setGraphicObjectProperty(iAxes, __GO_BACKGROUND__, &iColorIndex, jni_int, 1);
519
520     //a.filled = "off"
521     setGraphicObjectProperty(iAxes, __GO_FILLED__, &iFilled, jni_bool, 1);
522
523     //a.axes_visible = "off"
524     setGraphicObjectProperty(iAxes, __GO_X_AXIS_VISIBLE__, &iAxesVisible, jni_bool, 1);
525     setGraphicObjectProperty(iAxes, __GO_Y_AXIS_VISIBLE__, &iAxesVisible, jni_bool, 1);
526     setGraphicObjectProperty(iAxes, __GO_Z_AXIS_VISIBLE__, &iAxesVisible, jni_bool, 1);
527
528     //f.immediate_drawing = "on"
529     iDrawing = 1;
530     setGraphicObjectProperty(_iFig, __GO_IMMEDIATE_DRAWING__, &iDrawing, jni_bool, 1);
531 }
532 /*--------------------------------------------------------------------------*/
533 int addColor(int _iFig, double* _pdblNewColor)
534 {
535     int iColorIndex = 0;
536     int i = 0, j = 0;
537     int iColorMapSize = 0;
538     int* piColorMapSize = &iColorMapSize;
539     double* pdblColorMap = NULL;
540     //for new figure, we have to set figure and axes background to [0.8 0.8 0.8]
541     //to do that, we have to update figure.colormap to add new color if not exist.
542     //or get index of color in current color_map
543
544     //first get figure.color_map
545     getGraphicObjectProperty(_iFig, __GO_COLORMAP_SIZE__, jni_int, (void**)&piColorMapSize);
546     getGraphicObjectProperty(_iFig, __GO_COLORMAP__, jni_double_vector, (void **)&pdblColorMap);
547
548     //check if newColor already in coloMap
549     for (i = 0 ; i < iColorMapSize ; i++)
550     {
551         BOOL bFound = TRUE;
552         double* pdblCurrentColor = pdblColorMap + i;
553         for (j = 0 ; j < COLOR_COMPONENT ; j++)
554         {
555             if (*(pdblCurrentColor + j * iColorMapSize) != _pdblNewColor[j])
556             {
557                 bFound = FALSE;
558                 break;
559             }
560         }
561
562         if (bFound)
563         {
564             iColorIndex = i + 1;
565             break;
566         }
567     }
568
569     //not found in current color map
570     if (iColorIndex == 0)
571     {
572         int iNewColorMapSize = (iColorMapSize + 1) * COLOR_COMPONENT;
573         double* pdblNewColorMap = (double*)MALLOC(sizeof(double) * iNewColorMapSize);
574         //we have to add the new color at the end of the current color map
575         for (i = 0 ; i < COLOR_COMPONENT ; i++)
576         {
577             memcpy(pdblNewColorMap + i * (iColorMapSize + 1),
578                    pdblColorMap + (i * iColorMapSize),
579                    iColorMapSize * sizeof(double));
580
581             pdblNewColorMap[i * (iColorMapSize + 1) + iColorMapSize] = _pdblNewColor[i];
582         }
583
584         setGraphicObjectProperty(_iFig, __GO_COLORMAP__, pdblNewColorMap, jni_double_vector, iNewColorMapSize);
585
586         iColorIndex = iColorMapSize + 1;
587     }
588     return iColorIndex;
589 }
590 /*--------------------------------------------------------------------------*/