1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5     ---------------------------------------------------------------------
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  ************************************************************************
20  ************************************************************************/
21 
22 #include "uitree.hh"
23 #include <sstream>
24 #include "exception.hh"
25 #include "global.hh"
26 
27 static Tree makeSubFolderChain(Tree path, Tree elem);
28 static Tree putFolder(Tree folder, Tree item);
29 static Tree getFolder(Tree folder, Tree ilabel);
30 
error(const char * s,Tree t)31 static void error(const char* s, Tree t)
32 {
33     fprintf(stderr, "ERROR : %s (%p)\n", s, (void*)t);
34 }
35 
36 #define FAUST_ERROR(s, t)        \
37     {                            \
38         error(s, t);             \
39         throw faustexception(s); \
40     }
41 
42 //------------------------------------------------------------------------------
43 // Property list
44 //------------------------------------------------------------------------------
45 
46 #if 0
47 
48 // version normale, qui marche, mais qui ne range pas en ordre alphabetique
49 static bool findKey(Tree pl, Tree key, Tree& val)
50 {
51 	if (isNil(pl)) 				return false;
52 	if (left(hd(pl)) == key) 	{ val = right(hd(pl)); return true; }
53 	/*  left(hd(pl)) != key	*/	return findKey (tl(pl), key, val);
54 }
55 
56 static Tree updateKey(Tree pl, Tree key, Tree val)
57 {
58 	if (isNil(pl)) 				return cons ( cons(key,val), gGlobal->nil );
59 	if (left(hd(pl)) == key) 	return cons ( cons(key,val), tl(pl) );
60 	/*  left(hd(pl)) != key	*/	return cons ( hd(pl), updateKey( tl(pl), key, val ));
61 }
62 
63 static Tree removeKey(Tree pl, Tree key)
64 {
65 	if (isNil(pl)) 				return gGlobal->nil;
66 	if (left(hd(pl)) == key) 	return tl(pl);
67 	/*  left(hd(pl)) != key	*/	return cons (hd(pl), removeKey(tl(pl), key));
68 }
69 
70 #else
71 
72 // version experimentale qui range en ordre alphabetique
73 
isBefore(Tree k1,Tree k2)74 static bool isBefore(Tree k1, Tree k2)
75 {
76     // before comparing replace (type . label) by label
77     if (isList(k1)) {
78         k1 = tl(k1);
79     }
80     if (isList(k2)) {
81         k2 = tl(k2);
82     }
83 
84     // fprintf(stderr, "isBefore("); print(k1, stderr); fprintf(stderr,", "); print(k2, stderr); fprintf(stderr,")\n");
85     Sym s1, s2;
86     if (!isSym(k1->node(), &s1)) {
87         FAUST_ERROR("the node of the tree is not a symbol", k1);
88     }
89     if (!isSym(k2->node(), &s2)) {
90         FAUST_ERROR("the node of the tree is not a symbol", k2);
91     }
92 
93     // fprintf (stderr, "strcmp(\"%s\", \"%s\") = %d\n", name(s1), name(s2), strcmp(name(s1), name(s2)));
94     return strcmp(name(s1), name(s2)) < 0;
95 }
96 
findKey(Tree pl,Tree key,Tree & val)97 static bool findKey(Tree pl, Tree key, Tree& val)
98 {
99     if (isNil(pl)) return false;
100     if (left(hd(pl)) == key) {
101         val = right(hd(pl));
102         return true;
103     }
104     if (isBefore(left(hd(pl)), key)) return findKey(tl(pl), key, val);
105     return false;
106 }
107 
updateKey(Tree pl,Tree key,Tree val)108 static Tree updateKey(Tree pl, Tree key, Tree val)
109 {
110     if (isNil(pl)) return cons(cons(key, val), gGlobal->nil);
111     if (left(hd(pl)) == key) return cons(cons(key, val), tl(pl));
112     if (isBefore(left(hd(pl)), key)) return cons(hd(pl), updateKey(tl(pl), key, val));
113     return cons(cons(key, val), pl);
114 }
115 
116 /**
117  * Like updateKey but allow multiple items with same key
118  */
addKey(Tree pl,Tree key,Tree val)119 static Tree addKey(Tree pl, Tree key, Tree val)
120 {
121     if (isNil(pl)) return cons(cons(key, val), gGlobal->nil);
122     if (isBefore(key, left(hd(pl)))) return cons(cons(key, val), pl);
123     return cons(hd(pl), addKey(tl(pl), key, val));
124 }
125 
126 #if 0
127 static Tree removeKey(Tree pl, Tree key)
128 {
129 	if (isNil(pl)) 					return gGlobal->nil;
130 	if (left(hd(pl)) == key) 		return tl(pl);
131 	if (isBefore(left(hd(pl)),key))	return cons (hd(pl), removeKey(tl(pl), key));
132 	return pl;
133 }
134 #endif
135 #endif
136 
137 //------------------------------------------------------------------------------
138 // gestion de la construction de l'arbre d'interface utilisateur
139 //------------------------------------------------------------------------------
140 
uiFolder(Tree label,Tree elements)141 Tree uiFolder(Tree label, Tree elements)
142 {
143     return tree(gGlobal->UIFOLDER, label, elements);
144 }
isUiFolder(Tree t)145 bool isUiFolder(Tree t)
146 {
147     return isTree(t, gGlobal->UIFOLDER);
148 }
isUiFolder(Tree t,Tree & label,Tree & elements)149 bool isUiFolder(Tree t, Tree& label, Tree& elements)
150 {
151     return isTree(t, gGlobal->UIFOLDER, label, elements);
152 }
153 
uiWidget(Tree label,Tree varname,Tree sig)154 Tree uiWidget(Tree label, Tree varname, Tree sig)
155 {
156     return tree(gGlobal->UIWIDGET, label, varname, sig);
157 }
isUiWidget(Tree t,Tree & label,Tree & varname,Tree & sig)158 bool isUiWidget(Tree t, Tree& label, Tree& varname, Tree& sig)
159 {
160     return isTree(t, gGlobal->UIWIDGET, label, varname, sig);
161 }
162 
163 // place un item dans un folder. Remplace eventuellement l'element de meme nom.
putFolder(Tree folder,Tree item)164 Tree putFolder(Tree folder, Tree item)
165 {
166     Tree label, content;
167 
168     if (!isUiFolder(folder, label, content)) {
169         fprintf(stderr, "ERROR in addFolder : not a folder\n");
170     }
171     return uiFolder(label, updateKey(content, uiLabel(item), item));
172 }
173 
174 // place un item dans un folder. Sans Remplacement
addToFolder(Tree folder,Tree item)175 Tree addToFolder(Tree folder, Tree item)
176 {
177     Tree label, content;
178 
179     if (!isUiFolder(folder, label, content)) {
180         fprintf(stderr, "ERROR in addFolder : not a folder\n");
181     }
182     return uiFolder(label, addKey(content, uiLabel(item), item));
183 }
184 
185 // get an item from a folder (or return NIL)
getFolder(Tree folder,Tree ilabel)186 Tree getFolder(Tree folder, Tree ilabel)
187 {
188     Tree flabel, content, item;
189     if (!isUiFolder(folder, flabel, content)) {
190         fprintf(stderr, "ERROR in getFolder : not a folder\n");
191     }
192     if (findKey(content, ilabel, item)) {
193         return item;
194     } else {
195         return gGlobal->nil;
196     }
197 }
198 
199 // cree une chaine de dossiers correspondant a path et contenant in fine elem
makeSubFolderChain(Tree path,Tree elem)200 Tree makeSubFolderChain(Tree path, Tree elem)
201 {
202     if (isNil(path)) {
203         return elem;
204     } else {
205         return putFolder(uiFolder(hd(path), gGlobal->nil), makeSubFolderChain(tl(path), elem));
206     }
207 }
208 
putSubFolder(Tree folder,Tree path,Tree item)209 Tree putSubFolder(Tree folder, Tree path, Tree item)
210 {
211     if (isNil(path)) {
212         // return putFolder(folder, item);
213         return addToFolder(folder, item);
214     } else {
215         Tree subfolder = getFolder(folder, hd(path));
216         if (isUiFolder(subfolder)) {
217             return putFolder(folder, putSubFolder(subfolder, tl(path), item));
218         } else {
219             return putFolder(folder, makeSubFolderChain(path, item));
220         }
221     }
222 }
223 
224 /*
225 Fonctionnement des dossiers.
226 Dossier a 1 niveau : Un dossier contient une liste de choses reperees par un nom :
227     Dossier[(l1,d1)...(ln,dn)]
228 ou (lx,dx) est une chose dx reperee par un nom lx. On suppose les lx tous differents
229 
230 On peut ajouter une chose a un dossier : Ajouter(Dossier, Chose) -> Dossier
231 
232 Si le dossier contient deja qq chose de meme nom, cette chose est remplacee par la nouvelle.
233 
234 AJOUTER (Dossier[(l1,d1)...(ln,dn)], (lx,dx)) -> Dossier[(l1,d1)...(lx,dx)...(ln,dn)]
235 
236 AJOUTER (Dossier[(l1,d1)...(lx,dx)...(ln,dn)], (lx,dx')) -> Dossier[(l1,d1)...(lx,dx')...(ln,dn)]
237 */
238 
239 // Handle empty labels in a consistent way
ptrToHex(Tree ptr)240 string ptrToHex(Tree ptr)
241 {
242     stringstream res;
243     res << hex << ptr;
244     return res.str();
245 }
246 
checkNullLabel(Tree t,const string & label,bool bargraph)247 string checkNullLabel(Tree t, const string& label, bool bargraph)
248 {
249     return (label == "") ? (bargraph ? ptrToHex(t) : string("0x00")) : label;
250 }
251