/*
* eval.cc
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#include "state_button.h"
#include "capturer.h"
#include "binaural_drones.h"
#include "console.h"
#include "din.h"
#include "ui_list.h"
#include "fractaliser.h"
#include "keyboard_keyboard.h"
#include "main.h"
#include "mondrian.h"
#include "oscilloscope.h"
#include "recorder.h"
#include "tcl_interp.h"
#include "authors_note.h"

state_button* capturer_t::add () {
#ifdef __EVALUATION__
	if (ncaps == 1) {
		cons << RED << "Can add more mouse captures in the Licensed Version of DIN Is Noise" << eol;
		return 0;
	}
#endif
	state_button* sb = new state_button;
	sb->set_pos (sbx, sby);
	sbx += state_button::SIZE2;
	title.add_child (sb);
	caps.push_back (sb);
	++ncaps;
	return sb;
}

i_binaural_drones::i_binaural_drones () : 
  wav ("binaural-drones-waveform.crv"), 
  waved ("binaural-drones-waveform.ed"), 
  fdrcrv ("fader.crv"), 
  hlp ("binaural-drones.hlp") 
{

  instrument::load ("bd.ins");

#ifdef __EVALUATION__
		name = "binaural-drones [Evaluation Version]";
#else
		name = "binaural-drones";
#endif
	wavlis.sb = this;
	waved.add (&wav, &wavlis);
  extern curve_library wav_lib;
	waved.attach_library (&wav_lib);
	prev_mousex = prev_mousey = 0;
	vol_fader.sol (&fdrcrv);
	pitch_fader.sol (&fdrcrv);
	abort = 0;
  inst = 1;
}


din::din (cmdlist& cl) :
wave ("microtonal-keyboard-waveform.crv"),
waved ("microtonal-keyboard-waveform.ed"),
wavlis (wave_listener::MICROTONAL_KEYBOARD),
win (0, 0, view.xmax, view.ymax),
drone_wave ("drone.crv"),
droneed ("drone.ed"),
dronelis (wave_listener::DRONE),
fm ("fm", "fm.crv"),
am ("am", "am.crv"),
moded ("modulation.ed"),
gatr ("gr", "gater.crv"),
gated ("gater.ed"),
gatlib ("gater-patterns.lib"),
helptext ("din.hlp")
{

  load ("mkb.ins");

#ifdef __EVALUATION__
		name = "microtonal-keyboard [Evaluation Version]";
#else
		name = "microtonal-keyboard";
#endif

    prev_mousex = prev_mousey = delta_mousex = delta_mousey = 0;
    win_mousex = win_mousey = prev_win_mousex = prev_win_mousey = 0;
		tonex = toney = 0;

    current_range = 0;

    adding = 0;

    wanding = 0;

    moving_drones = 0;

    rising = falling = 0;

    n_dvap = 0;
    dvap = 0;
    dcol = 0;

    dap = 0;
    n_dap = 0;

    num_drones = 0;

		create_drone_pend = 0;

		static const int cap = 1024;
    selected_drones.reserve (cap);

		scaleinfo.scl = this;

		fdr_gater_prev_amount = 0;

		rvec.reserve (cap);
		svec.reserve (cap);
		xforming = NONE;

		num_selected_drones = num_browsed_drones = 0;

		ptr_scaleinfo = &all_notes;

		nstepz = 0;
		con_pts = 0;
		con_clr = 0;
		con_size = 0;
		totcon = 0;
		_2totcon = 0;
		ec = 0;

    inst = 1;

    dinfo.cen.lis = this;

}

void din::chuck () {
	if (num_selected_drones > 1) {
#ifdef __EVALUATION__
    if (num_selected_drones > 3) {
      cons << RED << "Chuck with more than 3 drones possible in the Licensed Version of DIN Is Noise" << eol;
      return;
    }
#endif
    int yes = 1;
    drone* sun = selected_drones[0];
    sun->chuck.set (yes, 0);
    drone* planet = 0;
		for (int i = 1; i < num_selected_drones; ++i) {
      sun->trail.set (0);
      planet = selected_drones[i];
      planet->chuck.set (yes, sun);
      sun->chuck.sat = planet;
      planet->chuck.sat = 0;
      planet->chuck.calc (planet);
      sun = planet;
    }
    planet->trail.set (10000);
    cons << GREEN << "Chucked " << num_selected_drones << " drones" << eol;
  } else {
		cons << RED_A2D << eol;
  }
}

void din::create_drone_pendulum () {

#ifdef __EVALUATION__
	cons << RED << "Can create drone pendulums only in the Licensed Version of DIN Is Noise" << eol;
	return;
#endif

	int o = dinfo.drone_pend.orient;
  int no = !o;
	double along[] = {rgn.width, rgn.height};
  int num_drones = dinfo.drone_pend.n;

	CLEAR_SELECTED_DRONES

	double xl [] = {double(rgn.left), double(rgn.midx)};
	double yl [] = {double((rgn.bottom + rgn.top) / 2.0), double(rgn.bottom)};
	double x = xl [o], y = yl [o];
	double* xy [] = {&x, &y};
	double& xyo = *xy[o];
	double depths [] = {double(rgn.top - rgn.midy), double(rgn.right - rgn.midx)};
	double depth = depths[o];

  int nd1 = num_drones - 1;
  double spacing = along[o] * 1.0 / nd1;
	double _1bylast = 1.0 / nd1;
	double a = 0.0f, da = _1bylast;
  float obpm = 0.0f;
	for (int i = 0; i < num_drones; ++i) {
		get_color::data.p = i * _1bylast;
		drone* pd = add_drone (x, y);
		drone& d = *pd;
    if (!dinfo.seloncre) {
		  d.sel = 1;
		  selected_drones.push_back (pd);
    }
		d.mod.active = 1;
		mod_params* mods [] = {&d.mod.am, &d.mod.fm};
		mod_params& mod = *mods[o];
		mod.depth = warp_depth (a) * depth;
    obpm = warp_bpm (a) * dinfo.drone_pend.bpm;
		mod.bv.set_bpm (obpm);
    mods[no]->bv.set_bpm (obpm);
		a += da;
		xyo += spacing;
	}

  if (!dinfo.seloncre) print_selected_drones ();

	drone_pendulum_group* grp = new drone_pendulum_group (o, depth, selected_drones, num_selected_drones);
	drone_pendulums.push_back (grp);

	uis.dpeu.bpm.set_value (dinfo.drone_pend.bpm);
	uis.dpeu.depth.set_value (depth);

	cons << GREEN << "Created a drone pendulum of " << num_drones << " drones." << eol;

}

void din::create_drone_mesh () {
	mkb_selector.mesh = 0;
#ifdef __EVALUATION__
	if (mkb_selector.rowcol > 4) {
		cons << RED << "Can only create a 2 x 2 drone mesh with the Evaluation Version oF DIN Is Noise" << eol;
		return;
	}
#endif
	mkb_selector.orderer = mkb_selector.orderers [dinfo.mesh_vars.order];
	mkb_selector.order_order ();
	amd.triggert = dinfo.mesh_vars.duration * 1.0f / mkb_selector.rowcol;
	amd.i = 0;
	amd.j = mkb_selector.cols;

  CLEAR_SELECTED_DRONES
	amd.start ();

}

void din::change_range_note (int i, int d) {
#ifdef __EVALUATION__
	cons << RED << "Change Left/Right Note/Octave is available in the Licensed Version of DIN Is Noise" << eol;
	return;
#endif
	int cn = dinfo.change_note;
	scale_info& si = *ptr_scaleinfo;

	range& sr = ranges [dinfo.sel_range];
	if (cn)
		sr.change_note (i, d, si);
	else
		sr.change_octave (i, d, si);

	int ri, rj;
	ri = rj = dinfo.sel_range;
	if (i) {
		int srr = dinfo.sel_range + 1;
		if (srr < num_ranges) {
			range& rsrr = ranges [srr];
			if (cn) 
				rsrr.change_note (0, d, si);
			else
				rsrr.change_octave (0, d, si);
			ri = dinfo.sel_range; rj = srr;
		}	
	} else {
		int srl = dinfo.sel_range - 1;
		if (srl > -1) {
			range& rsrl = ranges [srl];
			if (cn) 
				rsrl.change_note (1, d, si);
			else
				rsrl.change_octave (1, d, si);
			ri = srl; rj = dinfo.sel_range;
		}
	}

	if (ri == rj) 
		refresh_drones (ri); 
	else 
		refresh_drones (ri, rj);

	note& L = sr.notes[0];
	note& R = sr.notes[1];
	sprintf (BUFFER, "Left Note = %s @ %0.3f Hz, Right Note = %s @ %0.3f Hz, Hz/pixel = %0.3f", L.name.c_str(), L.hz, R.name.c_str(), R.hz, sr.hz_per_pix ());
	cons << YELLOW << BUFFER << eol;

}

int din::connect_drones () {

	if (num_selected_drones < 2) {
		cons << RED_A2D << eol;
		return 0;
	}

#ifdef __EVALUATION__
	#define MAX_CONN 4
	if (num_selected_drones > MAX_CONN) {
		cons << RED << "Can only connect upto " << MAX_CONN << " drones in the Evaluation Version of DIN Is Noise" << eol;
		return 0;
	}
#endif

	if (nstepz == 0) {
		cons << RED << "Bad Steps value, please check Menu > Drone Tools > Steps" << eol;
		return 0;
	}

	for (int s = 0; s < nstepz; ++s) {
		int ds = stepz[s];
		for (int i = 0, j = 0; i < num_selected_drones; ++i) {
			j = i + ds;
			if (MENU.cb_conn_wrap.state) j %= num_selected_drones;
			if (j < num_selected_drones) {
				drone* pdi = selected_drones[i];
				drone& di = *pdi;
				drone* pdj = selected_drones[j];
				drone& dj = *pdj;
				if (can_connect (pdi, pdj)) {
					double m = magnitude (pdi->cx, pdi->cy, pdj->cx, pdj->cy);
					di.connections.push_back (pdj);
					dj.connections.push_back (pdi);
					di.mags.push_back (m);
					dj.mags.push_back (m);
					++di.nconn;
					++dj.nconn;
					totcon += 2;
				} 
			} 
		}
	}

	_2totcon = 2 * totcon;
	alloc_conns ();

  if (MENU.trackcon.state) {
    int last = num_selected_drones - 1;
    drone* pld = selected_drones [last];
    drone *pdi = 0, *pdj = 0;
    for (int i = 0, j = 1; i < last; ++i, ++j) {
      pdi = selected_drones [i];
      pdj = selected_drones [j];
      push_back (trackers, pdi);
      pdi->tracking = drone::POINT;
      pdi->tracked_drone = pdj;
    }
    pld->tracking = drone::USE;
    pld->tracked_drone = pdi;
    push_back (trackers, pld);
  }

	return 1;

}

int plugin::apply (multi_curve& crv) {

#ifdef __EVALUATION__
	cons << RED << "Can apply plugins only in the Licensed Version of DIN Is Noise" << eol;
	return 0;
#endif

  int n = points.size ();
  if (n == 0) return 0;

  crv.clear (0);

	for (int i = 0; i < n; ++i) {
    point<float>& p = points[i];
    crv.add_vertex (p.x, p.y);
    crv.add_left_tangent (p.x, p.y);
    crv.add_right_tangent (p.x, p.y);
	}

  if (shapeform) 
		crv.set_shapeform (1); 
	else 
		crv.evaluate ();

	CRVED->set_curve_style (&crv);

  return 1;

}

int fractaliser::apply (multi_curve& crv) { // needed bcos of tangents
#ifdef __EVALUATION__
	cons << RED << "Can apply plugins only in the Licensed Version of DIN Is Noise" << eol;
	return 0;
#endif
  render ();
  int npts = points.size ();
  if (npts == 0) return 0;
  crv.clear (0);
	// if (change_curve_name) crv.set_name (ss.str());

	typedef std::list< point<float> >::iterator points_list_iterator;
	points_list_iterator lti = left_tangents.begin (), rti = right_tangents.begin ();
  for (int i = 0; i < npts; ++i) {
    point<float>& p = points[i];
    point<float>& lt = *lti++;
    point<float>& rt = *rti++;
    crv.add_vertex (p.x, p.y);
    crv.add_left_tangent (lt.x, lt.y);
    crv.add_right_tangent (rt.x, rt.y);
  }
  crv.set_shapeform (shapeform);
  crv.evaluate ();
  return 1;
}

keyboard_keyboard::keyboard_keyboard () :
  wave ("keyboard-keyboard-waveform.crv"), 
  waved ("keyboard-keyboard-waveform.ed"), 
  wavlis (wave_listener::KEYBOARD_KEYBOARD),
  attackcrv ("attack.crv"), decaycrv ("decay.crv"),
  attacked ("attack.ed"), decayed ("decay.ed"),
  helptext ("keyboard-keyboard.hlp"), velcrv ("velocity.crv"), 
  veled ("velocity.ed"), vellib ("velocity.lib") 
{

  load ("kbkb.ins");

#ifdef __EVALUATION__
		name = "keyboard-keyboard [Evaluation Version]";
#else
		name = "keyboard-keyboard";
#endif
  last_mousex = last_mousey = -1;
  num_triggered_notes = 0;
	scaleinfo.scl = this;
	show_nearby_notes = 0;

	fname = "keyboard-keyboard.settings";
	ifstream file ((user_data_dir + fname).c_str (), ios::in);
	if (!file) {
		dlog << "!!! couldnt load: " << fname << endl;
	} else {
		string ignore;
		file >> ignore >> show_nearby_notes;
		file >> ignore >> ATTACK_TIME;
		file >> ignore >> DECAY_TIME;
		file >> ignore >> NOTE_VOLUME;
		file >> ignore >> PITCH_BEND;
		file >> ignore >> trig_what;
		PITCH_BEND_PER_PIXEL = 0.01f * PITCH_BEND;
	}

  inst = 1;

}


extern const char* INSTRUMENTS [];
extern const char* INSTRUMENTS_SHORT [];
extern const int NUM_INSTRUMENTS, LAST_INSTRUMENT;
extern int CURRENT_INSTRUMENT;
extern string INSTRUMENT;
extern instrument* INSTRUMENT_PTR [];
extern checkbutton* LAST_TABS [];
extern oscilloscope scope;

void load_instrument (instrument* inst) {

  if (inst == 0) inst = get_current_instrument ();

  MENU.b_microtonal_keyboard.turn_off (DONT_CALL_LISTENER);
  MENU.b_keyboard_keyboard.turn_off (DONT_CALL_LISTENER);
  MENU.b_mondrian.turn_off (DONT_CALL_LISTENER);
	MENU.b_binaural_drones.turn_off (DONT_CALL_LISTENER);

  if (inst == &keybd2) {
    MENU.b_keyboard_keyboard.turn_on ();
		MENU.next_tab = MENUP.cb_instrument;
    CURRENT_INSTRUMENT = 0;
  } else if (inst == &din0) {
    CURRENT_INSTRUMENT = 1;
    MENU.b_microtonal_keyboard.turn_on ();
		checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
		if (pcb) {
      MENU.next_tab = pcb; 
    } else {
      if (din0.num_selected_drones) 
        MENU.next_tab = MENUP.cb_mkb_drone_params;
      else
        MENU.next_tab = MENUP.cb_mkb_voice;
    }
  } else if (inst == &mondrian0) {
    CURRENT_INSTRUMENT = 2;
    MENU.b_mondrian.turn_on ();
    MENU.cb_instrument.turn_on ();
		checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
		if (pcb) MENU.next_tab = pcb; else MENU.next_tab = MENUP.cb_mon_tools;
  } else /*if (inst == &binaural_drones0)*/ {
    CURRENT_INSTRUMENT = 3;
		MENU.b_binaural_drones.turn_on ();
		MENU.cb_instrument.turn_on ();
		checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
		if (pcb) MENU.next_tab = pcb; else MENU.next_tab = MENUP.cb_binaural_drones_tools;
	}

  scope.load_current_instrument ();
  MENU.scol.setup ();

	MENU.next_tab_instr = inst;
	uis.set_current (inst);
  MENU.hide_editors ();
  MENU.show_editors (inst);

#ifdef __EVALUATION__
	if (dynamic_cast<ui*>(inst) != &anote)
#endif

  MENU.setup_tabs (inst);

  setup_plugin_labels ();
	cons ("setup-editors");
  uis.update_bottom_line ();

}

extern string VERSION_NUMBER;
extern string APP_NAME;

void make_app_name () {
	#ifdef __EVALUATION__
		APP_NAME = "DIN Is Noise " + VERSION_NUMBER + " | EVALUATION VERSION";
	#elif defined __LICENSED__
		APP_NAME = "DIN Is Noise " + VERSION_NUMBER + " | LICENSED VERSION";
	#endif
}

void range_mod_lis::clicked (button& b) {
	if (&b == MENUP.b_rm_start_all) {
#ifdef __EVALUATION__
		cons << RED << "Range modulation available only on the Licensed Version of DIN Is Noise" << eol;
		return;
#endif
		din0.set_ran_mod (1);
	} else if (&b == MENUP.b_rm_stop_all) {
		din0.set_ran_mod (0);
	} else if (&b == MENUP.b_rm_toggle) {
#ifdef __EVALUATION__
		cons << RED << "Range modulation available only on the Licensed Version of DIN Is Noise" << eol;
		return;
#endif
		din0.toggle_ran_mod ();
	} else if (&b == MENUP.b_rm_pause_resume) {
#ifdef __EVALUATION__
		cons << RED << "Range modulation available only on the Licensed Version of DIN Is Noise" << eol;
		return;
#endif
		din0.pause_resume_ran_mod ();
	} else {
		din0.dinfo.sel_range = din0.current_range;
		MENU.load_range (din0.dinfo.sel_range);
	}
}

void range_mod_lis::changed (checkbutton& cb) {

	if (&cb == MENUP.cb_mod_ran) {
#ifdef __EVALUATION__
		cons << RED << "Range modulation available only on the Licensed Version of DIN Is Noise" << eol;
		cb.set_state (!cb.state, 0);
		return;
#endif
		modulator& srm = din0.ranges[din0.dinfo.sel_range].mod;
		int& a = srm.active;
		if (a) a *= cb.state; else a = cb.state;
	} else 
		din0.dinfo.mark_sel_range = cb.state;
}

void recording_listener::clicked (button& b) {
	if (&b == MENUP.b_clear_record) {
		recorder0.clear ();
		uis.cb_record.set_text ("Record");
		MENU.cb_record.set_text ("Record");
		if (MENU.show) MENU.toggle ();
	} else {
#ifdef __EVALUATION__
		cons << RED << "Can save recordings only in the Licensed Version of DIN Is Noise" << eol;
		return;
#endif
		recorder0.start_saving ();
	}
}


void binaural_drones_listener::clicked (button& b) {

	if (&b == MENUP.bbd_select_all) {
		MENU.il_binaural_drones.select (1); // 1 == all
	} else if (&b == MENUP.bbd_select_none) {
		MENU.il_binaural_drones.select (0); // 0 == none
		//MENU.il_binaural_drones.last = 0;
	} else if (&b == MENUP.bbd_invert_select) {
		MENU.il_binaural_drones.invert_select ();
		//if (MENU.il_binaural_drones.num_selected() == 0) MENU.il_binaural_drones.last = 0;
	} else if (&b == MENUP.bbd_select2) {
		if (select_rule == ID) {
			tokenizer tv (MENU.bdf_value.text);
			int s, e, i; tv >> s >> e >> i;
			--s; --e;  // bcos starts at 1 on ui :(
			clamp (0, s, MENU.il_binaural_drones.n);
			clamp (0, e, MENU.il_binaural_drones.n);
			clamp (1, i, MENU.il_binaural_drones.n);
			sprintf (BUFFER, "wrap-get-nums %d %d %d", s, e, i);
		} else {
			const char* get [] = {"left", "right", "separation", "volume"};
			const char* op [] = {"==", ">=", "<=", "<>"};
			sprintf (BUFFER, "filter-binaurals %s %s %s", get[select_what], op[select_rule], MENU.bdf_value.text.c_str());
		}
		interpreter (BUFFER);
		tokenizer tz (interpreter.result);
		if (!MENU.il_binaural_drones.select_these (tz)) cons << RED << "No matching binaural drone pairs found!" << eol;
	}

	if (binaural_drones0.busy ()) {
		cons << RED << "Still executing your command, please wait or ESC to abort" << YELLOW << eol;
		return;
	}

	binaural_drones0.separation = MENU.sp_bd_separation.f_value;
	binaural_drones0.master_volume = (float)MENU.lf_master_volume.fld / 100.0f; 
	binaural_drones0.pairs = MENU.sp_bd_pairs.f_value;
	binaural_drones0.close_octave = MENU.cb_close_octave.state;
	binaural_drones0.resize_separation = MENU.cb_resize_separation.state;

	const char* selerr = "Please select some binaural drone pairs";
	if (&b == MENUP.b_create_binaurals_on_notes) {

#ifdef __EVALUATION__
	cons << RED << "Available only in the Licensed Version of DIN Is Noise" << eol;
	return;
#endif

		cons << YELLOW << "Creating binaural drones [fading in], please wait" << eol;
		float tonic;
		if (binaural_drones0.keynote == i_binaural_drones::START_PITCH) 
			tonic = binaural_drones0.starting_pitch; 
		else 
			tonic = get_tonic (&binaural_drones0);
		string intervals; 
		vector<string>& notes = binaural_drones0.scaleinfo.notes;
		int j = notes.size (); if (binaural_drones0.close_octave == 0) --j;
		for (int i = 0; i < j; ++i) intervals = intervals + notes[i] + " ";
		stringstream cmd;
		cmd << "create-binaurals-on-notes " << tonic << ' ' << binaural_drones0.separation << " {" << intervals << "}" << ' ' << binaural_drones0.resize_separation;
		interpreter (cmd.str());
		MENU.changed (MENU.cb_binaural_drones_edit);
	} else if (&b == MENUP.b_create_binaurals_from_pitch) {

#ifdef __EVALUATION__
	if (binaural_drones0.num_binaural_drones || binaural_drones0.pairs > 1) {
		cons << RED << "Can create > 1 binaural drone pair in the Licensed Version of DIN Is Noise" << eol;
		return;
	}
#endif

		cons << YELLOW << "Creating binaural drones [fading in], please wait" << eol;
		string start_pitch = MENU.lf_bd_start_pitch.fld.text;
		string spacing = MENU.lf_bd_spacing.fld.text;
		sprintf (BUFFER, "create-binaurals-from-pitch %f %f %d %f", binaural_drones0.starting_pitch, binaural_drones0.separation, binaural_drones0.pairs, binaural_drones0.spacing); 
		interpreter (BUFFER);
		MENU.changed (MENU.cb_binaural_drones_edit);
	} else if (&b == MENUP.bbd_sync) {
		if (MENU.il_binaural_drones.num_selected ()) {
			cons << YELLOW << "Syncing (fade-out + sync + fade-in) : please wait or ESC to abort" << eol;
			interpreter ("sync-selected-binaurals");
		} else {
			cons << RED << selerr << eol;
		}
	} else if (&b == MENUP.bbd_delete) {
		if (MENU.il_binaural_drones.num_selected ()) {
			cons << YELLOW << "Deleting (after fade-out) : please wait or ESC to abort" << eol;
			interpreter ("delete-selected-binaurals");
		} else {
			cons << RED << selerr << eol;
		}
	} else if (&b == MENUP.bd_modulate_up) {
		if (MENU.il_binaural_drones.num_selected ()) {
			cons << YELLOW << "Modulating up: please wait or ESC to abort" << eol;
			sprintf (BUFFER, "modulate-selected-binaurals %f", binaural_drones0.modulation_amount);
			interpreter (BUFFER);
		} else {
			cons << RED << selerr << eol;
		}
	} else if (&b == MENUP.bd_modulate_down) {
		if (MENU.il_binaural_drones.num_selected ()) {
			cons << YELLOW << "Modulating down: please wait or ESC to abort" << eol;
			sprintf (BUFFER, "modulate-selected-binaurals %f", 1.0f / binaural_drones0.modulation_amount);
			interpreter (BUFFER);
		} else {
			cons << RED << selerr << eol;
		}
	} else if (&b == MENUP.bbd_flip) {
		if (MENU.il_binaural_drones.num_selected ()) {
			for (int i = 0; i < MENU.il_binaural_drones.n; ++i) {
				if (MENU.il_binaural_drones.items[i].sel) {
					binaural_drone* bi = binaural_drones0.binaural_drones[i];
					float r_hz = bi->l_hz, l_hz = bi->r_hz;
					bi->set_hz (binaural_drone::LEFT, l_hz);
					bi->set_hz (binaural_drone::RIGHT, r_hz);
				}
			}
			binaural_drones0.pitch_fader.start ("Pitch flip");
			cons << YELLOW << "Flipping Hz: please wait or ESC to abort" << eol;
			interpreter ("flip-selected-binaurals");
		} else {
			cons << RED << selerr << eol;
		}
	}
}


void mondrian::load_settings  (ifstream& file) {

  std::string ignore;

#ifdef __EVALUATION__
		name = "Mondrian [Evaluation Version]";
#else
		name = "Mondrian";
#endif

  float l, b, r,  t;
  file >> ignore >> l >> b >> r >> t;
  win.set (l, b, r, t);

  file >> ignore >> win_chunk.x >> win_chunk.y;
  file >> ignore >> obj_chunk.x >> obj_chunk.y;

  win_per_obj (win_chunk.x / obj_chunk.x, win_chunk.y / obj_chunk.y);
  obj_per_win (obj_chunk.x / win_chunk.x , obj_chunk.y / win_chunk.y);

  file >> ignore >> win_resolution;

  /*float dummy1 = 0, dummy2 = 0;
  win2obj (win_resolution, dummy1, obj_resolution, dummy2);*/

	file >> ignore >> label_hz_vol;
	file >> ignore >> min_voices;
	file >> ignore >> auto_del_rect.active;
	file >> ignore >> auto_del_rect.triggert;
	file >> ignore >> auto_split_rect.active;
	file >> ignore >> auto_split_rect.triggert;
	file >> ignore >> auto_split_orient;
	file >> ignore >> auto_split_at;
	file >> ignore >> split_leaf;
	file >> ignore >> delete_leaf;
	file >> ignore >> mondrian::min_split_size;
	file >> ignore >> draw__boxes;
	file >> ignore >> draw_ball.position;
	file >> ignore >> draw_ball.heading;
	file >> ignore >> draw__notes;
	file >> ignore >> label_notes;
	file >> ignore >> fill_boxes;
	file >> ignore >> num_boxes;
	file >> ignore >> added_ball_type;
	file >> ignore >> cursor;
	file >> ignore >> auto_adjust_voices;
	file >> ignore >> slit::HALF_SIZE;
	file >> ignore >> MENU.cb_turn_sync.state;
	file >> ignore >> MENU.cb_speed_sync.state;
  file >> ignore >> MENU.texture.state;
  file >> ignore >> tuning;
	MENU.cb_turn_sync.set_state (MENU.cb_turn_sync.state);
	MENU.cb_speed_sync.set_state (MENU.cb_speed_sync.state);
  MENU.texture.set_state (MENU.texture.state);

}


extern float MIN_TIME;
extern int can_wheel ();
extern int IPS;

int mondrian::handle_input () {

  if (moving_balls) move_balls (win.mousex - win.mousex_prev, win.mousey - win.mousey_prev);
	if (editing_slit) slit_lip.edit (); else 
	if (editing_edge) set_edge (hit, edge, win.mousex, win.mousey);

  if (lmb) {
		if (lmb_clicked == 0) {
			if (stop_moving_balls ()); 
			else if (stop_editing_slit ());
			else if (stop_editing_edge ());
			else if (try_slitting ());
			else {
				finding f; find (root, win.mousex, win.mousey, f); 
				hit = f.found; 
				if (hit) { // box hit
					box<float>& bf = hit->extents;
					edge = bf.get_edge_hit (win.mousex, win.mousey, gutter2);
					if (edge != edge::NONE) { // edge hit
						// slit hit?
						slit_lip.slitt = 0;
						float* curs [rect::nedges] = {&win.mousex, &win.mousey, &win.mousex, &win.mousey};
						slit_lip.cur = curs[edge];
						if (get_slit_lip (slit_lip, hit, edge, *slit_lip.cur)) { // slit hit!
							float* prevs [rect::nedges] = {&win.mousex_prev, &win.mousey_prev, &win.mousex_prev, &win.mousey_prev};
							slit_lip.prev = prevs[edge];
							toggle_flag (editing_slit, "Just move mouse to edit slit. ESC to stop."); // edit this slit
						} else toggle_flag (editing_edge, "Just move mouse to move edge. ESC or click to stop."); // edit this edge
						mon_selector.abort ();
					} 
				}
			}
		}

		if (adding_balls) { // user is adding balls
      if (started_making_ball == 0) { 
        started_making_ball = 1;
        new_ball = new ball (added_ball_type);
        new_ball->set_velocity (1.0f, 1.0f);
				mon_selector.abort ();
      } else {
				float dx, dy; 
				win.diff_mouse (dx, dy);
				if (dx == 0 && dy == 0); else new_ball->set_velocity (dx, dy); 
				new_ball->x = win.mousex;
				new_ball->y = win.mousey;
      }
    } 		

		lmb_clicked = 1;

  } else {
		lmb_clicked = 0;
		if (new_ball) {
			new_ball->frozen = 0;
			finding f; find (root, new_ball->x, new_ball->y, f);
#ifdef __EVALUATION__
			if (num_balls) {
				cons << RED << "Can create more balls in the Licensed Version of DIN Is Noise" << eol;
				f.found = 0;
			}
#endif
			if (f.found) {
				new_ball->R = f.found;
				f.found->balls.push_back (new_ball);
				balls.push_back (new_ball);
				browse.clear ();
				clear_selected<ball> (selected_balls, num_selected_balls); 
				new_ball->select = 1;
				selected_balls.push_back (new_ball);
				++num_selected_balls;
				++num_balls;
				after_selection ();
			} else 
				delete new_ball;

			started_making_ball = 0;
    	new_ball = 0;
		}
  }

	static const double reptf = 1./7, repts = 1./48.;
	double repts1 = 1./IPS;

	// octave shift
  if (keypressed (SDLK_z)) {if (!modulate_balls (-1)) modulate_down ();}
  else if (keypressed (SDLK_x)) {if (!modulate_balls (1)) modulate_up ();}

  // split box 
  if (keypressed (SDLK_r)) { // vertically
    if (recting ()) ; else {
      if (SHIFT) 
        multi_split_rect (split::VERTICAL); // vertically at notes
      else if (CTRL) 
        multi_split_rect (num_boxes, split::VERTICAL); // vertically make num_boxes
      else 
        split_rect (split::VERTICAL, win.mousex); // into 2 new boxes
    }
	}
  else if (keypressed (SDLK_f)) { // horizontally
    if (recting ()) ; else {
      if (SHIFT) 
        multi_split_rect (split::HORIZONTAL); // horizontally at notes
      else if (CTRL)
        multi_split_rect (num_boxes, split::HORIZONTAL); // horizontally make num_boxes
      else 
        split_rect (split::HORIZONTAL, win.mousey); // into 2 new boxes
    }
	}
	else if (keypressed (SDLK_t)) {
    if (recting ()) ; else {
      if (SHIFT || CTRL) 
        make_nxn_grid (); // make grid of num_boxes x num_boxes
      else
        make_note_grid (); // make note grid
    }
	}

	if (keypressed(SDLK_RETURN)) toggle_triggered_sound ();

	// change ball speed
	static const float delta_speed = 0.25;
  if (keypressedd (SDLK_LEFTBRACKET)) change_speed (MENU.sp_mondrian_change_speed, -delta_speed);
  else if (keypressedd (SDLK_RIGHTBRACKET)) change_speed (MENU.sp_mondrian_change_speed, delta_speed);

	// change ball attack & decay time
	if (keypressedd (SDLK_SEMICOLON, reptf, repts)) {
		if (SHIFT) {
			delta_attack_time -= MIN_TIME;
			if (delta_attack_time < MIN_TIME) delta_attack_time = MIN_TIME;
      sprintf (BUFFER, "delta attack time = %0.3f secs", delta_attack_time);
      cons << BUFFER << eol;
		} else 
			--MENU.sp_mondrian_change_attack_time;
	}
	else if (keypressedd (SDLK_QUOTE, reptf, repts)) {
    if (SHIFT) {
      delta_attack_time += MIN_TIME;
      sprintf (BUFFER, "delta attack time = %0.3f secs", delta_attack_time);
      cons << BUFFER << eol;
    } else 
      ++MENU.sp_mondrian_change_attack_time;
	}
	else if (keypressedd (SDLK_COMMA, reptf, repts)) {
     if (SHIFT) {
       delta_decay_time -= MIN_TIME;
       if (delta_decay_time < MIN_TIME) delta_decay_time = MIN_TIME;
       sprintf (BUFFER, "delta decay time = %0.3f secs", delta_decay_time);
       cons << BUFFER << eol;
     } else 
       --MENU.sp_mondrian_change_decay_time;
	}
	else if (keypressedd (SDLK_PERIOD, reptf, repts)) {
      if (SHIFT) {
        delta_decay_time += MIN_TIME;
        sprintf (BUFFER, "delta decay time = %0.3f secs", delta_decay_time);
        cons << BUFFER << eol;
      } else 
        ++MENU.sp_mondrian_change_decay_time;
	}

	// change ball course
	if (keypressedd (SDLK_o, reptf, repts1)) {
		if (CTRL) 
			toggle_auto_rotate (1); // always change anti-clockwise
		else  if (SHIFT) {
			change_ball_dtheta (-1);
		}
		else 
			rotate_velocity (+1);
	} else if (keypressedd (SDLK_p, reptf, repts1)) {
		if (CTRL) 
			toggle_auto_rotate (-1); // always change clockwise
		else if (SHIFT) {
			change_ball_dtheta (+1);
		}
		else 
			rotate_velocity (-1); // rotate velocity vector clockwise
	}

	// ball / slit selection
  if (keypressed (SDLK_l)) { 
    select_all_targets ();
  } else if (keypressed (SDLK_i)) {
    if (SHIFT) 
			MENU.cb_label_hz_vol.toggle (); // label pitch/volume of triggered notes
		else
			invert_selected_targets ();
	} else if (keypressed (SDLK_k)) { // box
		select_box_targets ();
	} else if (keypressed (SDLK_n)) {
		clear_selected_targets ();
	}

  // ball browsing
  if (keypressedd (SDLK_LEFT)) browse_ball (-1); // MENU.bolis.picked (MENU.ol_browse_balls.option, -1);
  else if (keypressedd (SDLK_RIGHT)) browse_ball (+1); // MENU.bolis.picked (MENU.ol_browse_balls.option, +1);
  
  // freeze/thaw balls
  if (keypressed (SDLK_SPACE)) {
		list<ball*>& balls = get_balls ();
		if (SHIFT) freeze_balls (balls); 
		else if (CTRL) thaw_balls (balls);
		else freeze_thaw_balls (balls); 
	}
  
	if (keypressed (SDLK_j)) flip_velocity (); // flip ball velocity ie flips ball direction
	
	if (keypressedd (SDLK_KP_PLUS, reptf, repts)) ++MENU.sp_mondrian_change_slit_size;
	else if (keypressedd (SDLK_KP_MINUS, reptf, repts)) --MENU.sp_mondrian_change_slit_size; 

	if (keypressedd (SDLK_MINUS, reptf, repts)) --MENU.sp_mondrian_change_trail_size;
	else if (keypressedd (SDLK_EQUALS, reptf, repts)) ++MENU.sp_mondrian_change_trail_size;

  if (keypressed (SDLK_b)) {
		if (SHIFT) do_add_balls (ball::WRECKER);
		else if (CTRL) do_add_balls (ball::HEALER);
		else do_add_balls (); // add bouncers
	}

  if (keypressed (SDLK_m)) do_move_balls (); // move balls
	if (keypressed (SDLK_c)) delete_selected_targets ();

	if (keypressedd (SDLK_y)) change_min_voices (-1);
	else if (keypressedd (SDLK_u)) change_min_voices (+1);
	
	if (keypressed (SDLK_F9)) remove_slits_on_current_edge ();
	else if (keypressed (SDLK_F10)) remove_slits_on_current_box ();
	else if (keypressed (SDLK_F11)) remove_slits_on_boxes_with_balls ();
	else if (keypressed (SDLK_F12)) remove_all_slits ();

	if (keypressed (SDLK_F3)) toggle_balls_type (ball::WRECKER);
	else if (keypressed (SDLK_F4)) toggle_balls_type (ball::HEALER);
	else if (keypressed (SDLK_F5)) toggle_balls_type (ball::BOUNCER);
	else if (keypressed (SDLK_F6)) switch_balls_type ();
	else if (keypressed (SDLK_F7)) select_type (ball::WRECKER);
	else if (keypressed (SDLK_F8)) select_type (ball::HEALER);

	if (keypressed (SDLK_v)) {
    if (recting()) ; else {
      if (SHIFT) delete_all_rects = !delete_all_rects;
      else 
        delete_current_rect ();
    }
	}

	if (keypressed (SDLK_g)) {
		MENU.monl.picked (MENU.ol_selection_targets.option, +1);
		if (sel_tar == SELECT_BALLS) 
			cons << GREEN << "Selection target: balls" << eol; 
		else 
			cons << GREEN << "Selection target: slits" << eol;
	}

	if (keypressed (SDLK_h)) toggle_slit_anim ();

  if (!mouse_slider0.active) {
    if (keypressedd (SDLK_9)) {
        if (SHIFT) --MENU.sp_mondrian_change_note_poly_radius; else --MENU.sp_mondrian_change_note_poly_points;
    } else if (keypressedd (SDLK_0)) {
      if (SHIFT) ++MENU.sp_mondrian_change_note_poly_radius; else ++MENU.sp_mondrian_change_note_poly_points;
    }
  }

	if (keypressed (SDLK_SLASH)) {if (!mouse_slider0.active) start_slitting ();}
	else if (keypressedd (SDLK_INSERT)) ++MENU.sp_mondrian_change_slit_anim_time;
	else if (keypressedd (SDLK_DELETE)) --MENU.sp_mondrian_change_slit_anim_time;

  if (keypressed (SDLK_F1)) _help ();

	// movement
	if (can_wheel ()) do_zoom (-wheel * zoom);
	double pan_rept = window::PAN_REPEAT, zoom_rept = window::ZOOM_REPEAT;
	if  (keypressedd (SDLK_a, pan_rept, pan_rept)) do_panx (-pan); 
	else if (keypressedd (SDLK_d, pan_rept, pan_rept)) do_panx (+pan);
	else if (keypressedd (SDLK_w, pan_rept, pan_rept)) do_pany (+pan);
	else if (keypressedd (SDLK_s, pan_rept, pan_rept)) do_pany (-pan);

  if (!mouse_slider0.active) {
    if (keypressedd (SDLK_q, zoom_rept, zoom_rept)) do_zoom (+zoom); // zoom out
    else if (keypressedd (SDLK_e, zoom_rept, zoom_rept)) do_zoom (-zoom);  // zoom in
  }

  return 1;
}

void mondrian::clone_ball (ball* b) {
#ifdef __EVALUATION__
      if (num_balls) cons << RED << "Can clone balls in the Licensed Version of DIN Is Noise" << eol;
      return;
#endif
  ball* cb = new ball;
  b->clone_this (cb);
  locate_ball (cb);
  balls.push_back (cb);
  ++num_balls;
}


state_button* point_modulator::add (hit_t& h) {
#ifdef __EVALUATION__
	if (nlst == 1) {
		cons << RED << "Can add more point modulators in the Licensed Version of DIN Is Noise" << eol;
		return 0;
	}
#endif
	on_lst (_desel);
	state_button* sb = new state_button;
	sb->set_pos (sbx, sby);
	sbx += state_button::SIZE2;
	title.add_child (sb);
	const float bpm = 5.0f;
	mod_dat md (h, sb, bpm);
	lst.push_back (md);
	++nlst;
	sb->set_listener (this);
	sb->set_state (1);

	set_title ();
	return sb;
}


int ui_list::handle_input () {

  // handle quit
  //
  #ifdef __MACOSX_CORE__
  if (keydown (SDLK_LMETA) && keypressed (SDLK_q)) { // command key + q  on mac os x
    quit = IMMEDIATE;
    return 1;
  }
	#else 
	if (ALT && keypressed (SDLK_F4)) { // alt + f4 on windows & linux
    quit = IMMEDIATE;
		return 1;
	}
	#endif

  
  // handle widgets
	//
  basic_editor::hide_cursor = mouse_slider0.active;
	if (widget::focus) { // handle input of widget with focus
		widget::focus->handle_input ();
		return 1;
	} else {
		vector<widget*> widgets = widgets_of [current];
		for (vector<widget*>::size_type i = 0, j = widgets.size (); i < j; ++i) {
			widget* wi = widgets[i];
			if (wi->visible && wi->enabled) {
				wi->handle_input ();
				if (widget::focus) return 1;
			}
		}
	}

	// toggle menu
	// 
	#ifdef __EVALUATION__
		if (current != &anote) { // no menu on author's note
	#endif
		if (rmb) {
			if (!rmb_clicked) {
				rmb_clicked = 1;
				if (escape_from_things ());
				else main_menu.toggle ();
			}
		} else rmb_clicked = 0;
	#ifdef __EVALUATION__
	}
	#endif

  // handle current screen 
  current->handle_input ();

  if (keypressed (SDLK_BACKQUOTE)) { // flip to last screen
    if (prev && !mouse_slider0.active) {
      if (prev->ed) load_editor (prev);
      else if (prev->inst) load_instrument (dynamic_cast<instrument*>(prev));
      else set_current (prev); // settings screen for now
    }
  } 

  if (mouse_slider0.active == 0) {
    if (keypressed (SDLK_1)) { // switch instrument
      if (current->inst) goto_next_instrument (); // else back to current instrument
      load_instrument ();
    } else { // load editors attached to keys 2 - 8
      for (int i = 0; i < MAX_EDITORS; ++i) {
        if (keypressed (key[i])) {
          ui* edi = ed[i];
          if (edi) load_editor (edi);
        }
      }
    }
  }
	
	/*if (keypressed(SDLK_RETURN)) { 
    if (alt_down ()) { // clear recording
			main_menu.recl.clicked (main_menu.b_clear_record);
    } else if (CTRL) { // start recording
			if (cb_record.state == 0) cb_record.turn_on (); else cb_record.turn_off ();
    }
	}*/

	if (keypressed (SDLK_F2)) {
		if (UI_OFF) turn_on_ui (); else turn_off_ui ();
	}

	if (keypressed (SDLK_PAUSE)) {
		scope.visible = !scope.visible;
		main_menu.cb_scope.set_state (scope.visible, 0);
	}

	if (keypressed (SDLK_MENU)) main_menu.mbl.clicked (main_menu.b_menu);

  if (keypressed (SDLK_ESCAPE)) { 
    if (UI_OFF) turn_on_ui ();
    else if ( escape_from_things() ) ; 
		else { // ask to press esc again to really quit
			esc:
			if (esct == -1) {
				cons << console::red << "Press ESC again to quit" << eol;
				esct = ui_clk();
			} else {
				double dt = ui_clk() - esct;
				if (dt > 1.0) { // 1 sec timeout
					esct = -1;
					goto esc;
				} else if (quit != TRY) try_quit ();
			}
		}
  }

  return 1;

}

extern ui* SCREENS [];
void ui_list::setup () {

	// all screens
	int nscreens = 41;
	uis.resize (nscreens);
	uis[0] = 0;
	ui** si = SCREENS;
	for (int i = 1; i < nscreens; ++i, ++si) uis[i] = *si;

	// bottom line
  int dirs [] = {arrow_button::left, arrow_button::right, arrow_button::up, arrow_button::down};
  arrow_button* scrl [] = {&ab_scroll_left, &ab_scroll_right, &ab_scroll_up, &ab_scroll_down};
  for (int i = 0; i < 4; ++i) {
    arrow_button* si = scrl[i];
    si->set_dir (dirs[i]);
    si->set_size (12);
    si->set_listener (&sal);
    si->click_repeat = 1;
    si->first_repeat_time = 0.005;
    si->subsequent_repeat_time = 0.015;
  }

  cb_voice.set_listener (&vlis);
  cb_voice.colorize (0);

  cb_gater.set_listener (&glis);
  cb_gater.colorize (0);

  cb_delay.set_listener (&dlis);
  cb_delay.colorize (0);

  cb_compress.set_listener (&clis);

  b_settings.set_listener (&slis);

	cb_record.set_listener (&main_menu.recl);

	sp_voices.set ("Voices", 1, 1, MILLION, &vov, 0);
	sp_attack_time.set ("Attack time", 0.1f, 0.0f, MILLION, &atv, 0);
	sp_decay_time.set ("Decay time", 0.1f, 0.0f, MILLION, &dkv, 0);
	sp_pitch_bend.set ("Hz/Pixel", 0.01f, 0.0f, MILLION, &pbl, 0);
	sp_octave_shift_bpm.set ("BPM", 1, 0.0f, MILLION, &osl, 0);

	cb_show_nearby_notes.set_listener (&pbl);
	cb_show_nearby_notes.turn_off ();

  // waveform display
  //
  l_waveform_display.set_text ("Waveform");

  ab_prev_wav.set_dir (arrow_button::left);
  ab_prev_wav.set_listener (&wdl);
  ab_next_wav.set_dir (arrow_button::right);
  ab_next_wav.set_listener (&wdl);

  ab_prev_wav.click_repeat = ab_next_wav.click_repeat = 1;
  ab_prev_wav.first_repeat_time = ab_next_wav.first_repeat_time = 0.33;
  ab_prev_wav.subsequent_repeat_time = ab_next_wav.subsequent_repeat_time = 1/20.;

  cd_waveform_display.set_size (96, 96);
  cd_waveform_display.crv = &keybd2.wave; // keyboard-keyboard's waveform
  cd_waveform_display.setbbox (keybd2.wave.shapeform);

	// octave shift
  l_octave_shift.set_text ("Octave Shift");
  ab_octave_down.set_dir (arrow_button::left);
  ab_octave_down.set_listener (&osl);
  ab_octave_up.set_dir (arrow_button::right);
  ab_octave_up.set_listener (&osl);
	int arrow_size = 24;
  ab_octave_up.set_size (arrow_size);
  ab_octave_down.set_size (arrow_size);
	b_abort_octave_shift.set_text ("Abort");
	b_abort_octave_shift.set_listener (&main_menu.aosl);

	// keys trigger? notes or noise
	ol_trig_what.set_listener (&twl);

	DEFINE_PARAMETERS 
  widget_load ("d_parameters", pw, npars);
	for (int i = 1; i < npars; ++i) {
		d_parameters.add_child (pw[i]);
		//pw[i]->set_moveable(1);
	}

	d_parameters.set_name ("Parameters");
	d_parameters.set_moveable (1);

  ab_parameters.set_listener (&pal);

  cb_voice.set_state (din0.dinfo.voice);
  cb_gater.set_state (din0.dinfo.gater);
  cb_delay.set_state (din0.dinfo.delay);
  cb_compress.set_state (din0.dinfo.compress);

	cb_show_pitch_volume_board.set_listener (&spvl);
	cb_show_pitch_volume_drones.set_listener (&spvl);

#ifdef __EVALUATION__
  anote.setup ();
#endif

  plugin__browser.setup ();
  settings_scr.setup ();
  main_menu.setup ();

}

void nagscr () {
	#ifdef __EVALUATION__
		cons.clear ();
		uis.set_current (&anote); // nag screen :(
	#elif defined __LICENSED__
  	load_instrument (); // open with last used instrument
	#endif
}

void drone_commands_listener::startwanding () {
  int* flags [] = {&din0.adding, &din0.wanding};
  static const char* mesg [] = {"Click to add drones, ESC or Right Click to stop", "Just move mouse to add drones, ESC or Right Click to stop"};
  din0.adding = din0.wanding = 0;
  *flags[din0.dinfo.wand] = 1;
#ifdef __EVALUATION__
  if (din0.wanding) {
    din0.wanding = 0;
    cons << RED << "Wand only available in the Licensed Version of DIN Is Noise" << eol;
    return;
  }
#endif 
  cons << GREEN << mesg[din0.dinfo.wand] << eol;
}

void drone_commands_listener::clicked (button& b) {
	int toggle = 1;
  if (&b == MENUP.ol_add_wand.option) startwanding (); else
	if (&b == MENUP.b_delete_drones) din0.delete_selected_drones (); else
	if (&b == MENUP.b_select_all_drones) {toggle = 0; din0.select_all_drones ();} else
  if (&b == MENUP.b_launch_drones) din0.make_launchers (); else
	if (&b == MENUP.b_orbit_selected_drones) din0.orbit_selected_drones (); else
	if (&b == MENUP.b_freeze_drones) din0.freeze_drones (); else
	if (&b == MENUP.b_thaw_drones) din0.thaw_drones (); else
	if (&b == MENUP.b_select_launchers) {toggle = 0; din0.select_launchers (); } else
	if (&b == MENUP.b_set_targets) din0.set_targets (); else
	if (&b == MENUP.b_select_attractors) {toggle = 0; din0.select_attractors (); } else
	if (&b == MENUP.b_select_attractees) {toggle = 0; din0.select_attractees (); } else
	if (&b == MENUP.b_stop_launching_drones) din0.destroy_launchers (); else
	if (&b == MENUP.b_invert_drone_selection) {toggle = 0; din0.invert_selected_drones();} else
  if (&b == MENUP.b_track_drones) din0.make_trackers (); else
	if (&b == MENUP.b_select_tracked_drones) {toggle = 0; din0.select_tracked_drones (); } else
	if (&b == MENUP.ol_create_this.option) din0.toggle_create_this (); else
	if (&b == MENUP.b_clear_targets) din0.clear_targets(); else
	if (&b == MENUP.b_flip_rows_cols) {
		int r = MENU.sp_mesh_rows.f_value, c = MENU.sp_mesh_cols.f_value;
		MENU.sp_mesh_rows.set_value (c);
		MENU.sp_mesh_cols.set_value (r);
		din0.dinfo.rows = c;
		din0.dinfo.cols = r;
		mkb_selector.set_mesh (din0.meshh.create, din0.dinfo.rows, din0.dinfo.cols);
		MENU.picked (MENU.ol_mesh_point.option, 0);
		toggle = 0;
	}

	if (toggle) MENU.toggle ();
}


VALUE_CHANGED(menu,genslis) {
  int& gens = din0.dinfo.gens;
  gens = (int) f;
#ifdef __EVALUATION__
  if (gens > 1) {
    cons << RED << "Generations > 1 only in the licensed version of DIN Is Noise" << eol;
    gens = 1;
		MENU.gens.set_value (1);
    return;
  }
#endif
  cons << GREEN << "Default Generations = " << gens << eol;
}
