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