HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / Common / Control / ResponseDefs.sc
blob8d3093d4df176eb255f33cce629d23e496906f23
1 AbstractResponderFunc {
2         classvar allFuncProxies;
3         var <func, <srcID, <enabled = false, <dispatcher, <permanent = false;
4         
5         *initClass { Class.initClassTree(AbstractDispatcher); allFuncProxies = IdentitySet.new; }
6         
7         enable { 
8                 if(enabled.not, {
9                         if(permanent.not, { CmdPeriod.add(this) });
10                         dispatcher.add(this);
11                         enabled = true;
12                         allFuncProxies.add(this);
13                 });
14         } 
15         
16         disable { 
17                 if(permanent.not, { CmdPeriod.remove(this) });
18                 dispatcher.remove(this);
19                 enabled = false;
20         }
21         
22         prFunc_ {|newFunc|  
23                 func = newFunc; 
24                 this.changed(\function);
25         }
26         
27         add {|newFunc| func = func.addFunc(newFunc); this.changed(\function); }
28         
29         remove {|removeFunc| func = func.removeFunc(removeFunc); this.changed(\function); }
30         
31         gui { this.subclassResponsibility(thisMethod) }
32         
33         cmdPeriod { this.free }
34         
35         oneShot {
36                 var oneShotFunc, wrappedFunc;
37                 wrappedFunc = func;
38                 oneShotFunc = { arg ...args; wrappedFunc.value(*args); this.free };
39                 this.prFunc_(oneShotFunc);
40         }
41         
42         permanent_{|bool| 
43                 permanent = bool;
44                 if(bool && enabled, { CmdPeriod.remove(this) }, {CmdPeriod.add(this) })
45         }
46         
47         fix { this.permanent_(true) }
48         
49         free { allFuncProxies.remove(this); this.disable }
50         
51         clear { this.prFunc_(nil) }
52         
53         *allFuncProxies { 
54                 var result;
55                 result = IdentityDictionary.new;
56                 allFuncProxies.do({|funcProxy|
57                         var key;
58                         key = funcProxy.dispatcher.typeKey;
59                         result[key] = result[key].add(funcProxy)
60                 });
61                 ^result;
62         }
63         
64         *allEnabled { 
65                 var result;
66                 result = IdentityDictionary.new;
67                 allFuncProxies.select(_.enabled).do({|funcProxy|
68                         var key;
69                         key = funcProxy.dispatcher.typeKey;
70                         result[key] = result[key].add(funcProxy)
71                 });
72                 ^result;        
73         }
74         
75         *allDisabled { 
76                 var result;
77                 result = IdentityDictionary.new;
78                 allFuncProxies.reject(_.enabled).do({|funcProxy|
79                         var key;
80                         key = funcProxy.dispatcher.typeKey;
81                         result[key] = result[key].add(funcProxy)
82                 });
83                 ^result;        
84         }
85         
88 // defines the required interface
89 AbstractDispatcher {
90         classvar <>all;
91         var registered = false;
92         
93         *new { ^super.new.init; }
94         
95         init { all.add(this);}
96                 
97         *initClass { all = IdentitySet.new }
98         
99         add {|funcProxy| this.subclassResponsibility(thisMethod) } // proxies call this to add themselves to this dispatcher; should register this if needed
100         
101         remove {|funcProxy| this.subclassResponsibility(thisMethod) } // proxies call this to remove themselves from this dispatcher; should unregister if needed
102                 
103         value { this.subclassResponsibility(thisMethod) }
104         
105         valueArray {arg args; ^this.value(*args) } // needed to work in FunctionLists
106         
107         register { this.subclassResponsibility(thisMethod) } // register this dispatcher to listen for its message type
108         
109         unregister { this.subclassResponsibility(thisMethod) } // unregister this dispatcher so it no longer listens
110         
111         free { this.unregister; all.remove(this) } // I'm done
112         
113         typeKey { this.subclassResponsibility(thisMethod) } // a Symbol
114         
115         update { } // code here to update any changed state in this dispatcher's proxies, e.g. a new function; default does nothing
119 // basis for the default dispatchers
120 // uses function wrappers for matching
121 AbstractWrappingDispatcher :  AbstractDispatcher {
122         var active, <wrappedFuncs;
123         
124         init { super.init; active = IdentityDictionary.new; wrappedFuncs = IdentityDictionary.new; }
125         
126         add {|funcProxy| 
127                 var func, keys;
128                 funcProxy.addDependant(this);
129                 func = this.wrapFunc(funcProxy);
130                 wrappedFuncs[funcProxy] = func;
131                 keys = this.getKeysForFuncProxy(funcProxy);
132                 keys.do({|key| active[key] = active[key].addFunc(func) }); // support multiple keys
133                 if(registered.not, {this.register});
134         }
135         
136         remove {|funcProxy|
137                 var func, keys;
138                 funcProxy.removeDependant(this);
139                 keys = this.getKeysForFuncProxy(funcProxy);
140                 func = wrappedFuncs[funcProxy];
141                 keys.do({|key| active[key] = active[key].removeFunc(func) }); // support multiple keys
142                 wrappedFuncs[funcProxy] = nil;
143                 if(active.size == 0, {this.unregister});
144         }
145         
146                 // old Funk vs. new Funk
147         updateFuncForFuncProxy {|funcProxy|
148                 var func, oldFunc, keys;
149                 func = this.wrapFunc(funcProxy);
150                 oldFunc = wrappedFuncs[funcProxy];
151                 wrappedFuncs[funcProxy] = func;
152                 keys = this.getKeysForFuncProxy(funcProxy);
153                 keys.do({|key| active[key] = active[key].replaceFunc(oldFunc, func) }); // support multiple keys
154         }
155                 
156         wrapFunc { this.subclassResponsibility(thisMethod) }
157         
158         getKeysForFuncProxy { this.subclassResponsibility(thisMethod) }
159         
160         update {|funcProxy, what| if(what == \function, { this.updateFuncForFuncProxy(funcProxy) }) }
161         
162         free { wrappedFuncs.keys.do({|funcProxy| funcProxy.removeDependant(this) }); super.free }
163         
166 // The default dispatchers below store by the 'most significant' message argument for fast lookup
167 // These are for use when more than just the 'most significant' argument needs to be matched
168 AbstractMessageMatcher {
169         var <>func;
170         
171         value { this.subclassResponsibility(thisMethod) }
172         
173         valueArray {arg args; ^this.value(*args) } // needed to work in FunctionLists
177 ///////////////////// OSC
179 OSCMessageDispatcher : AbstractWrappingDispatcher {
180         
181         wrapFunc {|funcProxy|
182                 var func, srcID, recvPort;
183                 func = funcProxy.func;
184                 srcID = funcProxy.srcID;
185                 recvPort = funcProxy.recvPort;
186                 ^case(
187                         { srcID.notNil && recvPort.notNil }, { OSCFuncBothMessageMatcher(srcID, recvPort, func) },
188                         { srcID.notNil }, { OSCFuncAddrMessageMatcher(srcID, func) },
189                         { recvPort.notNil }, { OSCFuncRecvPortMessageMatcher(recvPort, func) },
190                         { func }
191                 );
192         }
193         
194         getKeysForFuncProxy {|funcProxy| ^[funcProxy.path];}
195         
196         value {|time, addr, recvPort, msg| active[msg[0]].value(msg, time, addr, recvPort);}
197         
198         register { 
199                 thisProcess.recvOSCfunc = thisProcess.recvOSCfunc.addFunc(this); 
200                 registered = true; 
201         }
202         
203         unregister { 
204                 thisProcess.recvOSCfunc = thisProcess.recvOSCfunc.removeFunc(this);
205                 registered = false;
206         }
207         
208         typeKey { ^('OSC unmatched').asSymbol }
209         
212 OSCMessagePatternDispatcher : OSCMessageDispatcher {
213         
214         value {|time, addr, recvPort, msg| 
215                 var pattern;
216                 pattern = msg[0];
217                 active.keysValuesDo({|key, func|
218                         if(key.matchOSCAddressPattern(pattern), {func.value(msg, time, addr, recvPort);});
219                 })
220         }
221         
222         typeKey { ^('OSC matched').asSymbol }
223         
226 OSCFunc : AbstractResponderFunc {
227         classvar <>defaultDispatcher, <>defaultMatchingDispatcher, traceFunc, traceRunning = false;
228         var <path, <recvPort;
229         
230         *initClass {
231                 defaultDispatcher = OSCMessageDispatcher.new;
232                 defaultMatchingDispatcher = OSCMessagePatternDispatcher.new;
233                 traceFunc = {|time, replyAddr, recvPort, msg|
234                         "OSC Message Received:\n\ttime: %\n\taddress: %\n\trecvPort: %\n\tmsg: %\n\n".postf(time, replyAddr, recvPort, msg);
235                 }
236         }
237         
238         *new { arg func, path, srcID, recvPort, dispatcher;
239                 ^super.new.init(func, path, srcID, recvPort, dispatcher ? defaultDispatcher);
240         }
241         
242         *newMatching { arg func, path, srcID, recvPort;
243                 ^super.new.init(func, path, srcID, recvPort, defaultMatchingDispatcher);
244         }
245         
246         *trace {|bool = true| 
247                 if(bool, {
248                         if(traceRunning.not, {
249                                 thisProcess.addOSCFunc(traceFunc);
250                                 CmdPeriod.add(this);
251                                 traceRunning = true;
252                         });
253                 }, {
254                         thisProcess.removeOSCFunc(traceFunc);
255                         CmdPeriod.remove(this);
256                         traceRunning = false;
257                 });
258         }
259         
260         *cmdPeriod { this.trace(false) }
261         
262         init {|argfunc, argpath, argsrcID, argrecvPort, argdisp|
263                 path = (argpath ? path).asString;
264                 if(path[0] != $/, {path = "/" ++ path}); // demand OSC compliant paths
265                 path = path.asSymbol;
266                 srcID = argsrcID ? srcID;
267                 recvPort = argrecvPort ? recvPort;
268                 func = argfunc;
269                 dispatcher = argdisp ? dispatcher;
270                 this.enable;
271                 allFuncProxies.add(this);
272         }
273         
274         printOn { arg stream; stream << this.class.name << "(" <<* [path, srcID] << ")" }
275         
278 OSCdef : OSCFunc {
279         classvar <all; 
280         var <key;
281         
282         *initClass {
283                 all = IdentityDictionary.new;
284         }
285         
286         *new { arg key, func, path, srcID, recvPort, dispatcher;
287                 var res = all.at(key);
288                 if(res.isNil) {
289                         ^super.new(func, path, srcID, recvPort, dispatcher).addToAll(key);
290                 } {
291                         if(func.notNil) { 
292                                 if(res.enabled, {
293                                         res.disable;
294                                         res.init(func, path, srcID, recvPort, dispatcher);
295                                 }, { res.init(func, path, srcID, recvPort, dispatcher).disable; });
296                         }
297                 }
298                 ^res
299         }
300         
301         addToAll {|argkey| key = argkey; all.put(key, this) }
302         
303         free { all[key] = nil; super.free; }
304         
305         printOn { arg stream; stream << this.class.name << "(" <<* [key, path, srcID] << ")" }
306         
310 // if you need to test for address func gets wrapped in this
311 OSCFuncAddrMessageMatcher : AbstractMessageMatcher {
312         var addr;
313         
314         *new {|addr, func| ^super.new.init(addr, func);}
315         
316         init {|argaddr, argfunc| addr = argaddr; func = argfunc; }
317         
318         value {|msg, time, testAddr, recvPort| 
319                 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)}, {
320                         func.value(msg, time, testAddr, recvPort)
321                 })
322         }
325 // if you need to test for recvPort func gets wrapped in this
326 OSCFuncRecvPortMessageMatcher : AbstractMessageMatcher {
327         var recvPort;
328         
329         *new {|recvPort, func| ^super.new.init(recvPort, func);}
330         
331         init {|argrecvPort, argfunc| recvPort = argrecvPort; func = argfunc; }
332         
333         value {|msg, time, addr, testRecvPort| 
334                 if(testRecvPort == recvPort, {
335                         func.value(msg, time, addr, testRecvPort)
336                 })
337         }
340 OSCFuncBothMessageMatcher : AbstractMessageMatcher {
341         var addr, recvPort;
342         
343         *new {|addr, recvPort, func| ^super.new.init(addr, recvPort, func);}
344         
345         init {|argaddr, argrecvPort, argfunc| addr = argaddr; recvPort = argrecvPort; func = argfunc; }
346         
347         value {|msg, time, testAddr, testRecvPort| 
348                 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)} and: {testRecvPort == recvPort}, {
349                         func.value(msg, time, testAddr, testRecvPort)
350                 })
351         }
354 ///////////////////// MIDI
356 // for \noteOn, \noteOff, \control, \polytouch
357 MIDIMessageDispatcher : AbstractWrappingDispatcher {
358         var <>messageType;
359         
360         *new {|messageType| ^super.new.messageType_(messageType) }
361         
362         getKeysForFuncProxy {|funcProxy| ^(funcProxy.msgNum ? (0..127)).asArray;} // noteNum, etc.
363         
364         value {|src, chan, num, val| active[num].value(val, num, chan, src);}
365         
366         register { 
367                 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).addFunc(this)); 
368                 registered = true; 
369         }
370         
371         unregister { 
372                 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).removeFunc(this));
373                 registered = false;
374         }
375         
376         // wrapper objects based on arg type and testing requirements
377         wrapFunc {|funcProxy|
378                 var func, chan, srcID;
379                 func = funcProxy.func;
380                 chan = funcProxy.chan;
381                 srcID = funcProxy.srcID;
382                 ^case(
383                         { srcID.notNil && chan.isArray }, {MIDIFuncBothCAMessageMatcher(chan, srcID, func)},
384                         { srcID.notNil && chan.notNil }, {MIDIFuncBothMessageMatcher(chan, srcID, func)},
385                         { srcID.notNil }, {MIDIFuncSrcMessageMatcher(srcID, func)},
386                         { chan.isArray }, {MIDIFuncChanArrayMessageMatcher(chan, func)},
387                         { chan.notNil }, {MIDIFuncChanMessageMatcher(chan, func)},
388                         { func }
389                 );
390         }
391         
392         typeKey { ^('MIDI ' ++ messageType).asSymbol }  
395 // for \touch, \program, \bend
396 MIDIMessageDispatcherNV : MIDIMessageDispatcher {
397         
398         getKeysForFuncProxy {|funcProxy| ^(funcProxy.chan ? (0..15)).asArray;} // chan
399         
400         value {|src, chan, val| active[chan].value(val, chan, src);}
401         
402         // wrapper objects based on arg type and testing requirements
403         wrapFunc {|funcProxy|
404                 var func, chan, srcID;
405                 func = funcProxy.func;
406                 chan = funcProxy.chan;
407                 srcID = funcProxy.srcID;
408                 // are these right?
409                 ^case(
410                         { srcID.notNil }, {MIDIFuncSrcMessageMatcherNV(srcID, func)},
411                         { func }
412                 );
413         }
417 MIDIFunc : AbstractResponderFunc {
418         classvar <>defaultDispatchers;
419         var <chan, <msgNum, <msgVal, <msgType;
420         
421         *initClass {
422                 defaultDispatchers = IdentityDictionary.new;
423                 [\noteOn, \noteOff, \control, \polytouch].do({|type|
424                         defaultDispatchers[type] = MIDIMessageDispatcher(type);
425                 });
426                 [\touch, \program, \bend].do({|type|
427                         defaultDispatchers[type] = MIDIMessageDispatcherNV(type);
428                 });
429         }
430         
431         *new { arg func, msgNum, chan, msgType, srcID, dispatcher;
432                 ^super.new.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]);
433         }
434         
435         *cc { arg func, ccNum, chan, srcID, dispatcher;
436                 ^this.new(func, ccNum, chan, \control, srcID, dispatcher);
437         }
438         
439         *noteOn { arg func, noteNum, chan, srcID, dispatcher;
440                 ^this.new(func, noteNum, chan, \noteOn, srcID, dispatcher);
441         }
442         
443         *noteOff { arg func, noteNum, chan, srcID, dispatcher;
444                 ^this.new(func, noteNum, chan, \noteOff, srcID, dispatcher);
445         }
446         
447         *polytouch { arg func, noteNum, chan, srcID, dispatcher;
448                 ^this.new(func, noteNum, chan, \polytouch, srcID, dispatcher);
449         }
450         
451         *touch { arg func, chan, srcID, dispatcher;
452                 ^this.new(func, nil, chan, \touch, srcID, dispatcher);
453         }
454         
455         *bend { arg func, chan, srcID, dispatcher;
456                 ^this.new(func, nil, chan, \bend, srcID, dispatcher);
457         }
458         
459         *program { arg func, chan, srcID, dispatcher;
460                 ^this.new(func, nil, chan, \program, srcID, dispatcher);
461         }
462         
463         init {|argfunc, argmsgNum, argchan, argType, argsrcID, argdisp|
464                 msgNum = msgNum ? argmsgNum;
465                 chan = chan ? argchan;
466                 srcID = argsrcID ? srcID;
467                 func = argfunc;
468                 msgType = argType ? msgType;
469                 dispatcher = argdisp ? dispatcher;
470                 this.enable;
471                 allFuncProxies.add(this);
472         }
473         
474         // post pretty
475         printOn { arg stream; stream << this.class.name << "(" <<* [msgType, msgNum, chan] << ")" }
479 MIDIdef : MIDIFunc {
480         classvar <>all; // same as other def classes, do we need a setter really?
481         var <key;
482         
483         *initClass {
484                 all = IdentityDictionary.new;
485         }
486         
487         *new { arg key, func, msgNum, chan, msgType, srcID, dispatcher;
488                 var res = all.at(key);
489                 if(res.isNil) {
490                         ^super.new(func, msgNum, chan, msgType, srcID, dispatcher).addToAll(key);
491                 } {
492                         if(func.notNil) { 
493                                 if(res.enabled, {
494                                         res.disable;
495                                         res.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]);
496                                 }, { res.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]).disable; });
497                         }
498                 }
499                 ^res
500         }
501         
502         *cc { arg key, func, ccNum, chan, srcID, dispatcher;
503                 ^this.new(key, func, ccNum, chan, \control, srcID, dispatcher);
504         }
505         
506         *noteOn { arg key, func, noteNum, chan, srcID, dispatcher;
507                 ^this.new(key, func, noteNum, chan, \noteOn, srcID, dispatcher);
508         }
509         
510         *noteOff { arg key, func, noteNum, chan, srcID, dispatcher;
511                 ^this.new(key, func, noteNum, chan, \noteOff, srcID, dispatcher);
512         }
513         
514         *polytouch { arg key, func, noteNum, chan, srcID, dispatcher;
515                 ^this.new(key, func, noteNum, chan, \polytouch, srcID, dispatcher);
516         }
517         
518         *touch { arg key, func, chan, srcID, dispatcher;
519                 ^this.new(key, func, nil, chan, \touch, srcID, dispatcher);
520         }
521         
522         *bend { arg key, func, chan, srcID, dispatcher;
523                 ^this.new(key, func, nil, chan, \bend, srcID, dispatcher);
524         }
525         
526         *program { arg key, func, chan, srcID, dispatcher;
527                 ^this.new(key, func, nil, chan, \program, srcID, dispatcher);
528         }
529         
530         addToAll {|argkey| key = argkey; all.put(key, this) }
531         
532         free { all[key] = nil; super.free; }
533         
534         // post pretty
535         printOn { arg stream; stream << this.class.name << "(" <<* [key, msgType, msgNum, chan] << ")" }
536         
540 // if you need to test for srcID func gets wrapped in this
541 MIDIFuncSrcMessageMatcher : AbstractMessageMatcher {
542         var srcID;
543         
544         *new {|srcID, func| ^super.new.init(srcID, func);}
545         
546         init {|argsrcID, argfunc| srcID = argsrcID; func = argfunc; }
547         
548         value {|value, num, chan, testSrc|
549                 if(srcID == testSrc, {func.value(value, num, chan, testSrc)}) 
550         }
553 // if you need to test for srcID func gets wrapped in this
554 MIDIFuncChanMessageMatcher : AbstractMessageMatcher {
555         var chan;
556         
557         *new {|chan, func| ^super.new.init(chan, func);}
558         
559         init {|argchan, argfunc| chan = argchan; func = argfunc; }
560         
561         value {|value, num, testChan, srcID|
562                 if(chan == testChan, {func.value(value, num, testChan, srcID)}) 
563         }
566 // if you need to test for chanArray func gets wrapped in this
567 MIDIFuncChanArrayMessageMatcher : AbstractMessageMatcher {
568         var chanBools, <>func;
569         
570         *new {|chanArray, func|
571                 var chanBools;
572                 // lookup bool by index fastest, so construct an Array here
573                 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
574                 ^super.new.init(chanBools, func);
575         }
576         
577         init {|argchanbools, argfunc| chanBools = argchanbools; func = argfunc; }
578         
579         value {|value, num, testChan, srcID| 
580                 // lookup bool by index fastest
581                 if(chanBools[testChan], {func.value(value, num, testChan, srcID)}) 
582         }
585 // version for message types which don't pass a val
586 MIDIFuncSrcMessageMatcherNV : MIDIFuncSrcMessageMatcher {
587         
588         value {|num, chan, testSrc|
589                 if(srcID == testSrc, {func.value(num, chan, testSrc)}) 
590         }
593 // if you need to test for chan and srcID func gets wrapped in this
594 MIDIFuncBothMessageMatcher : AbstractMessageMatcher {
595         var chan, srcID;
596         
597         *new {|chan, srcID, func| ^super.new.init(chan, srcID, func);}
598         
599         init {|argchan, argsrcID, argfunc| chan = argchan; srcID = argsrcID; func = argfunc; }
600         
601         value {|value, num, testChan, testSrc| 
602                 if(srcID == testSrc and: {chan == testChan}, {func.value(value, num, testChan, testSrc)}) 
603         }       
607 // if you need to test for chanArray and srcID func gets wrapped in this
608 MIDIFuncBothCAMessageMatcher : AbstractMessageMatcher {
609         var chanBools, srcID;
610         
611         *new {|chanArray, srcID, func| 
612                 var chanBools;
613                 // lookup bool by index fastest, so construct an Array here
614                 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
615                 ^super.new.init(chanBools, srcID, func);
616         }
617         
618         init {|argbools, argsrcID, argfunc| chanBools = argbools; srcID = argsrcID; func = argfunc; }
619         
620         value {|value, num, testChan, testSrc| 
621                 if(srcID == testSrc and: {chanBools[testChan]}, {func.value(value, num, testChan, testSrc)}) 
622         }