diff --git a/HelpSource/Classes/ServerTreeView.schelp b/HelpSource/Classes/ServerTreeView.schelp new file mode 100644 index 00000000000..cbf87d16caa --- /dev/null +++ b/HelpSource/Classes/ServerTreeView.schelp @@ -0,0 +1,52 @@ +class:: ServerTreeView +summary:: Show Server-side Node Tree +categories:: GUI>Views +related:: Classes/ServerTreeView + +description:: +ServerTreeView shows the node tree on a server. + +note:: +The link::Classes/ServerTreeView:: will disappear when the pointed server is not running. +:: + +classmethods:: + +method:: new +argument:: aServer +The server to display its node tree. +argument:: bounds +A link::Classes/Rect::, or any object responding to the link::Search#asRect#.asRect:: method. +argument:: interval +Polling interval. + +instancemethods:: + +method::close +Close the receiver. + +examples:: + +code:: +// Start server +s.boot; + +// Create a ServerTreeView: +a = ServerTreeView(s) + +// Create a ServerTreeView: +b = ServerTreeView(s) + +// Create a ServerTreeView: + +c = ServerTreeView(s, Rect(300, 600, 300, 600)) // arguments: server, bounds +// Create a ServerTreeView: + +d = ServerTreeView(s, Rect(600, 600, 300, 600), 1) // arguments: server, bounds, interval + +// Close a particular ServerTreeView: +a.close + +// Close some ServerTreeView instances. +[b, c].do { |v| v.close } +:: diff --git a/SCClassLibrary/Common/Control/Server.sc b/SCClassLibrary/Common/Control/Server.sc index 69848ae1cfb..1441aafbbd2 100644 --- a/SCClassLibrary/Common/Control/Server.sc +++ b/SCClassLibrary/Common/Control/Server.sc @@ -357,7 +357,7 @@ Server { var scopeWindow, scopeWindow, =0, { + child = dumpFunc.value(msg[i+1]); + }, { + j = 4; + child = ().synth.instrument_(msg[i+2]); + if(printControls, { + controls = (); + msg[i+3].do({ + controls[msg[i + j]] = msg[i + j + 1]; + j = j + 2; + }); + child.controls = controls; + i = i + 4 + (2 * controls.size); + }, {i = i + 3 }); + }); + child.id = id; + }); + event.children = children; + event; + }; + finalEvent = dumpFunc.value(msg[3]); + done = true; + collectChildren = {|group| + group.children.collect({|child| + if(child.children.notNil,{ + child.id -> collectChildren.value(child); + }, { + child.id -> child.instrument; + }); + }); + }; + levels = collectChildren.value(finalEvent); + countSize = {|array| + var size = 0; + array.do({|elem| + if(elem.value.isArray, { size = size + countSize.value(elem.value) + 2}, {size = size + 1;}); + }); + size + }; + defer { + view.bounds = Rect(0, 0, 400, max(400, tabSize * (countSize.value(levels) + 2))); + view.refresh; + } + }, '/g_queryTree.reply', this.server.addr).fix; + + updater = { + fork { + loop { + defer { + if (this.server.serverIsRunning.not) { + if(window.isClosed.not) { + treeViewStatus.string_(" STOPPED: last updated server tree") + .background_(Color.grey(0.7)); + this.server.doWhenBooted{ + treeViewStatus.string_(" STARTED: current server tree") + .background_(Color.grey(0.9)) + } + } + } + }; + this.server.sendMsg("/g_queryTree", 0, 0); + interval.wait; + } + } + }; + updateFunc = updater.value; + CmdPeriod.add(updateFunc); + SystemClock.sched(3, { + if(done.not, { + actionIfFail.value(); + "Server failed to respond to Group:queryTree!".warn; + }); + }); + } + + stop { + treeViewStatus.string_(" STOPPED: last updated server tree") + .background_(Color.grey(0.7)); + updateFunc.stop; + CmdPeriod.remove(updateFunc); + resp.free; + } + + close { + this.stop; + window.close + } +} + + Server { // splitting makeWindow and makeGui, makes sure that makeWindow can be overridden @@ -102,7 +307,7 @@ }; w.view.keyDownAction = { arg view, char, modifiers; - // if any modifiers except shift key are pressed, skip action + // if any modifiers except shift key are pressed, skip action if(modifiers & 16515072 == 0) { case @@ -259,26 +464,26 @@ }; if(isLocal or: { options.remoteControlVolume }) { - { - var volSpec, currentVolume; - var volumeSlider, muteButton, muteActions, volController; + { + var volSpec, currentVolume; + var volumeSlider, muteButton, muteActions, volController; - currentVolume = this.volume.volume; + currentVolume = this.volume.volume; - muteActions = [{this.unmute}, {this.mute}]; - volSpec = [volume.min, volume.max, \db].asSpec; + muteActions = [{this.unmute}, {this.mute}]; + volSpec = [volume.min, volume.max, \db].asSpec; - gui.staticText.new(w, Rect(0,0, 44, 18)) + gui.staticText.new(w, Rect(0,0, 44, 18)) .font_(font) .string_("volume:"); - muteButton = gui.button.new(w, Rect(0, 0, 20, 18)) + muteButton = gui.button.new(w, Rect(0, 0, 20, 18)) .font_(font) .canFocus_(false) .states_([ ["M"], ["M", nil, faintRed] - ]) + ]) .action_({arg me; if(this.serverRunning) { muteActions[me.value].value; @@ -288,7 +493,7 @@ } }); - volumeNum = gui.numberBox.new(w, Rect(0, 0, 28, 18)) + volumeNum = gui.numberBox.new(w, Rect(0, 0, 28, 18)) .font_(font) .value_(currentVolume) .align_(\center) @@ -297,9 +502,9 @@ newdb = me.value.clip(-90, 6); this.volume_(newdb); volumeSlider.value_(volSpec.unmap(newdb)); - }); + }); - volumeSlider = gui.slider.new(w, Rect(0, 0, 172, 18)) + volumeSlider = gui.slider.new(w, Rect(0, 0, 172, 18)) .value_(volSpec.unmap(currentVolume).round(0.1)) .onClose_{volController.remove} .action_({arg me; @@ -307,7 +512,7 @@ newdb = volSpec.map(me.value).round(0.1); this.volume_(newdb); volumeNum.value_(newdb); - }) + }) .keyDownAction_({arg slider, char, modifiers, unicode, keycode; if (char == $], { slider.increment; }); if (char == $[, { slider.decrement; }); @@ -316,8 +521,8 @@ if (unicode == 16rF701, { slider.decrement; }); if (unicode == 16rF702, { slider.decrement; }); nil; - }); - volController = SimpleController(volume) + }); + volController = SimpleController(volume) .put(\amp, {|changer, what, vol| { volumeNum.value_(vol.round(0.01)); @@ -334,23 +539,23 @@ volumeSlider.value_(volSpec.unmap(volume.volume)); }) - }.value; + }.value; }; w.front; serverController = SimpleController(this) - .put(\serverRunning, { if(this.serverRunning, running, stopped) }) - .put(\counts,{ - countsViews.at(0).string = statusWatcher.avgCPU.round(0.1); - countsViews.at(1).string = statusWatcher.peakCPU.round(0.1); - countsViews.at(2).string = statusWatcher.numUGens; - countsViews.at(3).string = statusWatcher.numSynths; - countsViews.at(4).string = statusWatcher.numGroups; - countsViews.at(5).string = statusWatcher.numSynthDefs; - }) - .put(\bundling, bundling) - .put(\default, showDefault); + .put(\serverRunning, { if(this.serverRunning, running, stopped) }) + .put(\counts,{ + countsViews.at(0).string = statusWatcher.avgCPU.round(0.1); + countsViews.at(1).string = statusWatcher.peakCPU.round(0.1); + countsViews.at(2).string = statusWatcher.numUGens; + countsViews.at(3).string = statusWatcher.numSynths; + countsViews.at(4).string = statusWatcher.numGroups; + countsViews.at(5).string = statusWatcher.numSynthDefs; + }) + .put(\bundling, bundling) + .put(\default, showDefault); if(isLocal){ serverController.put(\cmdPeriod, { recorder.setProperty(\value, 0) @@ -360,166 +565,29 @@ this.startAliveThread; } - plotTree {|interval=0.5| - var onClose, window = Window.new(name.asString + "Node Tree", - Rect(128, 64, 400, 400), - scroll:true - ).front; - window.view.hasHorizontalScroller_(false).background_(Color.grey(0.9)); - onClose = this.plotTreeView(interval, window.view, { defer {window.close}; }); - window.onClose = { - onClose.value; - }; - } - - plotTreeView {|interval=0.5, parent, actionIfFail| - var resp, done = false; - var collectChildren, levels, countSize; - var view, bounds; - var updater, updateFunc; - var tabSize = 25; - var pen, font; - - pen = GUI.current.pen; - font = Font.sansSerif(10); - - view = UserView.new(parent, Rect(0,0,400,400)); - - view.drawFunc = { - var xtabs = 0, ytabs = 0, drawFunc; - - drawFunc = {|group| - var thisSize, rect, endYTabs; - xtabs = xtabs + 1; - ytabs = ytabs + 1; - pen.font = font; - group.do({|node| - if(node.value.isArray, { - thisSize = countSize.value(node); - endYTabs = ytabs + thisSize + 0.2; - rect = Rect(xtabs * tabSize + 0.5, - ytabs * tabSize + 0.5, - parent.bounds.width - (xtabs * tabSize * 2), - thisSize * tabSize; - ); - pen.fillColor = Color.grey(0.8); - pen.fillRect(rect); - pen.strokeRect(rect); - pen.color = Color.black; - pen.stringInRect( - " Group" + node.key.asString + - (node.key == 1).if("- default group", ""), - rect - ); - drawFunc.value(node.value); - ytabs = endYTabs; - },{ - rect = Rect(xtabs * tabSize + 0.5, - ytabs * tabSize + 0.5, - 7 * tabSize, - 0.8 * tabSize - ); - pen.fillColor = Color.white; - pen.fillRect(rect); - pen.strokeRect(rect); - pen.color = Color.black; - pen.stringInRect( - " " ++ node.key.asString + node.value.asString, - rect - ); - ytabs = ytabs + 1; - }); - }); - xtabs = xtabs - 1; - }; - drawFunc.value(levels); - }; - - // msg[1] controls included - // msg[2] nodeID of queried group - // initial number of children - resp = OSCFunc({ arg msg; - var finalEvent; - var i = 2, j, controls, printControls = false, dumpFunc; - if(msg[1] != 0, {printControls = true}); - dumpFunc = {|numChildren| - var event, children; - event = ().group; - event.id = msg[i]; - event.instrument = nil; // need to know it's a group - i = i + 2; - children = Array.fill(numChildren, { - var id, child; - // i = id - // i + 1 = numChildren - // i + 2 = def (if synth) - id = msg[i]; - if(msg[i+1] >=0, { - child = dumpFunc.value(msg[i+1]); - }, { - j = 4; - child = ().synth.instrument_(msg[i+2]); - if(printControls, { - controls = (); - msg[i+3].do({ - controls[msg[i + j]] = msg[i + j + 1]; - j = j + 2; - }); - child.controls = controls; - i = i + 4 + (2 * controls.size); - }, {i = i + 3 }); - }); - child.id = id; - }); - event.children = children; - event; - }; - finalEvent = dumpFunc.value(msg[3]); - done = true; - collectChildren = {|group| - group.children.collect({|child| - if(child.children.notNil,{ - child.id -> collectChildren.value(child); - }, { - child.id -> child.instrument; - }); - }); - }; - levels = collectChildren.value(finalEvent); - countSize = {|array| - var size = 0; - array.do({|elem| - if(elem.value.isArray, { size = size + countSize.value(elem.value) + 2}, {size = size + 1;}); - }); - size - }; - defer { - view.bounds = Rect(0, 0, 400, max(400, tabSize * (countSize.value(levels) + 2))); - view.refresh; - } - }, '/g_queryTree.reply', addr).fix; - - updateFunc = { - updater = fork { - loop { - this.sendMsg("/g_queryTree", 0, 0); - interval.wait; - } + plotTree { |interval=0.5, bounds| + var detectWidth = { |thisRecBounds| + if(thisRecBounds.asRect.width < 400) { + var bnds = thisRecBounds.asRect; + "The width value you set will be changed to 400, the minimum width.".postln; + Rect(bnds.left, bnds.top, 400, bnds.height); + } { + thisRecBounds } }; - updateFunc.value; - CmdPeriod.add(updateFunc); - SystemClock.sched(3, { - if(done.not, { - actionIfFail.value(); - "Server failed to respond to Group:queryTree!".warn; - }); - }); - //action to be executed when enclosing window closes - ^{ - updater.stop; - CmdPeriod.remove(updateFunc); - resp.free; - } + if(plotTreeWindow.isNil) { + bounds = bounds ?? Rect(128, 64, 400, 400); + bounds = detectWidth.(bounds); + plotTreeWindow = Window(name.asString ++ " Node Tree", bounds, scroll:true); + plotTreeWindow.onClose = { plotTreeWindow = nil }; + this.plotTreeView(interval, plotTreeWindow, { defer { plotTreeWindow.close } }); + } { + bounds = bounds ?? this.plotTreeWindow.bounds; + bounds = detectWidth.(bounds); + plotTreeWindow.bounds_(bounds).front; + }; + } + plotTreeView { |interval=0.5, parent, actionIfFail| + ServerTreeView(this, parent: parent).start(interval, actionIfFail) } }