deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / Collections / Library.sc
blob3bd5f892ff5028fe43829271899b94aa3f19e247
1 MultiLevelIdentityDictionary : Collection
4         var <>dictionary;
6         *new {
7                 ^super.new.init
8         }
9         init {
10                 dictionary = this.newInternalNode;
11         }
13         newInternalNode { ^this.nodeType.new }
15         nodeType {
16                 ^IdentityDictionary;
17         }
19         at { arg ... path;
20                 ^this.atPath(path)
21         }
22         atPathFail { arg path, function;
23                 var item;
24                 item = dictionary;
25                 path.do({ arg name;
26                         item = item.at(name);
27                         if (item.isNil, { ^function.value });
28                 });
29                 ^item
30         }
31         atPath { arg path;
32                 ^this.atPathFail(path)
33         }
35         put { arg ... path;
36                 var item;
37                 item = path.pop;
38                 ^this.putAtPath(path, item);
39         }
40         putAtPath { arg path, val;
41                 var item, lastName;
42                 path = path.copy;
43                 lastName = path.pop;
44                 item = dictionary;
45                 path.do({ arg name;
46                         item = item.atFail(name, {
47                                 var newitem;
48                                 newitem = this.newInternalNode;
49                                 item.put(name, newitem);
50                                 newitem
51                         });
52                 });
53                 item.put(lastName, val);
54         }
56         create { arg ... args;
57                 var item;
58                 item = dictionary;
59                 args.do({ arg name;
60                         item = item.atFail(name, {
61                                 var newitem;
62                                 newitem = this.newInternalNode;
63                                 item.put(name, newitem);
64                                 newitem
65                         });
66                 });
67         }
69         choose { arg ... start;
70                 var item;
71                 if(start.isEmpty,{
72                         item = dictionary;
73                 },{
74                         item = this.at(*start);
75                         if(item.isNil,{
76                                 Error("Library-choose start address not found: " ++ start).throw;
77                         });
78                 });
79                 ^this.prChooseFrom(item);
80         }
81         putTree { arg ... items;
82                 this.prPutTree([],items)
83         }
84         postTree { arg obj,tabs=0;
85                 if(obj.isNil,{ obj = dictionary });
86                 if(obj.isKindOf(this.nodeType),{
87                         "".postln;
88                         obj.keysValuesDo({ arg k,v;
89                                 tabs.do({ Char.tab.post });
90                                 k.post;
91                                 ": ".post;
92                                 this.postTree(v,tabs + 1)
93                         });
94                 },{
95                         Char.tab.post;
96                         obj.asString.postln;
97                 })
98         }
99         do { arg function;
100                 dictionary.do(function);
101         }
102                 // remove only the leaf node indicated by path
103                 // parent nodes remain in the MLID even if they are empty
104         removeAt {
105                 arg ... path;
106                 ^this.removeAtPath(path)
107         }
108         removeAtPath { arg path;
109                 var item, lastName;
110                 path = path.copy;
111                 lastName = path.pop;
112                 item = dictionary;
113                 path.do({ arg name;
114                         item = item.at(name);
115                         if (item.isNil, { ^nil });
116                 });
117                 ^item.removeAt(lastName);
118         }
119                 // remove the leaf node
120                 // as well as parent nodes that become empty after removing the child
121                 // slower but leaves less cruft in the tree
122         prRemoveAtPathRecursive { |path, i = 0, item|
123                 var name = path[i], result;
124                 if(item[name].isNil) { ^nil };
125                 if(i < (path.size-1)) {
126                         result = this.prRemoveAtPathRecursive(path, i+1, item[name]);
127                         (item[name].isEmpty).if({ item.removeAt(name) });
128                         ^result
129                 } {
130                         ^item.removeAt(name)
131                 };
132         }
133         removeEmptyAtPath { arg path;
134                 ^this.prRemoveAtPathRecursive(path, 0, dictionary)
135         }
136         removeEmptyAt { arg ...path;
137                 ^this.prRemoveAtPathRecursive(path, 0, dictionary);
138         }
140         //private
141         add { arg assn;
142                 this.put(assn.key, assn.value);
143         }
144         remove { ^this.shouldNotImplement(thisMethod) }
145         removeFail { ^this.shouldNotImplement(thisMethod) }
147         prChooseFrom { arg dict;
148                 var item;
149                 item = dict.choose;
150                 if(item.isKindOf(this.nodeType),{
151                         ^this.prChooseFrom(item);
152                 },{
153                         ^item
154                 })
155         }
156         prPutTree { arg keys,items;
157                 forBy(0,items.size - 1,2,{ arg i;
158                         var key,item;
159                         key = items.at(i);
160                         item = items.at(i + 1);
161                         if(item.isArray.not,{
162                                 this.put(* keys ++ [key,item]);
163                         },{
164                                 //array
165                                 this.prPutTree(keys ++ [key],item);
166                         })
167                 });
168         }
169         leaves { arg startAt;
170                 if(startAt.isNil,{
171                         startAt = dictionary;
172                 },{
173                         startAt = this.at(*startAt);
174                 });
175                 ^this.prNestedValuesFromDict(startAt);
176         }
177         prNestedValuesFromDict { arg dict;
178                 ^dict.values.collect({ arg thing;
179                         if(thing.isKindOf(this.nodeType),{
180                                 this.prNestedValuesFromDict(thing)
181                         },{
182                                 thing
183                         })
184                 })
185         }
187         // Tree-like do methods
188         leafDo {
189                 arg func;
191                 this.doLeafDo([], this.dictionary, func);
192         }
193         leafDoFrom {
194                 arg folderpath, func;
195                 var folder;
197                 folderpath = folderpath.asArray;
199                 folder = this.atPath(folderpath);
200                 if (folder.notNil && folder.isKindOf(this.nodeType), {
201                         this.doLeafDo(folderpath, folder, func);
202                 });
203         }
205         doLeafDo {
206                 arg path, object, func;
208                 if (object.isKindOf(this.nodeType), {
209                         object.keysValuesDo({
210                                 arg name, subobject;
211                                 this.doLeafDo(path ++ [name], subobject, func)
212                         });
213                 }, {
214                         func.value(path, object);
215                 })
216         }
218         treeDo {
219                 arg branchFunc, leafFunc, argument0, postBranchFunc;
220                 var result;
222                 result = this.doTreeDo([], this.dictionary, branchFunc, leafFunc, argument0, postBranchFunc);
223                 ^result;
224         }
225         treeDoFrom {
226                 arg folderpath, branchFunc, leafFunc, argument0, postBranchFunc;
227                 var folder, result;
229                 folderpath = folderpath.asArray;
231                 folder = this.atPath(folderpath);
232                 if (folder.isKindOf(this.nodeType), {
233                         result = this.doTreeDo(folderpath, folder, branchFunc, leafFunc, argument0, postBranchFunc);
234                 }, {
235                         result = nil;
236                 });
238                 ^result;
240         }
241         doTreeDo {
242                 arg path, object, branchFunc, leafFunc, argument, postBranchFunc;
243                 var result;
245                 if (object.isKindOf(this.nodeType), {
246                         if (branchFunc.notNil, {
247                                 result = branchFunc.value(path, object, argument);
248                         }, {
249                                 result = argument;
250                         });
251                         object.keysValuesDo({
252                                 arg name, subobject;
253                                 this.doTreeDo(path ++ [name], subobject, branchFunc, leafFunc, result, postBranchFunc)
254                         });
255                         if (postBranchFunc.notNil, {
256                                 result = postBranchFunc.value(path, object, result);
257                         });
258                         ^result
259                 }, {
260                         leafFunc.value(path, object, argument);
261                 })
262         }
264         treeCollect { arg branchFunc, leafFunc, postBranchFunc;
265                 var result;
266                 result = this.doTreeCollect([], this.dictionary, branchFunc, leafFunc, postBranchFunc);
267                 ^result;
268         }
269         doTreeCollect { arg path, object, branchFunc, leafFunc, postBranchFunc;
270                 var confirm, collection, result;
272                 if (object.isKindOf(this.nodeType), {
273                         if (branchFunc.notNil, {
274                                 #confirm, result = branchFunc.value(path, object);
275                         }, {
276                                 #confirm, result = [true, nil]
277                         });
278                         if (confirm, {
279                                 collection = [];
280                                 object.keysValuesDo({
281                                         arg name, subobject;
282                                         collection = collection.add(this.doTreeCollect(path ++ [name], subobject,
283                                                 branchFunc, leafFunc, postBranchFunc));
284                                 });
285                                 collection.removeAllSuchThat({arg item; item.isNil});
286                                 if (postBranchFunc.notNil, {
287                                         result = postBranchFunc.value(path, object, collection);
288                                 }, {
289                                         result = nil;
290                                 });
291                                 ^result
292                         }, {
293                                 ^nil
294                         });
295                 }, {
296                         ^leafFunc.value(path, object)
297                 });
298         }
300         sortedTreeDo {
301                 arg branchFunc, leafFunc, argument0, postBranchFunc, sortFunc;
302                 var result;
304                 result = this.doSortedTreeDo([], this.dictionary, branchFunc, leafFunc, argument0, postBranchFunc, sortFunc);
305                 ^result;
306         }
307         doSortedTreeDo {
308                 arg path, object, branchFunc, leafFunc, argument, postBranchFunc, sortFunc;
309                 var result;
311                 sortFunc = sortFunc ? {arg a, b; a < b};
313                 if (object.isKindOf(this.nodeType), {
314                         if (branchFunc.notNil, {
315                                 result = branchFunc.value(path, object, argument);
316                         }, {
317                                 result = argument;
318                         });
319                         object.sortedKeysValuesDo({
320                                 arg name, subobject;
321                                 this.doSortedTreeDo(path ++ [name], subobject, branchFunc, leafFunc, result, postBranchFunc, sortFunc)
322                         });
323                         if (postBranchFunc.notNil, {
324                                 postBranchFunc.value(path, object, result);
325                         });
326                         ^result
327                 }, {
328                         leafFunc.value(path, object, argument);
329                 })
330         }
332         leafDoInBranch {
333                 arg folderpath, function;
334                 var path, folder;
336                 folderpath = folderpath.asArray;
338                 folder = this.atPath(folderpath);
339                 if (folder.notNil && folder.isKindOf(this.nodeType), {
340                         folder.keysValuesDo({
341                                 arg name, object;
342                                 if (object.isKindOf(this.nodeType).not, {
343                                         function.value(folderpath ++ [name], object);
344                                 });
345                         });
346                 });
347         }
349         storeOn { arg stream;
350                 stream << this.class.name << "[" <<<* dictionary << "]"
351         }
353         printOn { arg stream;
354                 stream << this.class.name << "[" <<* dictionary << "]"
355         }
360 LibraryBase : MultiLevelIdentityDictionary
362         *global {
363                 ^this.subclassResponsibility(thisMethod);
364         }
365         *global_ { arg obj;
366                 ^this.subclassResponsibility(thisMethod);
367         }
370         *clear {
371                 this.global = this.new;
372         }
373         *at { arg ... args;
374                 ^this.global.at(*args);
375         }
377         *atList { arg args;
378                 ^this.global.at(*args)
379         }
380         *putList { arg args;
381                 ^this.global.put(*args)
382         }
384         *put { arg ... args;
385                 ^this.global.put(*args)
386         }
387         *create { arg ... args;
388                 ^this.global.create(*args)
389         }
391         *postTree {
392                 this.global.postTree
393         }
397 Library : LibraryBase
399         classvar global;
401         *global { ^global }
402         *global_ { arg obj; global = obj; }
404         *initClass {
405                 global = this.new;
406         }
409 Archive : LibraryBase
411         classvar global;
412         classvar <>archiveDir;
414         *global { ^global }
415         *global_ { arg obj; global = obj; }
417         *initClass {
418                 global = this.new;
419                 archiveDir = Platform.userAppSupportDir;
420         }
422         *read { arg filename;
423                 var expandedFileName;
424                 expandedFileName = filename ?? (archiveDir ++ "/archive.sctxar");
425                 if (File.exists(expandedFileName)) {
426                         if (expandedFileName.endsWith(".scar")) {
427                                 global = this.readBinaryArchive(expandedFileName);
428                         }{
429                                 global = this.readArchive(expandedFileName);
430                         };
431                         if (global.isNil) {
432                                 global = this.new;
433                         };
434                 }
435         }
436         *write { arg filename;
437                 var expandedFileName;
438                 expandedFileName = filename ?? (archiveDir ++ "/archive.sctxar");
439                 global.writeArchive(expandedFileName);
440         }