/*
?0 UserClasses Family

For several values of a variable, execute an action.
Basically just a form for specifying a for loop.

usage: makeFamily() constructs a Family and maps it to the screen. The Family
instance is destroyed when its window is dismissed.

The slider value ranges from start to end. Due to the length of time it
generally takes to complete an action it is best to use the middle button
to select a value with the slider. Dragging the slider button or using
it too rapidly will cause many action requests to be ignored since a
slider event taking place while handling the previous event is prevented
from executing the action recursively. The occasionally has the unintended
effect of missing the last action when one releases the mouse button.

?1 start end numbersteps
Beginning and final values of the loop variable. The number of steps
includes these limiting values so should be at least 2 but if it is 1 then
the loop reduces to only the start value.
?1 Variable
Pops up a SymChooser for selection of a variable name.
The Family object cannot work without a variable since there is no default.
?1 Action
Pops up a stringchooser for selectionof the body of the loop. The default
run() action is typically what is desired.
?1 Run
Starts the loop
?1 Stop
? Now stops the loop even in the middle of the action. (The stdrun.hoc run()
action regularly checks the stop_run variable.)
? Atendofaction
Waits for the current action to finish before stopping.
?1 Cont
starts the action with the next value of the variable.
(If the previous action was stopped in the middle,
that action is not restarted where it left off.)
*/

help ?0

begintemplate Family
public frun, box

nstep = 0
strdef variable, generator, cmd, tempstr
objref sc, box, this

proc init() {
	generator = "run()"
	sc = new SymChooser()
	xstart = 0
	xend = 0
	xinc = 0
	x = 0
	slideval = 0
	nstep = 1
	istep = 0
	vstop = 0
	if (numarg() == 1) {
		if ($1 == 0) {
			return
		}
	}
	setval()
	build()
	box.map()
}

proc build() {
	box = new VBox()
	box.ref(this)
	box.save("save()")
	box.intercept(1)
	xpanel("Family")
	xpvalue("start", &xstart, 1, "setval()")
	xpvalue("end", &xend, 1, "setval()")
	xpvalue("number steps", &nstep, 1, "setval()")
	xvarlabel(variable)
	xvarlabel(generator)
	xpanel()
	xpanel("Family", 1)
	xbutton("Variable", "changevar()")
	xbutton("Action", "changegen()")
	xpanel()
	xpanel("Family", 1)
	xbutton("Run", "frun()")
	xmenu("Stop")
	xbutton("Now", "fstop()")
	xbutton("At end of action", "vstop = 1")
	xmenu()
	xbutton("Cont", "fcontinue()")
	xpanel()
	xpanel("Family")
	xslider(&slideval, 0, 1, "slcmd()", 0, 1)	// slow slider
	xpanel()
	box.intercept(0)
}

proc setval() {
	if (nstep > 1) {
		inc = (xend - xstart)/(nstep-1)
	}else{
		nstep = 1
		inc = 0
	}
}

proc changevar() {
	if (sc.run()) {
		sc.text(variable)
	}
}
proc changegen() {
	string_dialog("Action", generator)
}

proc frun() {
	istep = 0
	fcontinue()
}

proc fcontinue() {
	vstop = 0
	for (istep = istep; istep < nstep; istep = istep + 1) {
		x = xstart + istep * inc
		act()
		if (vstop) break
	}
}

proc act() {
	sprint(cmd, "%s = %g %s", variable, x, generator)
	execute(cmd)
}

proc fstop() {
	vstop = 1
	execute("stoprun=1") // makes sense if we are doing a stdrun run()
}

proc slcmd() {
	x = xstart + slideval*(xend - xstart)
	act()
}

proc save() {
	box.save("load_file(\"family.hoc\", \"Family\")\n}\n{")
	sprint(tempstr, "ocbox_ = new Family(0)")
	box.save(tempstr)
	box.save("}\n{object_push(ocbox_)}\n{")
	sprint(tempstr, "xstart=%g  xend=%g  nstep=%d  setval() build()\n",\
		xstart, xend, nstep)
	box.save(tempstr)
	sprint(tempstr, "variable = \"%s\"", variable)
	box.save(tempstr)
	sprint(tempstr, "generator = \"%s\"", generator)
	box.save(tempstr)
	box.save("}\n{object_pop()}\n{")
	box.save("ocbox_ = ocbox_.box")
}

endtemplate Family

objref tempobj
proc makeFamily(){
	tempobj = new Family()
	objref tempobj
}
