1 // idea by adc, mods by /f0, jrh, mc, ...
4 // proxymixer need not be built into a supplied window,
5 // so just call the parent window.
6 var <window, <zone, <proxyspace, <>nProxies;
10 var <editZoneBtn, <pxMons, <editBtnsAr, <editBtnsKr, <buttonLinesKr;
11 var <compArZone, <compKrZone;
13 var origBounds, fullBounds, editZoneOpen=false, <selectMethod=\existingProxies;
14 var <skippy, <editor, <recorder;
16 var <prevKrNames, <prevArNames;
17 var <scrollyAr, <keysRotationAr=0;
18 var <scrollyKr, <keysRotationKr=0;
21 GUI.skins.put(\jitSmall, (
22 fontSpecs: ["Helvetica", 10],
23 fontColor: Color.black,
24 background: Color(0.8, 0.85, 0.7, 0.5),
25 foreground: Color.grey(0.95),
26 onColor: Color(0.5, 1, 0.5),
27 offColor: Color.clear,
36 warn("" ++ this.class ++ ":w is deprecated. use .window instead.")
40 *new { arg proxyspace, nProxies = 16, title, where;
41 ^super.new.init(proxyspace, nProxies,
42 title ?? { format("proxyspace: %", proxyspace.tryPerform(\name) ? "") }, where
46 init { arg space, nPxs = 16, argTitle, where;
48 var winHeight, height, color, bounds;
49 skin = GUI.skins[\jitSmall];
50 font = GUI.font.new(*skin.fontSpecs);
53 bounds = ((where ? (10@250)).asArray ++ [0,0]).asRect;
55 proxyspace = space ? currentEnvironment;
56 if(proxyspace.isKindOf(ProxySpace).not) { Error("no proxy space present!").throw };
59 height = nProxies + 1 * (skin.buttonHeight) + 16;
63 origBounds = bounds.resizeTo(612, height + 10);
64 fullBounds = origBounds.resizeBy(345, 0);
67 zone = CompositeView(window, fullBounds.moveTo(0,0));
68 zone.decorator = FlowLayout(zone.bounds); //.gap_(6@6);
70 compArZone = CompositeView.new(zone, Rect(0, 0, 400, height))
71 .background_(skin.foreground);
72 compKrZone = CompositeView.new(zone, Rect(0, 0, 200, height))
73 .background_(skin.foreground);
75 compArZone.decorator = FlowLayout(compArZone.bounds).gap_(skin.gap);
76 compKrZone.decorator = FlowLayout(compKrZone.bounds).gap_(skin.gap);
85 window.onClose_({ this.stopUpdate });
90 runUpdate { editor.runUpdate; skippy.start; }
91 stopUpdate { editor.stopUpdate; skippy.stop }
94 window = Window(this.title, origBounds);
95 window.view.background = skin.background;
99 skippy = SkipJack({ this.updateZones }, 0.2, { window.isClosed }, title);
103 editor = NodeProxyEditor(nil, nProxies - 1, zone);
106 openEditZone { arg flag;
107 if(flag == 1 or: {editZoneOpen.not}) {
108 if(editZoneOpen.not) {
109 origBounds = window.bounds;
110 window.bounds = window.bounds.setExtent(fullBounds.width, fullBounds.height);
112 editZoneBtn.value = 1;
116 window.bounds = window.bounds.setExtent(origBounds.width, origBounds.height);
117 editZoneOpen = false;
118 editZoneBtn.value = 0;
125 GUI.popUpMenu.new(compArZone, Rect(10, 10, 110, skin.buttonHeight+2))
126 .items_([\existingProxies, \activeProxies, \playingProxies])
127 .action_({ arg view; selectMethod = view.items[view.value] })
130 GUI.button.new(compArZone, Rect(10, 10, 50, skin.buttonHeight+2))
132 [["reduce", skin.fontcolor, Color.clear]] )
133 .action_({ proxyspace.reduce }).font_(font);
134 GUI.button.new(compArZone, Rect(10, 10, 30, skin.buttonHeight+2))
136 [["doc", skin.fontcolor, Color.clear]] )
137 .action_({ proxyspace.document }).font_(font);
138 GUI.button.new(compArZone, Rect(10, 10, 30, skin.buttonHeight+2))
140 [["docc", skin.fontcolor, Color.clear]] )
142 proxyspace.document(this.selectedKeys)
145 GUI.button.new.new(compArZone, Rect(10, 10, 20, skin.buttonHeight+2)).states_([
146 ["#", skin.fontcolor, Color.clear ]
148 .action_({ editor.pxKey !? { proxyspace.storeOn(Post, [editor.pxKey]) } })
151 editZoneBtn = GUI.button.new(compArZone, Rect(10, 10, 60, skin.buttonHeight+2))
154 [ ["openEdit", skin.fontcolor, Color.clear],
155 ["closeEdit", skin.fontcolor, Color.clear]
157 .action_({ |b| this.openEditZone(b.value) });
159 GUI.button.new(compArZone, Rect(10, 10, 20, skin.buttonHeight+2))
162 [ ["R", Color.red, Color.clear] ])
163 .action_({ RecordProxyMixer(this, origBounds.resizeTo(472, 100)) });
168 var arLayout = compArZone.decorator;
174 #pxMons, editBtnsAr = Array.fill(nProxies, { arg i;
176 pxmon = ProxyMonitorGui(nil, compArZone, 360@skin.buttonHeight, makeWatcher: false); pxmon.zone.visible_(false);
178 // arLayout.shift(4);
180 edbut = GUI.button.new(compArZone, Rect(0,0, 16, 16))
184 ["ed", Color.black, Color.grey(0.75)],
185 ["ed", Color.black, Color.white]])
186 .action_({ arg btn, mod;
187 if (mod.notNil and: { mod.isAlt }) {
188 NodeProxyEditor(pxmon.proxy); } {
189 editor.proxy_(pxmon.proxy);
190 this.openEditZone(1);
191 editBtnsAr.do { |b| b.value_(0) };
192 editBtnsKr.do { |b| b.value_(0) };
200 scrollyAr = EZScroller.new(window,
201 Rect(compArZone.bounds.right - 16, 30, 12, nProxies * skin.buttonHeight),
203 { |sc| keysRotationAr = sc.value.asInteger.max(0); }
204 ).value_(0).visible_(true);
209 var layout = compKrZone.decorator;
212 GUI.staticText.new(compKrZone, Rect(0, 0, 180, 18))
213 .font_(font).align_(\center)
214 .string_("kr proxies");
220 GUI.button.new(compKrZone, Rect(0,0,20, skin.buttonHeight))
221 .font_(Font("Helvetica-Bold", 12))
222 .states_([["+", Color.red, Color.white]])
224 if(emergencySynth.isPlaying.not) {
225 emergencySynth = Synth.tail(0, "proxymixer_emergency");
226 NodeWatcher.register(emergencySynth);
228 emergencySynth.release
235 buttonLinesKr = Array.fill(nProxies, { arg i;
236 var nameBtn, pausBtn, sendBtn, editBtn, pollBtn;
238 nameBtn = GUI.dragSource.new(compKrZone, Rect(0,0,72, skin.buttonHeight))
241 pausBtn = GUI.button.new(compKrZone, Rect(0,0, 30, skin.buttonHeight))
242 .font_(font).states_([["paus", Color.black, skin.onColor],
243 ["rsum", Color.black, Color.clear]])
245 var editName = nameBtn.string.asSymbol;
246 var px = proxyspace.envir[editName];
248 if (px.paused) { px.resume } { px.pause };
251 sendBtn = GUI.button.new(compKrZone, Rect(0,0, 30, skin.buttonHeight))
252 .font_(font).states_([["send", Color.black, skin.onColor]])
254 var editName = nameBtn.string.asSymbol;
255 var px = proxyspace.envir[editName];
256 if (px.notNil) { px.send };
259 pollBtn = GUI.button.new(compKrZone, Rect(0,0, 24, skin.buttonHeight))
260 .font_(font).states_([["poll", Color.black, skin.foreground]])
262 var editName = nameBtn.string.asSymbol;
263 var px = proxyspace.envir[editName];
265 px.bus.getn(px.numChannels, { |arr| [editName, arr.unbubble].postln });
268 editBtn = GUI.button.new(compKrZone, Rect(0,0,20, skin.buttonHeight)).states_([
269 ["ed", skin.fontcolor, Color.new(0.7,0.7,0.7,1)],
270 ["ed", skin.fontcolor, Color.white]])
272 var editName = nameBtn.string.asSymbol;
273 editor.proxy_(proxyspace.envir[editName]);
275 this.openEditZone(1);
276 editBtnsAr.do { |b| b.value_(0) };
277 editBtnsKr.do { |b| b.value_(0) };
282 [ nameBtn, pausBtn, sendBtn, editBtn, pollBtn].do({ arg v; v.visible_(false) });
284 editBtnsKr = buttonLinesKr.collect(_[1]);
287 scrollyKr = EZScroller.new(window,
288 Rect(compKrZone.bounds.right - 16, 30, 12, nProxies * skin.buttonHeight),
290 { |sc| keysRotationKr = sc.value.asInteger.max(0); }
291 ).value_(0).visible_(true);
300 highlightSlots { |parOffset, num|
301 var onCol = Color(1, 0.5, 0.5);
302 var offCol = Color.clear;
303 { pxMons.do { |moni, i|
304 var col = if (i >= parOffset and: (i < (parOffset + num).max(0)), onCol, offCol);
305 moni.nameView.background_(col.green_([0.5, 0.7].wrapAt(i - parOffset div: 2)));
311 krProxyNames = proxyspace.krProxyNames;
313 if (krProxyNames.size > nProxies) {
315 scrollyKr.visible_(true)
316 .numItems_(krProxyNames.size)
317 .value_(keysRotationKr ? (krProxyNames.size - nProxies));
319 krProxyNames = krProxyNames.drop(keysRotationKr).keep(nProxies);
321 scrollyKr.visible_(false);
324 if (krProxyNames != prevKrNames) {
326 var butLine, nameSink, key, px, showLine;
327 key = krProxyNames[i];
328 px = proxyspace.envir[ key ];
330 butLine = buttonLinesKr[i];
331 nameSink = butLine[0];
332 nameSink.object_(px).string_(key ? "");
334 showLine = key.notNil and: px.notNil;
335 butLine.do(_.visible_(showLine));
338 prevKrNames = krProxyNames;
341 existingProxies { ^proxyspace.arProxyNames }
342 activeProxies { ^proxyspace.arProxyNames({ |px, key| px.isPlaying }) }
343 playingProxies { ^proxyspace.arProxyNames({ |px, key| px.monitor.isPlaying }) }
345 selectedKeys { ^this.perform(selectMethod) }
348 var arPxNames, pxKey, px, pxMon;
349 arPxNames = this.selectedKeys;
351 if (arPxNames.size > nProxies) {
352 scrollyAr.visible_(true)
353 .numItems_(arPxNames.size)
354 .value_(keysRotationAr ? (arPxNames.size - nProxies));
356 arPxNames = arPxNames.drop(keysRotationAr).keep(nProxies);
358 scrollyAr.visible_(false);
361 if (arPxNames != prevArNames) {
364 pxKey = arPxNames[i];
366 px = proxyspace.envir.at(pxKey);
368 pxMon.zone.visible_(true);
369 editBtnsAr[i].visible_(true);
372 pxMon.zone.visible_(false);
373 editBtnsAr[i].visible_(false);
377 arPxNames.do { |name, i| pxMons[i].updateAll };
379 prevArNames = arPxNames;
382 adjustWindowSize { arg n;
384 window.bounds = if(editZoneOpen)
385 { origBounds.resizeBy(0, 330) }
386 { origBounds.copy.height_(n * 18 + 80) };
391 NdefMixerOld : ProxyMixerOld {
393 *new { |server, nProxies = 16, title, bounds|
395 if (server.isKindOf(Symbol)) {
396 server = Server.named.at(server);
397 if (server.isNil) { Error("NdefMixer: no server named %.".format(server)).throw };
399 server = server ? Ndef.defaultServer ? Server.default;
402 space = Ndef.dictFor(server);
404 title = title ? ("Ndef:" + server.name);
405 ^super.new(space, nProxies, title, bounds);