Subversion Repositories DIN Is Noise

Rev

Rev 2338 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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


#include "main.h"
#include "menu.h"
#include "ui_list.h"
#include "viewwin.h"
#include "din.h"
#include "keyboard_keyboard.h"
#include "mondrian.h"
#include "binaural_drones.h"
#include "tcl_interp.h"
#include "console.h"
#include "recorder.h"
#include "oscilloscope.h"
#include "ball.h"
#include "mesh.h"
#include "noiser.h"
#include "log.h"
#include "fft.h"
#include "file-utils.h"
#include "drawrrow.h"
#include "autorotator.h"
#include "autoflip.h"
#include "defvelaccel.h"
#include "color.h"
#include <string>
#include <fstream>

#define DONT_MOUSE_WARP 0

using namespace std;

extern int mousex, mousey;
extern beat2value octave_shift;

extern std::string INSTRUMENT;
extern const char* INSTRUMENTS[];
extern int CURRENT_INSTRUMENT, LAST_INSTRUMENT, NUM_INSTRUMENTS;

extern float VOICE_VOLUME;
extern int NUM_OCTAVES;
extern void setup_plugin_labels ();
extern oscilloscope scope;
extern const float PI_BY_180;
extern char BUFFER [];
extern int wheely;
extern int line_height;
extern const char *ol_fixed_lbls [];
extern plugin_browser plugin__browser;

#define SECONDS " seconds"
#define DEGREES " degrees"

menu::menu () :
  diram (modulator::AM), dirfm (modulator::FM),
  bm_zoom_in(16), pb_zoom_in (16), bm_zoom_out(16), mb_zoom_out (16),
  abm_left (12), abm_right(12), abm_up(12), abm_down(12),
  abe_left (12), abe_right(12), abe_up(12), abe_down(12),
  abl_left (12, arrow_button::left), abl_right (12, arrow_button::right),
  gater_style_lis (ol_gater_style, "gr", " Style = "),
  am_style_lis (ol_am_style, "am", " AM style = "),
  fm_style_lis (ol_fm_style, "fm", " FM style = "),
  s_phrase_position (256, 16),
  td_tap_display (36),
  gc_top(0),
  gc_bottom(1)
  {
  num_tabs = 0;
  am_depth = fm_depth = 0;
  dam_depth = dfm_depth = dam_bpm = dfm_bpm = 0;

  cnl.id = 0;
  cnl.name = "Left Note";
  cnl.orient = mouse_slider_listener::X;

  cnr.id = 1;
  cnr.name = "Right Note";
  cnr.orient = mouse_slider_listener::X;

  arl.name = "Range Left";
  arl.orient = mouse_slider_listener::X;

  arr.name = "Range Right";
  arr.orient = mouse_slider_listener::X;

  arb.name = "Range Left & Right";
  arb.orient = mouse_slider_listener::Y;

  rhl.name = "Range Height";
  rhl.orient = mouse_slider_listener::Y;

  s_phrase_position.sizer.visible = 1;
 
}

void menu::setup_items () {

  b_close.visible = 0;

  button* inst_ed [] = {
    &b_keyboard_keyboard, // instruments
    &b_microtonal_keyboard,
    &b_mondrian,
    &b_binaural_drones,
    &b_microtonal_keyboard_waveform, // editors
    &b_drone_waveform,
    &b_drone_modulation,
    &b_voice_modulation,
    &b_gater,
    &b_keyboard_keyboard_waveform,
    &b_attack,
    &b_decay,
    &b_midi_velocity,
    &b_delays,
    &b_octave_shift,
    &b_compressor,
    &b_morse_code,
    &b_mondrian_waveform,
    &b_mondrian_attack,
    &b_mondrian_decay,
    &b_binaural_drones_waveform,
    &b_range_modulation,
    &b_range_width_height,
    &b_range_pitch_vol,
    &b_point_modulation,
    &b_noise_interpolator,
    &b_drone_pend,
    &edlife2vel,
  };

  // all other menu items
  widget* mi [] = {
    &b_exit_din,
    &l_octave_shift,
    &ab_octave_down,
    &ab_octave_up,
    &sp_octave_shift_bpm,
    &sp_gater_bpm,
    &sp_voice_volume,
    &cb_show_anchors,
    &sp_change_drone_handle_size,
    &sp_change_drone_trail_length,
    &sp_am_depth,
    &sp_fm_depth,
    &sp_am_bpm,
    &sp_fm_bpm,
    &cb_instrument,
    &cb_editors,
    &cb_mkb_drone_params,
    &cb_file,
    &ol_gater_style,
    &l_gater,
    &ol_am_style,
    &ol_fm_style,
    &cb_mkb_drone_tools,
    &ol_add_wand,
    &moverailstrafe,
    &b_delete_drones,
    &b_select_all_drones,
    &b_invert_drone_selection,
    &b_record_phrase,
    &b_clear_phrases,
    &l_phrase_position,
    &s_phrase_position,
    &ol_set_range,
    &b_default_to_selected,
    &b_default_to_all,
    &b_selected_to_all,
    &b_key_to_pitch_at_cursor,
    &cb_scope,
    &sp_scope_height,
    &sp_scope_samples,
    &l_tap_bpm,
    &td_tap_display,
    &l_tap_bpm_value,
    &cb_am,
    &cb_fm,
    &cb_gater,
    &cb_octave_shift,
    &cb_auto_reset,
    &cb_ed_tools,
    &abe_left,
    &abe_right,
    &abe_up,
    &abe_down,
    &b_close,
    &b_snapx,
    &b_snapy,
    &b_snapboth,
    &b_snapnone,
    &l_snap,
    &pb_zoom_in,
    &mb_zoom_out,
    &b_insert_vertex,
    &b_delete_vertex,
    &b_fold_tangents,
    &ol_mirror,
    &ol_mirror_tangents,
    &cb_selection_only,
    &b_undo,
    &b_redo,
    &b_copy,
    &ol_paste,
    &b_draw_replacement_curve,
    &l_library,
    &abl_left,
    &abl_right,
    &lf_curve_name,
    &b_add_curve,
    &b_replace_curve,
    &b_delete_curve,
    &l_capture,
    &b_start_capture,
    &b_assign_capture,
    &cb_label_vertices,
    &cb_show_waveform_samples,
    &crvwav.hz,
    &b_pick_curve,
    &sp_curve_limit,
    &crvwav.periods,
    &ol_curve_style,
    &sp_curve_rpm,
    &b_stop_rotating,
    &cb_draw_curve,
    &cb_record,
    &b_clear_record,
    &lf_file,
    &b_save,
    &b_select_attractees,
    &b_select_attractors,
    &b_orbit_selected_drones,
    &cb_show_vel,
    &cb_show_accel,
    &sp_change_drone_vel,
    &sp_change_drone_accel,
    &cb_show_gravity,
    &balloon,
    &sp_rotate_drone_vel,
    &sp_drones_per_min,
    &b_launch_drones,
    &b_stop_launching_drones,
    &ol_create_this,
    &sp_mesh_rows,
    &sp_mesh_cols,
    &b_track_drones,
    &b_select_tracked_drones,
    &sp_bounces,
    &sp_rebound,
    &b_add_balls,
    &b_move_selected_balls,
    &b_delete_selected_targets,
    &b_delete_all_targets,
    &b_select_all_targets,
    &b_invert_selected_targets,
    &b_select_targets_in_box,
    &b_split_horizontal,
    &b_split_vertical,
    &b_delete_box,
    &sp_mondrian_min_voices,
    &sp_mondrian_change_attack_time,
    &sp_mondrian_change_decay_time,
    &sp_mondrian_change_speed,
    &b_freeze_balls,
    &b_thaw_balls,
    &abm_left,
    &abm_right,
    &abm_up,
    &abm_down,
    &bm_zoom_in,
    &bm_zoom_out,
    &b_turn_off_ui,
    &b_set_targets,
    &b_clear_targets,
    &sp_drone_lifetime,
    &sp_orbit_insertion_time,
    &b_clear_modulations,
    &b_modulate_balls_up,
    &b_modulate_balls_down,
    &cb_binaural_drones_tools,
    &lf_master_volume,
    &sp_bd_separation,
    &b_create_binaurals_on_notes,
    &b_create_binaurals_from_pitch,
    &lf_bd_start_pitch,
    &sp_bd_pairs,
    &lf_bd_spacing,
    &cb_close_octave,
    &lf_vol_fade_time,
    &sp_mondrian_change_dir,
    &sp_mondrian_change_trail_size,
    &sp_mondrian_change_note_poly_points,
    &sp_mondrian_change_note_poly_radius,
    &b_auto_change_direction_clockwise,
    &b_stop_auto_changing_direction,
    &b_auto_change_direction_anti_clockwise,
    &b_flip_direction,
    &b_make_random_color,
    &ol_justification,
    &cb_resize_separation,
    &ol_key_note,
    &b_add_remove_slits,
    &b_select_wreckers,
    &b_select_healers,
    &b_switch_ball_type,
    &b_toggle_wreckers,
    &b_toggle_healers,
    &b_toggle_bouncers,
    &sp_mondrian_change_slit_size,
    &b_remove_slits_on_edge,
    &b_toggle_slit_anim,
    &cb_mondrian_auto_adjust_voices,
    &sp_mondrian_change_vol,
    &cb_draw_boxes,
    &cb_fill_boxes,
    &cb_draw_notes,
    &cb_label_notes,
    &ol_ball_types,
    &ol_split_types_h,
    &ol_split_types_v,
    &sp_mondrian_num_boxes,
    &b_make_note_grid,
    &b_make_nxn_grid,
    &b_delete_all_boxes,
    &cb_mkb_voice,
    &cb_mkb_misc,
    &b_select_launchers,
    &seloncre,
    &b_freeze_drones,
    &b_thaw_drones,
    &sp_dam_depth,
    &sp_dfm_depth,
    &sp_dam_bpm,
    &sp_dfm_bpm,
    &b_scale_drones,
    &b_rotate_drones,
    &ol_selection_targets,
    &sp_mondrian_change_slit_anim_time,
    &cb_mark_segments,
    &cb_auto_split_box,
    &cb_auto_delete_box,
    &sp_auto_split_time,
    &sp_auto_delete_time,
    &ol_auto_pick_box_split,
    &ol_auto_split_at,
    &ol_auto_split_orient,
    &ol_auto_pick_box_delete,
    &sp_min_split_size,
    &cb_speed,
    &cb_turn,
    &cb_teleport,
    &sp_turn_every,
    &sp_turn_min,
    &sp_turn_max,
    &sp_speed_every,
    &sp_speed_min,
    &sp_speed_max,
    &sp_max_speed,
    &sp_tel_every,
    &sp_tel_radius,
    &cb_draw_ball_position,
    &cb_draw_ball_heading,
    &cb_draw_ball_trails,
    &l_draw_ball,
    &cb_turn_sync,
    &cb_speed_sync,
    &sp_clone_every,
    &sp_max_clones,
    &sp_clone_offset,
    &sp_max_balls,
    &cb_clone,
    &cb_clone_can_clone,
    &ol_browse_balls,
    &cb_mon_tools,
    &cb_mon_parameters,
    &cb_mon_ballops,
    &cb_mon_boxops,
    &cb_mon_misc,
    &cb_transform,
    &sp_transform_every,
    &ol_bouncer,
    &ol_healer,
    &ol_wrecker,
    &cb_label_hz_vol,
    &il_binaural_drones,
    &cb_binaural_drones_edit,
    &bbd_select_all,
    &bbd_select_none,
    &bbd_invert_select,
    &bbd_delete,
    &bbd_sync,
    &lf_pitch_fade_time,
    &lf_modulation_amount,
    &bd_modulate_up,
    &bd_modulate_down,
    &bbd_modulate,
    &bbd_select2,
    &ol_select_what,
    &ol_select_rule,
    &bdf_value,
    &lf_l,
    &lf_r,
    &lf_sep,
    &lf_vol,
    &ol_just,
    &bbd_flip,
    &b_adjust_board_height,
    &b_adjust_range_left,
    &b_adjust_range_right,
    &b_adjust_range_both,
    &sp_snap_left,
    &sp_snap_right,
    &ol_bounce_style,
    &cb_mod_ran,
    &sp_range,
    &sp_ran_mod_width,
    &sp_ran_mod_width_bpm,
    &cb_mark_ran,
    &l_ran_mod,
    &b_rm_pause_resume,
    &b_rm_start_all,
    &b_rm_stop_all,
    &b_rm_toggle,
    &b_get_cur_ran,
    &ol_snap_style,
    &sp_ran_mod_height,
    &sp_ran_mod_height_bpm,
    &cb_mkb_ranges,
    &l_adjust_range,
    &l_adjust_height,
    &b_adjust_range_height,
    &dronearrow.shoulder.position,
    &dronearrow.shoulder.width,
    &l_drone_arrow,
    &sp_default_width,
    &sp_default_height,
    &b_change_note_left,
    &b_change_note_right,
    &ol_change_note_style,
    &ol_set_unset_toggle,
    &b_set,
    &b_unset,
    &b_toggle,
    &sp_browse_drone,
    &ol_drone_order,
    &l_drone_order,
    &ol_mesh_point,
    &f_mesh_xy,
    &cb_sync_rows_cols,
    &sp_mesh_dur,
    &b_flip_rows_cols,
    &cb_overlay,
    &dp_numdrones,
    &dp_bpm1,
    &dp_orient,
    &b_swap_curves,
    &cb_pitch_dis,
    &cb_vol_dis,
    &sp_lev_sz,
    &ol_change_note,
    &b_change_note_both,
    &sp_drone_master_vol,
    &l_use_drone_pend,
    &sp_drones_per_pend,
    &l_apply_to,
    &cb_am_bpm,
    &cb_fm_bpm,
    &b_set_to_mesh_rows,
    &b_set_to_mesh_cols,
    &ol_drone_is,
    &b_ball_trig,
    &ol_fixed,
    &cb_draw_mesh,
    &lf_conn_steps,
    &s_red_min,
    &s_green_min,
    &s_blue_min,
    &s_red_max,
    &s_green_max,
    &s_blue_max,
    &cb_conn_wrap,
    &separators.main,
    &separators.dp0,
    &cb_modulation,
    &cb_motion,
    &cb_visual,
    &b_connect,
    &b_disconnect,
    &ol_color,
    &separators.dp1,
    &b_abort_octave_shift,
    &sp_stiff,
    &b_arrow_reset,
    &b_mute,
    &b_unmute,
    &gabt,
    &sp_drone_vol,
    &drone2noise,
    &noise2drone,
    &b_set_xform_center,
    &autorotate.title,
    &autorotate.whichl,
    &autorotate.start,
    &autorotate.stop,
    &autorotate.toggle,
    &autorotate.clockwise,
    &autorotate.anticlockwise,
    &autorotate.rpm,
    &cb_defaults,
    &sp_wand_dist,
    &ol_drones_are,
    &riset,
    &fallt,
    &lifetime,
    &mortalize,
    &reincarnate,
    &immortalize,
    &diram.vert,
    &diram.hor,
    &diram.vel,
    &diram.accel,
    &dirfm.vert,
    &dirfm.hor,
    &dirfm.vel,
    &dirfm.accel,
    &diram.lbl,
    &dirfm.lbl,
    &cb_chuck,
    &chuck,
    &chspeed,
    &chflip,
    &chtog,
    &chlen,
    &chapt,
    &chtrail,
    &handlesize,
    &sva.lbl,
    &sva.whats,
    &sva.neg,
    &sva.zero,
    &sva.vert,
    &sva.hor,
    &sva.vel,
    &sva.accel,
    &autorotate.autoflip.lbl,
    &autorotate.autoflip.angle,
    &autorotate.autoflip.set,
    &autorotate.autoflip.unset,
    &autorotate.autoflip.toggle,
    &dva.whichl,\
    &dva.ldir,\
    &dva.odir,\
    &dva.neg,\
    &dva.randomize,\
    &dva.mag,\
    &dva.anticlockwise,\
    &dva.clockwise,\
    &choutline,
    &chautoresettrails,
    &anchored,
    &trailsize,
    &dronearrow.neck,
    &dronearrow.cap,
    &dronearrow.decap,
    &autorotate.deg,
    &autorotate.tps,
    &autorotate.mov,
    &autorotate.smooth,
    &autorotate.tick,
    &dronearrowdefaults.lbl,
    &dronearrowdefaults.neck,
    &dronearrowdefaults.shoulder.position,
    &dronearrowdefaults.shoulder.width,
    &dronearrowdefaults.cap,
    &dronearrowdefaults.arrow,
    &trackcon,
    &dva.autorotate.cb,
    &dva.autorotate.mov,
    &dva.autorotate.dir,
    &dva.autorotate.rpm,
    &dva.autorotate.dps,
    &dva.autorotate.tps,
    &dva.autoflip.cb,
    &dva.autoflip.deg,
    &vel0,
    &accel0,
    &dva.sync,
    &gens,
    &ddpm,
    &dva.autorotate.uet.deg,
    &dva.autorotate.uet.tps,
    &ddpl,
    &dpl,
    &sp_dam_pos,
    &sp_dfm_pos,
    &am2fm,
    &fm2am,
    &scopacity,
    &cb_ed_curves,
    &ed_flip_rotation,
    &cb_ed_autoflip,
    &sp_curve_every,
    &ed_autoflip_reset,
    &dva.autopause.cb,
    &dva.autopause.every,
    &dva.autopause.f0r,
    &autorotate.autopause.lbl,
    &autorotate.autopause.set,
    &autorotate.autopause.unset,
    &autorotate.autopause.toggle,
    &autorotate.autopause.every,
    &autorotate.autopause.f0r,
    &autorotate.rndflipause.whichl,
    &autorotate.rndflipause.set,
    &autorotate.rndflipause.unset,
    &autorotate.rndflipause.toggle,
    &damd0,
    &dfmd0,
    &dva.autopause.tar,
    &masvol0,
    &crvwav.time,
    &texture,
    &texstep,
    &text_ure,
    &revmod,
    &sva.perp,
    &rotaccel,
    &rdvel,
    &rdaccel,
    &size_tangent,
    &draweditables,
    &track_phrase_position,
    &viewtoo,
    &drawsnaps,
    &drawcursor,
    &pinunpin,
    &sep_bin,
    &sep0,
    &ancopa,
    // next item here
  };


  int ii = 0;
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) {
    button* ei = inst_ed[i];
    ei->id = i;
#ifndef __WIDGET_MOVE__
    ei->set_listener (&insl);
#endif
    items[ii++] = ei;
  }
  for (int i = NUM_INSTRUMENTS; i < n_inst_ed; ++i) {
    button* ei = inst_ed[i];
    ei->id = i + 1; // as 0 is instrument
#ifndef __WIDGET_MOVE__
    ei->set_listener (&edl);
#endif
    items[ii++] = ei;
  }

  for (int i = n_inst_ed, j = 0; i < nitems; ++i) items[ii++] = mi[j++];

  dlog << "+++ Items list setup +++ " << endl;

#ifndef __WIDGET_MOVE__
  {
    button* btns [] = {&b_set_to_mesh_rows, &b_set_to_mesh_cols, &b_set, &b_unset, &b_toggle};
    click_listener* cl [] = {&stmrl, &stmcl, &sutl, &sutl, &sutl};
    checkbutton* cbtns [] = {&cb_am_bpm, &cb_fm_bpm};
    state_listener* sl [] = {&abl, &fbl};
    for (int i = 0; i < 5; ++i) btns[i]->set_listener (cl[i]);
    for (int i = 0; i < 2; ++i) cbtns[i]->set_listener (sl[i]);

  }
#endif

  // tabs
  num_tabs = 0;
  last_tab = next_tab = cur_tab = 0;
  next_tab_instr = 0;
  checkbutton* cb_tabs [] = {
    &cb_file,
    &cb_instrument,
    &cb_editors,
    &cb_mkb_voice,
    &cb_mkb_drone_tools,
    &cb_mkb_drone_params,
    &cb_mkb_ranges,
    &cb_mkb_misc,
    &cb_ed_tools,
    &cb_mon_tools,
    &cb_mon_parameters,
    &cb_mon_ballops,
    &cb_mon_boxops,
    &cb_mon_misc,
    &cb_binaural_drones_tools,
    &cb_binaural_drones_edit,
    &cb_ed_curves,
  };

  static const char* const cb_tab_lbls [] = {
    "File",
    "Instrument",
    "Editors",
    "Voice",
    "Drone Tools",
    "Drone Params",
    "Ranges",
    "Misc",
    "Tools",
    "Tools",
    "Params",
    "Ball Ops",
    "Box Ops",
    "Misc",
    "Create",
    "Edit",
    "Curves",
  };

  for (int i = 0; i < 17; ++i) {
    checkbutton* ci = cb_tabs[i];
    ci->set_text (cb_tab_lbls[i]);
    ci->set_listener (this);
  }

  widget* wfile [] = {
    &b_exit_din,
    &cb_record,
    &b_clear_record,
    &b_save,
    &lf_file,
    &b_turn_off_ui,
    &cb_scope,
    &sp_scope_height,
    &sp_scope_samples,
    &scopacity,
  };

#ifndef __WIDGET_MOVE__
  cb_record.set_listener (&recl);
  b_clear_record.set_listener (&recl);
  b_save.set_listener (&recl);
  b_exit_din.set_listener (&miscl);
  b_turn_off_ui.set_listener (&miscl);
  lf_file.fld.typing_lsnr = &recl;
#endif

  lf_file.fld.set_text ("din.wav");
  lf_file.lbl.set_text ("File on Desktop?");

  widget* winst [] = {
    &b_microtonal_keyboard,
    &b_keyboard_keyboard,
    &b_mondrian,
    &b_binaural_drones
  };

  widget* weds [] = {
    &b_keyboard_keyboard_waveform,
    &b_attack,
    &b_decay,
    &b_midi_velocity,
    &b_microtonal_keyboard_waveform,
    &b_drone_waveform,
    &b_drone_modulation,
    &b_voice_modulation,
    &b_range_modulation,
    &b_range_width_height,
    &b_range_pitch_vol,
    &b_gater,
    &b_point_modulation,
    &b_drone_pend,
    &edlife2vel,
    &b_mondrian_waveform,
    &b_mondrian_attack,
    &b_mondrian_decay,
    &b_binaural_drones_waveform,
    &b_delays,
    &b_octave_shift,
    &b_compressor,
    &b_morse_code,
    &b_noise_interpolator,
  };

  widget* wvoice [] = {
    &sp_voice_volume,
    &b_record_phrase,
    &b_clear_phrases,
    &s_phrase_position,
    &l_phrase_position,
    &track_phrase_position,
    &sp_am_depth,
    &sp_fm_depth,
    &sp_am_bpm,
    &sp_fm_bpm,
    &ol_am_style,
    &ol_fm_style,
  };

#ifndef __WIDGET_MOVE__
  for (int i = 1; i < 3; ++i) static_cast<button*>(wvoice[i])->set_listener (&pcl); // phrase commands listener
#endif

  widget* wdrone_tools [] = {
    &ol_add_wand,
    &b_delete_drones,
    &b_select_all_drones,
    &b_invert_drone_selection,
    &b_orbit_selected_drones,
    &b_select_attractees,
    &b_select_attractors,
    &b_launch_drones,
    &b_stop_launching_drones,
    &b_track_drones,
    &b_select_tracked_drones,
    &b_set_targets,
    &b_clear_targets,
    &b_select_launchers,
    &b_freeze_drones,
    &b_thaw_drones,
    &b_flip_rows_cols,
    &ol_create_this,
    &sp_mesh_rows,
    &sp_mesh_cols,
    &sp_browse_drone,
    &ol_drone_order,
    &l_drone_order,
    &cb_sync_rows_cols,
    &sp_mesh_dur,
    &dp_numdrones,
    &dp_bpm1,
    &dp_orient,
    &l_use_drone_pend,
    &cb_am_bpm,
    &cb_fm_bpm,
    &sp_drones_per_pend,
    &l_apply_to,
    &b_set_to_mesh_rows,
    &b_set_to_mesh_cols,
    &moverailstrafe,
    &b_scale_drones,
    &b_rotate_drones,
    &b_connect,
    &b_disconnect,
    &lf_conn_steps,
    &cb_conn_wrap,
    &b_mute,
    &b_unmute,
    &gabt,
    &drone2noise,
    &noise2drone,
    &b_set_xform_center,
    &mortalize,
    &reincarnate,
    &immortalize,
    &chuck,
    &trackcon,
    &balloon,
    &b_set,\
    &b_unset,\
    &b_toggle,\
    &ol_set_unset_toggle,\
  };

  LISTEN(ol_add_wand,&awdl)
  LISTEN(ol_add_wand.option,&dcl)
  LISTEN(ol_drones_are, &darl)

#ifndef __WIDGET_MOVE__
  LISTEN(b_scale_drones,&bsdl)
  LISTEN(b_rotate_drones,&brdl)
  LISTEN(moverailstrafe, &mrsl)
  LISTEN(moverailstrafe.option, &bmdl)
  LISTEN(balloon,&dugl)
  LISTEN(b_mute,&mul)
  LISTEN(b_unmute,&umul)
  LISTEN(drone2noise,&d2nl)
  LISTEN(noise2drone,&n2dl)
  LISTEN(seloncre,&dcl);
  LISTEN(cb_conn_wrap, &wrapl)
  LISTEN(b_connect, &bconl)
  LISTEN(b_disconnect, &bdconl)
  LISTEN(b_set_xform_center, &xcl);
  LISTEN(mortalize,&morl)
  LISTEN(immortalize,&immorl)
  LISTEN(reincarnate,&reinl)
  LISTEN(chuck, &chl)
  LISTEN(chflip,&flpl)
  LISTEN(chtog,&ctogl)
  LISTEN(chautoresettrails,&aurl)
#endif

  raill1.name = "Rail";
  strafel1.name = "Strafe";

  sp_stiff.set ("Connection stiffness", 0.001f, 0.0f, 1.0f, &stiffl);
  ancopa.set ("Opacity", 0.01f, 0.0f, 1.0f, &anchl);

  gabt.set (0.1f, 0, MILLION, &gabtl);
  gabt.set_text ("In", SECONDS);
  sp_drones_per_pend.set ("Drones Per Pendulum", 1, 2, MILLION, &dppl, 0);
  l_use_drone_pend.set_text ("Drone Pendulum's parameters?");
  l_apply_to.set_text ("Apply to");

  DECL_DRONE_PARAMS

  ol_drone_is.set_listener (this);
  diram.setup ("Set AM direction to: ");
  dirfm.setup ("Set FM direction to: ");

  sp_wand_dist.set ("Wand distance", 1, 0, MILLION, &wandl);
  riset.set ("Rise time", 0.1f, 0, MILLION, &rl);
  fallt.set ("Fall time", 0.1f, 0, MILLION, &fl);
  lifetime.set ("Lifetime", 0.1f, 0, MILLION, &lifel);
  handlesize.set ("Handle size", 1, 0, MILLION, &handl);
  trailsize.set ("Trail size", 1, 0, MILLION, &trll);
  ddpm.set ("Launches per minute", 1, 0, MILLION, &ddpml);
  ddpl.set ("Drones per launch", 1, 0, MILLION, &ddpll);
  gens.set ("Generations", 1, 1, MILLION, &gensl);
  gens.orient = mouse_slider_listener::NONE;

  autorotate.setup ();
  sva.setup ();
  dva.setup ();

  anchored.set_listener (&ancl);

  // chuck
  chspeed.set ("Speed", 1.0f, &spdl); chspeed.limits = 0;
  chlen.set ("Length", 1.0f, &lenl);
  chapt.set ("Angle per turn", 0.1f, &apfl); chapt.limits = 0;
  chtrail.set ("Trail length", 10000, &chtll);

#ifndef __WIDGET_MOVE__
  for (int i = 1; i < 17; ++i) dynamic_cast<button*>(wdrone_tools[i])->set_listener (&dcl);
  for (int i = 0, j = DRONE_PARAMS_N - 1; i < 4; ++i) dynamic_cast<checkbutton*>(wdrone_params[j--])->set_listener (&dcl);
#endif

  static const char* txt [] = {"AM Depth", "FM Depth", "AM BPM", "FM BPM"};
  spinner<float>* spn [] = {&sp_am_depth, &sp_fm_depth, &sp_am_bpm, &sp_fm_bpm};
  change_listener<field>* vlis [] = {&amdl, &fmdl, &ambpml, &fmbpml};
  spinner<float>* dspn [] = {&sp_dam_depth, &sp_dfm_depth, &sp_dam_bpm, &sp_dfm_bpm};
  change_listener<field>* dlis [] = {&damdl, &dfmdl, &dambpml, &dfmbpml};

  for (int i = 0; i < 4; ++i) {
    const char* txti = txt[i];
    spinner<float>* dspni = dspn[i];
    dspni->set (txti, 1.0f, dlis[i]);
    spinner<float>* spni = spn[i];
    spni->set_text (txti);
    spni->set_listener (vlis[i]);
  }

  for (int i = 1; i < 4; ++i) spn[i]->set_delta (1.0f);
  sp_am_depth.set_delta (0.01f);
  sp_am_bpm.set_limits (0, MILLION);
  sp_fm_bpm.set_limits (0, MILLION);

  sp_dam_pos.set ("AM Position", 0.01f, &dampl);
  sp_dfm_pos.set ("FM Position", 0.01f, &dfmpl);

  sp_dam_depth.lim = &damd0;
  sp_dfm_depth.lim = &dfmd0;

  LISTEN(revmod,&revl)
  LISTEN(revmod.option,&revcl)

  widget* wranges [] = {
    &b_selected_to_all,
    &b_default_to_selected,
    &b_default_to_all,
    &b_adjust_range_left,
    &b_adjust_range_right,
    &b_adjust_range_both,
    &b_adjust_range_height,
    &b_adjust_board_height,
    &b_rm_start_all,
    &b_rm_stop_all,
    &b_rm_toggle,
    &b_rm_pause_resume,
    &b_get_cur_ran,
    &cb_mod_ran,
    &cb_mark_ran,
    &sp_ran_mod_width,
    &sp_ran_mod_width_bpm,
    &sp_ran_mod_height,
    &sp_ran_mod_height_bpm,
    &sp_range,
    &l_ran_mod,
    &l_adjust_height,
    &l_adjust_range,
    &ol_set_range,
    &sp_default_width,
    &sp_default_height,
    &b_change_note_left,
    &b_change_note_right,
    &ol_change_note_style,
    &ol_change_note,
    &b_change_note_both,
    &ol_fixed,
    &sp_snap_left,
    &sp_snap_right,
    &ol_snap_style,
  };

#ifndef __WIDGET_MOVE__
  for (int i = 0; i < 3; ++i) static_cast<button*>(wranges[i])->set_listener (&sral);
  for (int i = 3; i < 6; ++i) static_cast<button*>(wranges[i])->set_listener (&rwl);
  b_adjust_range_height.set_listener (&rhl);
  b_adjust_board_height.set_listener (&bhl);
  for (int i = 8; i < 13; ++i) static_cast<button*>(wranges[i])->set_listener (&rml);
  cb_mod_ran.set_listener (&rml);
  cb_mark_ran.set_listener (&rml);
  b_change_note_left.set_listener (&cnl);
  b_change_note_right.set_listener (&cnr);
  b_change_note_both.set_listener (&cnb);
#endif

  ol_change_note.set_listener (&cnol);
  ol_change_note_style.set_listener (&cnsl);

  widget* wmisc [] = {
    &l_octave_shift,
    &ab_octave_down,
    &ab_octave_up,
    &sp_octave_shift_bpm,
    &l_gater,
    &sp_gater_bpm,
    &ol_gater_style,
    &b_key_to_pitch_at_cursor,
    &l_tap_bpm,
    &td_tap_display,
    &l_tap_bpm_value,
    &cb_am,
    &cb_fm,
    &cb_gater,
    &cb_octave_shift,
    &cb_auto_reset,
    &cb_pitch_dis,
    &cb_vol_dis,
    &sp_lev_sz,
    &b_abort_octave_shift,
  };

#ifndef __WIDGET_MOVE__
  LISTEN(b_abort_octave_shift, &aosl)
  cb_draw_mesh.set_listener (&dml);
  cb_pitch_dis.set_listener (&pvdl);
  cb_vol_dis.set_listener (&pvdl);
#endif

  sp_lev_sz.set ("Level size", 1, 1, MILLION, &pvdl);
 
  widget* wedtools [] = { // curve editors tools
    &abe_left,
    &abe_right,
    &abe_up,
    &abe_down,
    &pb_zoom_in,
    &mb_zoom_out,
    &l_snap,
    &b_snapx,
    &b_snapy,
    &b_snapboth,
    &b_snapnone,
    &drawsnaps,
    &b_pick_curve,
    &b_insert_vertex,
    &b_delete_vertex,
    &b_draw_replacement_curve,
    &b_undo,
    &b_redo,
    &viewtoo,
    &b_copy,
    &ol_paste,
    &b_swap_curves,
    &size_tangent,
    &b_fold_tangents,
    &cb_selection_only,
    &ol_mirror,
    &pinunpin,
    &ol_mirror_tangents,
    &lf_curve_name,
    &sp_curve_limit,
    &ol_curve_style,
    &l_library,
    &abl_left,
    &abl_right,
    &b_add_curve,
    &b_replace_curve,
    &b_delete_curve,
    &l_capture,
    &b_start_capture,
    &b_assign_capture,
    &cb_draw_curve,
    &draweditables,
    &cb_mark_segments,
    &cb_label_vertices,
    &cb_show_waveform_samples,
    &crvwav.hz,
    &crvwav.periods,
    &crvwav.time,
    &sp_curve_rpm,
    &b_stop_rotating,
    &ed_flip_rotation,
    &cb_ed_autoflip,
    &ed_autoflip_reset,
    &sp_curve_every,
    &cb_overlay,
    &drawcursor,
    &pinunpin,
  };

  widget* wmon [] = { // mondrian tools
    &b_add_balls,
    &b_move_selected_balls,
    &b_delete_selected_targets,
    &b_delete_all_targets,
    &b_select_all_targets,
    &b_invert_selected_targets,
    &b_split_horizontal,
    &b_split_vertical,
    &b_delete_box,
    &b_select_targets_in_box,
    &b_freeze_balls,
    &b_thaw_balls,
    &b_clear_modulations,
    &b_modulate_balls_up,
    &b_modulate_balls_down,
    &b_auto_change_direction_clockwise,
    &b_auto_change_direction_anti_clockwise,
    &b_stop_auto_changing_direction,
    &b_flip_direction,
    &b_make_random_color,
    &b_add_remove_slits,
    &b_select_wreckers,
    &b_select_healers,
    &b_switch_ball_type,
    &b_toggle_wreckers,
    &b_toggle_healers,
    &b_toggle_bouncers,
    &b_remove_slits_on_edge,
    &b_toggle_slit_anim,
    &b_make_note_grid,
    &b_make_nxn_grid,
    &b_delete_all_boxes,
    &b_ball_trig,
    &ol_ball_types,
    &ol_split_types_h,
    &ol_split_types_v,
    &sp_mondrian_num_boxes,
    &ol_selection_targets,
  };


  arrow_button* mnav[] = {&abm_left, &abm_down, &abm_right, &abm_up};
  int mdirs [] = {arrow_button::left, arrow_button::down, arrow_button::right, arrow_button::up};
  for (int i = 0; i < 4; ++i) {
    arrow_button* ab = mnav[i];
    ab->set_dir (mdirs[i]);
  }

  widget* wmon_pars [] = { // mondrian parameters
    &l_octave_shift,
    &ab_octave_down,
    &ab_octave_up,
    &sp_octave_shift_bpm,
    &b_abort_octave_shift,
    &sp_mondrian_min_voices,
    &sp_mondrian_change_attack_time,
    &sp_mondrian_change_decay_time,
    &sp_mondrian_change_speed,
    &sp_mondrian_change_dir,
    &sp_mondrian_change_trail_size,
    &sp_mondrian_change_note_poly_points,
    &sp_mondrian_change_note_poly_radius,
    &sp_mondrian_change_slit_size,
    &sp_mondrian_change_slit_anim_time,
    &cb_mondrian_auto_adjust_voices,
    &sp_mondrian_change_vol
  };

  widget* wmon_misc [] = { // mondrian misc
    &l_draw_ball,
    &abm_left,
    &abm_right,
    &abm_up,
    &abm_down,
    &bm_zoom_in,
    &bm_zoom_out,
    &cb_draw_boxes,
    &cb_fill_boxes,
    &cb_draw_notes,
    &cb_label_notes,
    &cb_label_hz_vol,
    &cb_draw_ball_position,
    &cb_draw_ball_heading,
    &cb_draw_ball_trails,
    &texture,
    &texstep,
    &text_ure,
  };

  widget* wmon_boxops [] = {
    &cb_auto_split_box,
    &cb_auto_delete_box,
    &sp_auto_split_time,
    &sp_auto_delete_time,
    &ol_auto_pick_box_split,
    &ol_auto_split_at,
    &ol_auto_split_orient,
    &ol_auto_pick_box_delete,
    &sp_min_split_size
  };

  widget* wmon_ballops [] = {
    &cb_turn, &cb_speed, &cb_teleport, &cb_clone, &sp_turn_every, &sp_turn_min, &sp_turn_max,
    &sp_speed_every, &sp_speed_min, &sp_speed_max, &sp_max_speed, &sp_tel_every, &sp_tel_radius,
    &cb_turn_sync, &cb_speed_sync, &sp_clone_every, &sp_max_clones, &sp_clone_offset, &sp_max_balls,
    &cb_clone_can_clone, &ol_browse_balls,
    &cb_transform, &sp_transform_every, &ol_bouncer, &ol_healer, &ol_wrecker,
  };
 
  spinner<float>* bospn [] = {&sp_turn_every, &sp_speed_every, &sp_tel_every, &sp_clone_every, &sp_transform_every};
  float bodta [] = {0.01f, 0.01f, 1.0f, 1.0f, 1.0f};
  for (int i = 0; i < ball_op::NUM_OPS; ++i) {
    spinner<float>* spni = bospn[i];
    spni->set_text ("Every", SECONDS);
    spni->set (bodta[i], 0.0f, MILLION, &bolis);
  }
  sp_turn_min.set_text ("Clockwise Max", DEGREES);
  sp_turn_min.set (1, 0, MILLION, &bolis);
  sp_turn_max.set_text ("Anti-clockwise Max", DEGREES);
  sp_turn_max.set (1, 0, MILLION, &bolis);
  sp_speed_min.set ("Brake", 0.1f, 0, MILLION, &bolis);
  sp_speed_max.set ("Accelerate", 0.1f, 0, MILLION, &bolis);
  sp_max_speed.set ("Max speed", 1, 0, MILLION, &bolis);
  sp_tel_radius.set ("Max distance", 1, 0, MILLION, &bolis);
  sp_clone_offset.set ("Offset", 1.0f, 0, MILLION, &bolis);
  sp_max_clones.set ("Max clones", 1, 1, MILLION, &bolis);
  sp_max_balls.set ("Max balls", 1, 0, MILLION,&bolis);

#ifndef __WIDGET_MOVE__

  checkbutton* bocbn [] = {&cb_turn, &cb_speed, &cb_teleport, &cb_clone, &cb_transform};
  for (int i = 0; i < ball_op::NUM_OPS; ++i) bocbn[i]->set_listener (&bolis);
  for (int i = 1; i < 7; ++i) static_cast<button*>(wmon_misc[i])->set_listener (&monl);
  for (int i = 7; i < 15; ++i) static_cast<checkbutton*>(wmon_misc[i])->set_listener (&monl);
  for (int i = 0; i < 33; ++i) static_cast<button*>(wmon[i])->set_listener (&monl);

  for (int i = 0; i < 6; ++i) static_cast<button*>(wedtools[i])->set_listener (&pzl);
  for (int i = 7; i < 11; ++i) static_cast<button*>(wedtools[i])->set_listener (&snl);
  drawsnaps.set_listener (&snl);
  drawcursor.set_listener (&cursl);
  viewtoo.set_listener (&urvl);

  button* crvops [] = {
    &b_pick_curve,
    &b_insert_vertex,
    &b_delete_vertex,
    &b_draw_replacement_curve,
    &b_fold_tangents,
    &ol_mirror.option,
    &b_copy,
    &ol_paste.option,
    &b_swap_curves,
    &b_undo,
    &b_redo,
    &abl_left,
    &abl_right,
    &b_add_curve,
    &b_replace_curve,
    &b_delete_curve,
    &b_start_capture,
    &b_assign_capture,
    &b_stop_rotating,
    &ed_flip_rotation,
    &ed_autoflip_reset,
    &size_tangent,
    &pinunpin,
  };

  for (int i = 0; i < 23; ++i) crvops[i]->set_listener (&col);
  col.name = "Tangent size"; // via mouse slider

#endif

  options_list* oll [] = {
    &ol_ball_types, &ol_split_types_h, &ol_split_types_v, &ol_selection_targets,
    &ol_auto_pick_box_split, &ol_auto_split_at, &ol_auto_split_orient,
    &ol_auto_pick_box_delete
  };
  for (int i = 0; i < 8; ++i) {
    options_list* oli = oll[i];
    oli->set_listener (&monl);
  }
  ol_split_types_h.set_text (mondrian_listener::split_types[monl.hsplit]);
  ol_split_types_v.set_text (mondrian_listener::split_types[monl.vsplit]);
  ol_selection_targets.set_text (mondrian_listener::selection_targets[mondrian0.sel_tar]);
  ol_browse_balls.set_listener (&bolis);

  b_undo.click_repeat = b_redo.click_repeat = 1;
  abl_left.click_repeat = abl_right.click_repeat = 1;

  ol_mirror.set_listener (this);

  lf_curve_name.set_listener (&col);
  ol_paste.set_listener (&pastl);

#ifndef __WIDGET_MOVE__
  checkbutton* cbed [] = {&cb_label_vertices, &cb_mark_segments, &cb_overlay, &cb_draw_curve, &draweditables, &cb_ed_autoflip, &sp_curve_every.variance.cb, 0};
  checkbutton** cp = cbed;
  while (*cp != 0) {
    (*cp)->set_listener (&col);
    cp++;
  }
#endif

  widget* wbd [] = { // binaural drones
    &b_create_binaurals_on_notes,
    &b_create_binaurals_from_pitch,
    &sp_bd_separation,
    &sp_bd_pairs,
    &lf_bd_start_pitch,
    &lf_bd_spacing,
    &ol_justification,
    &ol_key_note,
    &cb_resize_separation,
    &cb_close_octave,
  };

  widget* wbde [] = {
    &bbd_select_all,
    &bbd_select_none,
    &bbd_invert_select,
    &bbd_delete,
    &bbd_sync,
    &bbd_select2,
    &bd_modulate_up,
    &bd_modulate_down,
    &bbd_modulate,
    &bbd_flip,
    &il_binaural_drones,
    &lf_vol_fade_time,
    &lf_master_volume,
    &lf_pitch_fade_time,
    &lf_modulation_amount,
    &ol_select_what,
    &ol_select_rule,
    &bdf_value,
    &lf_l,
    &lf_r,
    &lf_sep,
    &lf_vol,
    &ol_just
  };

  il_binaural_drones.sel_lis = &bdl;
  lf_sep.fld.set_text (0.0f);
  ol_just.set_text (binaural_drones_listener::justs[binaural_drone::CENTER]);
  ol_just.set_listener (&bdl);

  bd_modulate_up.set_size (24);
  bd_modulate_down.set_size (24);
  bd_modulate_down.set_dir (arrow_button::left);

#ifndef __WIDGET_MOVE__
  for (int i = 0; i < 10; ++i) static_cast<button*>(wbde[i])->set_listener (&bdl);
  for (int i = 0; i < 2; ++i) static_cast<button*>(wbd[i])->set_listener (&bdl); // binaural drones commands
  cb_close_octave.set_listener (&bdl);
  cb_resize_separation.set_listener (&bdl);
#endif

  ol_select_what.set_listener (&bdl);
  ol_select_rule.set_listener (&bdl);
  ol_justification.set_listener (&bdl);
  ol_key_note.set_listener (&bdl);

  label_field* lfb [] = {
    &lf_bd_start_pitch, &lf_bd_spacing, &lf_master_volume, &lf_vol_fade_time,
    &lf_pitch_fade_time, &lf_modulation_amount, &lf_l, &lf_r, &lf_sep, &lf_vol
  };
  string lfbs [] = {
    "Start Pitch (Hz)", "Spacing (Hz)", "Master volume (%)", "Volume fade time (secs)",
    "Pitch fade time (secs)", "Amount", "L (Hz)", "R (Hz)", "Separation (Hz)", "Volume (%)"
  };
  for (int i = 0; i < 10; ++i) {
    label_field* li = lfb[i];
    li->set_label (lfbs[i]);
    li->set_listener (&bdl);
  }

  sp_bd_separation.set ("Separation (Hz)", 1.0f, 0.0f, MILLION, 0, 0);
  sp_bd_separation.set_value (1.0f);
  sp_bd_separation.orient = mouse_slider_listener::NONE;

  sp_bd_pairs.set ("Number of Pairs", 1, 1, MILLION, 0, 0);
  sp_bd_pairs.set_value (1);
  sp_bd_pairs.orient = mouse_slider_listener::NONE;

  for (int i = 0; i < 24; ++i) {
    editors.push_back (weds[i]);
    //weds[i]->set_moveable(1);
  }

  widget** wmap [] = {
    wfile,
    winst,
    weds,
    wvoice,
    wdrone_tools,
    wdrone_params,
    wranges,
    wmisc,
    wedtools,
    wmon,
    wmon_pars,
    wmon_ballops,
    wmon_boxops,
    wmon_misc,
    wbd,
    wbde,
    0
  };
  int numw [] = {
    10,
    4,
    24,
    12,
    58,
    DRONE_PARAMS_N,
    35,
    20,
    57,
    38,
    17,
    26,
    9,
    18,
    10,
    23,
    0
  };

  lf_conn_steps.set_label ("Steps");
  LISTEN(lf_conn_steps, &stepsl)
  lf_conn_steps.fld.typing_lsnr = &stepsl;
  lf_conn_steps.fld.expr = 0;

  DECL_COLOR_SLIDERS
  color clrs [] = {color(1.0f, 0.0f, 0.0f), color(0.0f, 1.0f, 0.0f), color(0.0f, 0.0f, 1.0f),
                    color(1.0f, 0.0f, 0.0f), color(0.0f, 1.0f, 0.0f), color(0.0f, 0.0f, 1.0f)};

  for (int i = 0; i < COLOR_SLIDERS_M; ++i) {
    slider<float>& si = dynamic_cast<slider<float>&>(*slrs [i]);
    si.set_width_height (128, si.extents.height);
    color& ci = clrs [i];
    si.set_color (ci.r, ci.g, ci.b);
    si.set_limits (0.0f, 1.0f);
#ifndef __WIDGET_MOVE__
    si.set_listener (&cscl, &cssl);
#endif
  }

  colorer.schemes[0]=&gc_top;
  colorer.schemes[1]=&gc_bottom;
  colorer.schemes[2]=&gc_blend;
  colorer.schemes[3]=&gc_rand;
  colorer.i = colorer_t::RANDOM;

  LISTEN(ol_color,&ocoll)
  LISTEN(ol_fixed,&fxl)
  LISTEN(ol_snap_style,&sdl);

  separator* seps [] = {&separators.main, &separators.dp0, &separators.dp1};
  const char* sepsn [] = {"main", "dp0", "dp1"};
  for (int i = 0; i < 3; ++i) {
    separator& si = *seps[i];
    si.set_name (sepsn[i]);
  }

  for (int i = 0; i < 17; ++i) {
    vector<widget*>& vw = tab_members[cb_tabs[i]];
    int n = numw[i];
    widget** wmi = wmap[i];
    for (int m = 0; m < n; ++m) {
      vw.push_back (wmi[m]);
#ifdef __WIDGET_MOVE__
      wmi[m]->set_moveable(1); // to move item
#endif
    }
    vw.push_back (&separators.main);
  }
 
  sp_voice_volume.set ("Volume", 0.001f, -MILLION, MILLION, &vvl);
  sp_drone_master_vol.set ("Master volume", 0.001f, -MILLION, MILLION, &dmvol);
  masvol0.set_state (1);
  sp_drone_master_vol.lim = &masvol0;

  sp_drone_vol.set ("Volume", 0.01f, -MILLION, MILLION, &dvol);
  sp_drone_vol.set_value (0.0f);

  l_octave_shift.set_text ("Octave Shift");
  ab_octave_down.set_dir (arrow_button::left);
  ab_octave_up.set_dir (arrow_button::right);

#ifndef __WIDGET_MOVE__
  ab_octave_down.set_listener (&osl);
  ab_octave_up.set_listener (&osl);
#endif
  int arrow_size = 24;
  ab_octave_up.set_size (arrow_size);
  ab_octave_down.set_size (arrow_size);
  sp_octave_shift_bpm.set ("BPM", 1, 0.0f, MILLION, &osl);

  sp_gater_bpm.set ("BPM", 1, 0, MILLION, &gbl);
  ol_gater_style.set_text (" style = ");
  ol_gater_style.set_listener (&gater_style_lis);

  l_gater.set_text ("Beater");
  l_gater.add_child (&sp_gater_bpm);
  l_gater.add_child (&ol_gater_style);

#ifdef __WIDGET_MOVE__

  int wek [] = {8, 14, 2, 3, 7, 3, 4, 4, 6};
  for (int i = 0, j = 4, k = 0; i < 9; ++i) {
    k = wek[i];
    makehier (&wedtools[j], k);
    j += k;
  }

  widget* osn [] = {&l_octave_shift, &ab_octave_down, &ab_octave_up, &b_abort_octave_shift, &sp_octave_shift_bpm, 0};
  makefam (osn, 5);

  widget* brs [] = {&ol_set_range, &b_default_to_selected, &b_default_to_all, &b_selected_to_all, 0};
  makehier (brs);

  widget* aht [] = {&l_adjust_height, &b_adjust_range_height, &b_adjust_board_height, 0};
  makehier (aht);

  widget* arb [] = {&l_adjust_range, &b_adjust_range_left, &b_adjust_range_right, &b_adjust_range_both, 0};
  makehier (arb);

  widget* brm [] = {&l_ran_mod, &b_rm_pause_resume, &b_rm_start_all, &b_rm_stop_all, &b_rm_toggle, 0};
  makehier (brm);

  widget* brc [] = {&ol_change_note, &b_change_note_left, &b_change_note_right, &b_change_note_both, &ol_change_note_style, 0};
  makehier (brc);

  widget* wmmv [] = {&cb_modulation, &cb_visual, &cb_motion, &cb_chuck, &cb_defaults, 0};
  makehier (wmmv);

  widget* dpw0 [] = {&cb_show_vel, &cb_show_accel, &cb_show_gravity, &cb_show_anchors, 0};
  makehier (dpw0);

  widget* dpw3 [] = {&sp_dam_depth, &sp_dfm_depth, &sp_dam_bpm,  &sp_dfm_bpm, &damd0, &dfmd0, 0};
  makehier (dpw3);

  widget* dpw4 [] = { &sp_drones_per_min, &dpl, &sp_drone_lifetime, &sp_orbit_insertion_time,  0};
  makehier (dpw4);

  widget* dpw5 [] = {&sp_change_drone_trail_length, &sp_change_drone_handle_size, 0};
  makehier (dpw5);

  widget* dpw6 [] = {&ol_set_unset_toggle, &b_toggle, &b_set, &b_unset, 0};
  makehier (dpw6);

  widget* rmw [] = {&cb_mod_ran, &ol_fixed, &sp_ran_mod_width, &sp_ran_mod_width_bpm, &sp_ran_mod_height, &sp_ran_mod_height_bpm, 0};
  makehier (rmw);

  widget* dpw [] = {&ol_create_this,&dp_orient,&dp_numdrones,&dp_bpm1, 0};
  makehier (dpw);

  widget *rcw [] = {&ol_create_this, &sp_mesh_rows, &sp_mesh_cols, &cb_sync_rows_cols, &b_flip_rows_cols, 0};
  makehier (rcw, 5);
  widget* dow [] = {&l_drone_order, &ol_drone_order, &ol_mesh_point, &f_mesh_xy, 0};
  makehier (dow, 4);
  widget* mshw [] = {&sp_mesh_rows, &sp_mesh_dur, &l_use_drone_pend, 0,};
  makehier (mshw, 3);
  sp_mesh_rows.add_child (&l_drone_order);
  widget* dpp [] = {&sp_drones_per_pend, &b_set_to_mesh_rows, &b_set_to_mesh_cols, 0};
  makehier (dpp);
  widget* apt [] = {&l_apply_to, &cb_am_bpm, &cb_fm_bpm, 0};
  makehier (apt);
  widget* udp [] = {&l_use_drone_pend, &sp_drones_per_pend, &l_apply_to, 0};
  makehier (udp);
  widget* wsp [] = {&sp_snap_left, &sp_snap_right, &ol_snap_style, 0};
  makehier (wsp);
  widget* wda [] = {&l_drone_arrow, &b_arrow_reset, &dronearrow.neck, &dronearrow.shoulder.width, &dronearrow.shoulder.position, &dronearrow.cap, &dronearrow.decap, 0};
  makehier (wda);
  widget* wda2 [] = {
    &dronearrowdefaults.lbl,
    &dronearrowdefaults.arrow,
    &dronearrowdefaults.neck,
    &dronearrowdefaults.shoulder.width,
    &dronearrowdefaults.shoulder.position,
    &dronearrowdefaults.cap,
    0,
  };
  makehier (wda2);
  widget* wbo [] = {&sp_bounces, &sp_rebound, &ol_bounce_style, 0};
  makehier (wbo);
  widget* wconn [] = {&b_disconnect, &b_connect, &lf_conn_steps, &cb_conn_wrap, &trackcon, 0};
  makehier (wconn);
  widget* wsl [] = {&s_red_min, &s_red_max, &s_green_min, &s_green_max, &s_blue_min, &s_blue_max, &ol_color, 0};
  makehier (wsl);

  {
    widget* w [] = {
      &b_select_all_drones,
      &b_invert_drone_selection,
      &b_select_attractees,
      &b_select_attractors,
      &b_select_launchers,
      &b_select_tracked_drones,
      &sp_browse_drone, 0,
    };
    makehier (w);
  }

  {
    widget* w[] = {
      &ol_add_wand,
      &b_delete_drones,
      &moverailstrafe,
      &b_rotate_drones,
      &b_scale_drones,
      &b_set_xform_center,
      &b_freeze_drones,
      &b_thaw_drones, 0,
    };
    makehier (w);
  }

  {
    widget* w[] = {
      &b_mute,
      &b_unmute,
      &drone2noise,
      &noise2drone,
      &gabt, 0,
    };
    makehier (w);
  }

  {
    widget* w[] = {
      &b_launch_drones,
      &b_stop_launching_drones,
      &balloon,
      &chuck,
      &b_orbit_selected_drones,
      &b_track_drones,
      &b_set_targets,
      &b_clear_targets, 0,
    };
    makehier(w);
  }

  {
    widget* w[] = {&riset, &fallt, &lifetime, &ddpm, &ddpl, &gens, 0};
    makehier (w);
  }

  {

    widget* w[] = {&handlesize, &trailsize, 0};
    makehier (w);
  }

  {
    widget* w[] = {&mortalize, &reincarnate, &immortalize, 0};
    makehier (w);
  }

  {
    widget* w[] = {&chflip, &chtog, &chspeed, &chlen, &chtrail, &chapt, &choutline, &chautoresettrails, 0};
    makehier (w);
  }

  {
    widget* w[] = {
      &l_draw_ball,
      &cb_draw_ball_position,
      &cb_draw_ball_heading,
      &cb_draw_ball_trails,
      0
    };
    makehier(w);
  }

  {
    widget* w[] = {
      &cb_draw_boxes,
      &cb_fill_boxes,
      &texture,
      &text_ure,
      &texstep,
      0
    };
    makehier(w);
  }
  {
    widget* w[] = {
      &abm_left,
      &abm_right,
      &abm_up,
      &abm_down,
      &bm_zoom_in,
      &bm_zoom_out,
      0
    };
    makehier(w);
  }
  {
    widget* w[] = {
      &cb_draw_notes,
      &cb_label_notes,
      &cb_label_hz_vol,
      0
    };
    makehier(w);
  }


  {
    widget* w[] = {
      &am2fm,
      &fm2am,
      &sp_dam_pos,
      &sp_dfm_pos,
      0
    };
    makehier(w);
  }

  /*
  {
    widget* w[] = {
    };
    makehier(w);
  }
  */


#endif

  sp_range.set ("Range", 1, &ranl);
  sp_range.draw_more = sp_range.variance.ui = 0;

  const char* rms [] = {"Width", "BPM", "Height", "BPM"};
  spinner<float>* srm [] = {&sp_ran_mod_width, &sp_ran_mod_width_bpm, &sp_ran_mod_height, &sp_ran_mod_height_bpm};
  int srl [] = {-MILLION, 0, -MILLION, 0};
  int sro[] = {mouse_slider_listener::X, mouse_slider_listener::X, mouse_slider_listener::Y, mouse_slider_listener::Y};
  change_listener<field>* rlis[] = {&rmwl, &rmwbl, &rmhl, &rmhbl};
  for (int i = 0; i < 4; ++i) {
    spinner<float>* sp = srm[i];
    sp->set (rms[i], 1.0f, srl[i], MILLION, rlis[i]);
    sp->orient = sro[i];
  }

  const char* rde [] = {"Default width", "Default height"};
  spinner<int>* sde [] = {&sp_default_width, &sp_default_height};
  int msd [] = {mouse_slider_listener::X, mouse_slider_listener::Y};
  for (int i = 0; i < 2; ++i) {
    spinner<int>& si = *sde[i];
    si.set (rde[i], 1, 0, MILLION, &rdel);
    si.orient = msd[i];
    si.draw_more = si.variance.ui = 0;
  }

  // drones
  //
  sp_change_drone_trail_length.set ("Trail length", 1, &dtl);
  sp_change_drone_trail_length.set_value (0);

  sp_change_drone_handle_size.set ("Handle size", 1, &dhsl);
  sp_change_drone_handle_size.set_value (0);

  sp_change_drone_vel.set ("Velocity", 0.1f, -MILLION, +MILLION, &dvl);
  sp_change_drone_accel.set ("Acceleration", 0.01f, -MILLION, +MILLION, &dal);
  sp_change_drone_vel.lim = &vel0;
  sp_change_drone_accel.lim = &accel0;

  sp_rotate_drone_vel.set ("Rotate velocity", 1.0f, &rdvl);
  rotaccel.set ("Rotate acceleration", 1.0f, &rdvl);
  sp_rotate_drone_vel.f_value.id = 0;
  rotaccel.f_value.id = 1;
  static const int rotdirsize = 12;
  rdvel.set_size (rotdirsize);
  rdaccel.set_size (rotdirsize);

  sp_drones_per_min.set ("Launches per minute", 1.0f, &dpml);
  dpl.set ("Drones per launch", 1.0f, &dpll);
  sp_drone_lifetime.set ("Lifetime", 0.01f, &dlf);

  sp_orbit_insertion_time.set ("Orbit insert time", 0.01f, &oil);

  sp_browse_drone.set ("Browse drone", 1, &brwdl);
  sp_browse_drone.draw_more = sp_browse_drone.variance.ui = 0;

  ol_create_this.set_listener (this);

  dp_orient.set_listener (this);
  dp_numdrones.set ("Number of Drones", 1, 2, MILLION, this);
  dp_bpm1.set ("BPM", 1.0f, 0, MILLION, this);

  spinner<int>* msh [] = {&sp_mesh_rows, &sp_mesh_cols};
  static const char* const mlb [] = {"Rows", "Columns"};
  for (int i = 0; i < 2; ++i) {
    spinner<int>* sp = msh[i];
    sp->set (mlb[i], 1, 2, MILLION, this, 0);
    sp->set_value (2);
    sp->orient = mouse_slider_listener::NONE;
    sp->draw_more = sp->variance.ui = 0;
  }

  cb_sync_rows_cols.set_text ("Sync");

  l_drone_order.set_text ("Create drones");
  ol_drone_order.set_listener (this);
  ol_mesh_point.set_listener (this);

  f_mesh_xy.set_text ("0 0");
  f_mesh_xy.change_lsnr = this;

  sp_mesh_dur.set (0.1f, 0, MILLION, this);
  sp_mesh_dur.set_value (1.0f);
  sp_mesh_dur.set_text ("In", SECONDS);

  ol_set_unset_toggle.set_listener (this);

  ol_am_style.set_listener (&am_style_lis); // voice am
  ol_fm_style.set_listener (&fm_style_lis); // voice fm
 
  l_phrase_position.set_text ("Phrase position ");
  s_phrase_position.set_limits (0.0f, 1.0f);

#ifndef __WIDGET_MOVE__
  ol_create_this.option.set_listener (&dcl);
  cb_sync_rows_cols.set_listener (&dcl);
  s_phrase_position.set_listener (this);
  b_key_to_pitch_at_cursor.set_listener (&miscl);
#endif
 
  const char* lrs [] = {"Adjust Height?", "Adjust?", "Modulation?", "Change Note"};
  label* lrm [] = {&l_adjust_height, &l_adjust_range, &l_ran_mod, &ol_change_note.option};
  for (int i = 0; i < 4; ++i) lrm[i]->set_text (lrs[i]);

  ol_set_range.set_listener (&sral);

  const char* snp [] = {"Snap left", "Snap right"};
  spinner<float>* snsp [] = {&sp_snap_left, &sp_snap_right};
  float snv [] = {din0.dinfo.snap.left, din0.dinfo.snap.right};
  for (int i = 0; i < 2; ++i) {
    spinner<float>* si = snsp[i];
    si->set (snp[i], 0.01f, &sdl);
    si->draw_more = 0;
    si->variance.ui = 0;
    si->set_value (snv[i]);
    si->orient = mouse_slider_listener::X;
  }

  l_drone_arrow.set_text ("Drone Arrow");
  dronearrowdefaults.lbl.set_text ("Drone Arrow");
  spinner<float>* das [] = {&dronearrow.shoulder.position, &dronearrow.neck, &dronearrow.shoulder.width};
  spinner2<float>* das2 [] = {&dronearrowdefaults.shoulder.position, &dronearrowdefaults.neck, &dronearrowdefaults.shoulder.width};
  const char* dasl [] = {"Shoulder Position", "Neck", "Shoulder Width"};
  float vals [] = {drone::arrowt::U, drone::arrowt::K, drone::arrowt::V};
  for (int i = 0; i < 3; ++i) {
    spinner<float>& si = *das[i];
    spinner2<float>& si2 = *das2[i];
    si.set (dasl[i], 0.1f, -MILLION, MILLION, &arrowl);
    si2.set (dasl[i], 0.1f, -MILLION, MILLION, &defarrowl, 0);
    si2.set_value (vals[i]);
    si2.orient = mouse_slider_listener::NONE;
  }
  for (int i = 0; i < 2; ++i) {
    spinner<float>& si = *das[i];
    spinner2<float>& si2 = *das2[i];
    si.updowndecinc ();
    si2.updowndecinc ();
  }
  dronearrowdefaults.cap.set_listener (&defarrowl);
  dronearrowdefaults.arrow.calc ();

  cb_scope.set_text ("Show oscilloscope");
  cb_scope.set_listener (&scol);
  sp_scope_height.set ("Height", 1, 0, MILLION, &scol);
  sp_scope_samples.set ("Samples", 1, 1, MILLION, &scol);
  scopacity.set ("Opacity", 0.01f, 0.0f, 1.0f, &scol);

  l_tap_bpm.set_text ("Tap BPM");
  checkbutton* cb_tars [] = {&cb_am, &cb_fm, &cb_gater, &cb_octave_shift, &cb_auto_reset};
  static const char* const cb_text [] = {"AM", "FM", "Beater", "Octave Shift", "Auto reset"};

  cb_auto_reset.turn_on ();
  for (int i = 0; i < 5; ++i) {
    checkbutton* cbi = cb_tars[i];
    cbi->set (cb_text[i], &tbl);
    l_tap_bpm.add_child (cbi);
  }
  td_tap_display.set_listener (&tbl);

  l_tap_bpm.add_child (&td_tap_display);
  l_tap_bpm.add_child (&l_tap_bpm_value);

  const char* bpmstr [] = {"os", "fm", "am", "gr"};
  spinner<float>* bpmspn [] = {&sp_octave_shift_bpm, &sp_fm_bpm, &sp_am_bpm, &sp_gater_bpm};
  for (int i = 0; i < 4; ++i) bpm_map[bpmstr[i]] = bpmspn[i];

  // editor tools
  arrow_button* enav[] = {&abe_left, &abe_down, &abe_right, &abe_up};
  int edirs [] = {arrow_button::left, arrow_button::down, arrow_button::right, arrow_button::up};
  for (int i = 0; i < 4; ++i) {
    arrow_button* ab = enav[i];
    ab->set_dir (edirs[i]);
  }

  l_snap.set_text ("Snap?");
  l_library.set_text ("Library");
  lf_curve_name.set_label ("Curve name");
  lf_curve_name.fld.expr = 0;
  l_capture.set_text ("Mouse capture");

  ol_mirror_tangents.set_listener (this);
  ol_curve_style.set_listener (this);

  sp_curve_rpm.set ("RPM", 1.0f, 0.0f, MILLION, &col);
  sp_curve_rpm.set_value (0.0f);

  sp_curve_every.set (1.0f, 0, MILLION, &col);
  sp_curve_every.set_listener (&col, 2); // variance
  sp_curve_every.set_text ("Every", DEGREES);

#ifndef __WIDGET_MOVE__
  cb_show_waveform_samples.set_listener (&col);
#endif

  crvwav.hz.set ("Hz", 1.0f, 0.01f, MILLION, &col);
  crvwav.periods.set ("Cycles", 1, 1, MILLION, &col);
  crvwav.time.set ("Time", 1.0f, 1, MILLION, &col);
  crvwav.hz.orient = crvwav.periods.orient = crvwav.time.orient = mouse_slider_listener::NONE;

  sp_curve_limit.set ("Curve roughness", 0.001f, 0.001f, MILLION, &col);

  // button labels
  const char* labels [] = {
    "Menu",
    "Microtonal Keyboard",
    "Keyboard Keyboard",
    "Mondrian",
    "Binaural Drones",
    "Waveform",
    "Drone Waveform",
    "Drone Modulation",
    "Voice Modulation",
    "Beater",
    "Waveform",
    "Attack",
    "Decay",
    "MIDI Velocity",
    "Delays",
    "Octave Shift",
    "Compressor",
    "Morse Code",
    "Exit DIN Is Noise",
    "Show anchors",
    " Move",
    "Delete",
    "Select all",
    "Invert selected",
    "Record a phrase",
    "Clear phrase",
    "Default to Selected",
    "Default to all",
    "Selected to all",
    "Set key to pitch at cursor",
    "Insert vertex",
    "Delete vertex",
    "Fold tangents",
    "Undo",
    "Redo",
    "Copy",
    " Paste",
    "Draw & replace curve",
    "Add",
    "Replace",
    "Delete",
    "Start",
    "Assign",
    "Label vertices",
    "Selection only",
    "Show waveform",
    "Pick curve",
    "Stop",
    "Draw curve",
    "Clear",
    "Record",
    "Select attractees",
    "Select attractors",
    "Orbit",
    "Show velocity",
    "Show acceleration",
    "Show gravity",
    "Balloon",
    "Launch",
    "Stop launch",
    "Track",
    "Select tracked",
    "Waveform",
    "Attack",
    "Decay",
    "Add ",
    "Move balls",
    "Delete selected balls",
    "Delete all balls",
    "Select all balls",
    "Invert ball selection",
    "Select balls in box",
    "Split box horizontally",
    "Split box vertically",
    "Delete box",
    "Freeze balls",
    "Thaw balls",
    "Turn Off UI",
    "Set targets",
    "Clear targets",
    "Clear modulations",
    "Modulate balls up",
    "Modulate balls down",
    "Create binaural drones on the notes of the scale",
    "Create binaural drones using parameters above",
    "Waveform",
    "Close octave",
    "Auto-change ball direction clockwise",
    "Auto-change ball direction anti-clockwise",
    "Stop auto-changing ball direction",
    "Flip ball direction",
    "Randomize box color",
    "Resize separation",
    "Add / Remove slits",
    "Toggle wreckers",
    "Toggle healers",
    "Toggle bouncers",
    "Healers <> Wreckers",
    "Select wreckers",
    "Select healers",
    "Remove slits on edge",
    "Toggle slit animation",
    "Auto adjust voices",
    "Draw boxes",
    "Fill boxes",
    "Draw notes",
    "Label notes",
    "Position",
    "Make note grid",
    "Make N x N grid",
    "Delete all boxes",
    "Select launchers",
    "Select on Creation",
    "Freeze",
    "Thaw",
    "Mark segments",
    "Auto split box",
    "Auto delete box",
    "Speed",
    "Turn",
    "Teleport",
    "Heading",
    "Trails",
    "Draw ball:",
    "Sync",
    "Sync",
    "Clone",
    "Clone can clone too",
    "Transform",
    "Label pitch and volume",
    "All",
    "None",
    "Invert",
    "Delete",
    "Sync",
    "Modulate",
    "Select",
    "Flip",
    "All",
    "Left",
    "Right",
    "Both",
    "Modulate",
    "Mark",
    "Pause/Resume",
    "Start",
    "Stop",
    "Toggle",
    "Get",
    "Range modulation",
    "Selected",
    "Left",
    "Right",
    "Set",
    "Unset",
    "Toggle",
    "Range Width & Height",
    "Flip",
    "Overlay Instrument/Editor",
    "Range Pitch & Volume",
    "Swap",
    "Overlay pitch distribution",
    "Overlay volume distributon",
    "Point Modulation",
    "Both",
    "AM BPM",
    "FM BPM",
    "Set to Rows",
    "Set to Columns",
    "Noise Interpolator",
    "Ball triggers note <> Ball triggers noise",
    "Scale",
    "Rotate",
    "Draw mesh outline",
    "Modulation",
    "Motion",
    "Visual",
    "Connect",
    "Disconnect",
    "Wrap",
    "Drone Pendulum",
    "Abort",
    "Close",
    "Reset",
    "Mute",
    "Unmute",
    "Drone > Noise",
    "Noise > Drone",
    "Find center",
    "Defaults",
    "Mortalize",
    "Reincarnate",
    "Immortalize",
    "Chuck",
    "Chuck",
    "Flip",
    "Toggle",
    "Draw chuck outline",
    "Auto reset trails",
    "Cap",
    "Decap",
    "Cap",
    "Track",
    "Auto rotate",
    "Auto flip",
    "-ve",
    "Randomize",
    "0",
    "0",
    "Sync",
    "Reset every tick",
    "Reset every tick",
    "Drone velocity modulation",
    "X",
    "Y",
    "Both",
    "None",
    "Set AM BPM to FM BPM",
    "Set FM BPM to AM BPM",
    "Flip",
    "Autoflip",
    "Reset",
    "Auto pause",
    "0",
    "0",
    "0",
    "Texture",
    "Size tangent",
    "Draw vertices & tangents",
    "Track",
    "View too",
    "Draw",
    "Draw cursor",
    "Pin / Unpin",
    "0",
    // next label
  };

  button* buttons [] = {
    &b_menu,
    &b_microtonal_keyboard,
    &b_keyboard_keyboard,
    &b_mondrian,
    &b_binaural_drones,
    &b_microtonal_keyboard_waveform,
    &b_drone_waveform,
    &b_drone_modulation,
    &b_voice_modulation,
    &b_gater,
    &b_keyboard_keyboard_waveform,
    &b_attack,
    &b_decay,
    &b_midi_velocity,
    &b_delays,
    &b_octave_shift,
    &b_compressor,
    &b_morse_code,
    &b_exit_din,
    &cb_show_anchors,
    &moverailstrafe.option,
    &b_delete_drones,
    &b_select_all_drones,
    &b_invert_drone_selection,
    &b_record_phrase,
    &b_clear_phrases,
    &b_default_to_selected,
    &b_default_to_all,
    &b_selected_to_all,
    &b_key_to_pitch_at_cursor,
    &b_insert_vertex,
    &b_delete_vertex,
    &b_fold_tangents,
    &b_undo,
    &b_redo,
    &b_copy,
    &ol_paste.option,
    &b_draw_replacement_curve,
    &b_add_curve,
    &b_replace_curve,
    &b_delete_curve,
    &b_start_capture,
    &b_assign_capture,
    &cb_label_vertices,
    &cb_selection_only,
    &cb_show_waveform_samples,
    &b_pick_curve,
    &b_stop_rotating,
    &cb_draw_curve,
    &b_clear_record,
    &cb_record,
    &b_select_attractees,
    &b_select_attractors,
    &b_orbit_selected_drones,
    &cb_show_vel,
    &cb_show_accel,
    &cb_show_gravity,
    &balloon,
    &b_launch_drones,
    &b_stop_launching_drones,
    &b_track_drones,
    &b_select_tracked_drones,
    &b_mondrian_waveform,
    &b_mondrian_attack,
    &b_mondrian_decay,
    &b_add_balls,
    &b_move_selected_balls,
    &b_delete_selected_targets,
    &b_delete_all_targets,
    &b_select_all_targets,
    &b_invert_selected_targets,
    &b_select_targets_in_box,
    &b_split_horizontal,
    &b_split_vertical,
    &b_delete_box,
    &b_freeze_balls,
    &b_thaw_balls,
    &b_turn_off_ui,
    &b_set_targets,
    &b_clear_targets,
    &b_clear_modulations,
    &b_modulate_balls_up,
    &b_modulate_balls_down,
    &b_create_binaurals_on_notes,
    &b_create_binaurals_from_pitch,
    &b_binaural_drones_waveform,
    &cb_close_octave,
    &b_auto_change_direction_clockwise,
    &b_auto_change_direction_anti_clockwise,
    &b_stop_auto_changing_direction,
    &b_flip_direction,
    &b_make_random_color,
    &cb_resize_separation,
    &b_add_remove_slits,
    &b_toggle_wreckers,
    &b_toggle_healers,
    &b_toggle_bouncers,
    &b_switch_ball_type,
    &b_select_wreckers,
    &b_select_healers,
    &b_remove_slits_on_edge,
    &b_toggle_slit_anim,
    &cb_mondrian_auto_adjust_voices,
    &cb_draw_boxes,
    &cb_fill_boxes,
    &cb_draw_notes,
    &cb_label_notes,
    &cb_draw_ball_position,
    &b_make_note_grid,
    &b_make_nxn_grid,
    &b_delete_all_boxes,
    &b_select_launchers,
    &seloncre,
    &b_freeze_drones,
    &b_thaw_drones,
    &cb_mark_segments,
    &cb_auto_split_box,
    &cb_auto_delete_box,
    &cb_speed,
    &cb_turn,
    &cb_teleport,
    &cb_draw_ball_heading,
    &cb_draw_ball_trails,
    &l_draw_ball,
    &cb_turn_sync,
    &cb_speed_sync,
    &cb_clone,
    &cb_clone_can_clone,
    &cb_transform,
    &cb_label_hz_vol,
    &bbd_select_all,
    &bbd_select_none,
    &bbd_invert_select,
    &bbd_delete,
    &bbd_sync,
    &bbd_modulate,
    &bbd_select2,
    &bbd_flip,
    &b_adjust_board_height,
    &b_adjust_range_left,
    &b_adjust_range_right,
    &b_adjust_range_both,
    &cb_mod_ran,
    &cb_mark_ran,
    &b_rm_pause_resume,
    &b_rm_start_all,
    &b_rm_stop_all,
    &b_rm_toggle,
    &b_get_cur_ran,
    &b_range_modulation,
    &b_adjust_range_height,
    &b_change_note_left,
    &b_change_note_right,
    &b_set,
    &b_unset,
    &b_toggle,
    &b_range_width_height,
    &b_flip_rows_cols,
    &cb_overlay,
    &b_range_pitch_vol,
    &b_swap_curves,
    &cb_pitch_dis,
    &cb_vol_dis,
    &b_point_modulation,
    &b_change_note_both,
    &cb_am_bpm,
    &cb_fm_bpm,
    &b_set_to_mesh_rows,
    &b_set_to_mesh_cols,
    &b_noise_interpolator,
    &b_ball_trig,
    &b_scale_drones,
    &b_rotate_drones,
    &cb_draw_mesh,
    &cb_modulation,
    &cb_motion,
    &cb_visual,
    &b_connect,
    &b_disconnect,
    &cb_conn_wrap,
    &b_drone_pend,
    &b_abort_octave_shift,
    &b_close,
    &b_arrow_reset,
    &b_mute,
    &b_unmute,
    &drone2noise,
    &noise2drone,
    &b_set_xform_center,
    &cb_defaults,
    &mortalize,
    &reincarnate,
    &immortalize,
    &cb_chuck,
    &chuck,
    &chflip,
    &chtog,
    &choutline,
    &chautoresettrails,
    &dronearrow.cap,
    &dronearrow.decap,
    &dronearrowdefaults.cap,
    &trackcon,
    &dva.autorotate.cb,
    &dva.autoflip.cb,
    &dva.neg,
    &dva.randomize,
    &vel0,
    &accel0,
    &dva.sync,
    &dva.autorotate.uet.deg,
    &dva.autorotate.uet.tps,
    &edlife2vel,
    &b_snapx,
    &b_snapy,
    &b_snapboth,
    &b_snapnone,
    &am2fm,
    &fm2am,
    &ed_flip_rotation,
    &cb_ed_autoflip,
    &ed_autoflip_reset,
    &dva.autopause.cb,
    &damd0,
    &dfmd0,
    &masvol0,
    &texture,
    &size_tangent,
    &draweditables,
    &track_phrase_position,
    &viewtoo,
    &drawsnaps,
    &drawcursor,
    &pinunpin,
    &sep0
    // next button
  };

  dlog << "+++ Labeling buttons +++" << endl;

  for (int i = 0; i < 235; ++i) {
    button* bi = buttons[i];
    bi->set_text (labels[i]);
  }

  dlog << "+++ Labeled buttons +++" << endl;

  am2fm.id = modulator::AM;
  fm2am.id = modulator::FM;
  LISTEN(am2fm, &am2fm2aml)
  LISTEN(fm2am, &am2fm2aml)

  LISTEN(cb_modulation,&cmod)
  LISTEN(cb_motion,&cmot)
  LISTEN(cb_visual,&cvis)
  LISTEN(cb_defaults,&cdef)
  LISTEN(cb_chuck,&cch)
  LISTEN(dronearrow.cap,&arrowl)
  LISTEN(dronearrow.decap,&arrowl)


#ifndef __WIDGET_MOVE__
  LISTEN (b_arrow_reset,&awl)
#endif

  ol_select_what.set_text ("L");
  ol_select_rule.set_text (" >= ");
  bdf_value.change_lsnr = &bdl;
  bdf_value.set_text (bdl.val[binaural_drones_listener::GREATER_THAN_EQUAL]);

  sp_bounces.set ("Bounces", 1, -1, MILLION, &bol);
  ol_bounce_style.set_listener (this);

  sep_bin.set ("Binaural Separation (Hz)", 0.01f, -MILLION, MILLION, &sepbinl);

  sp_rebound.set ("Rebound Speed %", 1, 0, MILLION, &rebl);

  sp_mondrian_min_voices.set ("Min Voices", 1, 1, MILLION, &monl);
  sp_mondrian_min_voices.draw_more = 0;
  sp_mondrian_min_voices.variance.ui = 0;

  cb_mondrian_auto_adjust_voices.set_listener (&monl);

  sp_mondrian_change_attack_time.set ("Ball attack time", 0.01f, &batl);
  sp_mondrian_change_decay_time.set ("Ball decay time", 0.01f, &bdtl);

  sp_mondrian_change_speed.set_text ("Ball speed");
  sp_mondrian_change_speed.set_listener (&bsl);
  sp_mondrian_change_speed.set_listener (&monl, 1);

  sp_mondrian_change_dir.set ("Ball direction", 1, &brl);
  sp_mondrian_change_dir.orient = mouse_slider_listener::X;
  sp_mondrian_change_dir.draw_more = 0;
  sp_mondrian_change_dir.variance.ui = 0;

  sp_mondrian_change_trail_size.set ("Ball trail length", 1, &tll);
  sp_mondrian_change_note_poly_points.set ("Note polygon points", 1, 2, MILLION, &nppl);
  sp_mondrian_change_note_poly_radius.set ("Note polygon radius", 1, 0, MILLION, &nprl);

  sp_mondrian_change_slit_size.set ("Slit size", 1.0f, &ssl);

  sp_mondrian_change_slit_anim_time.set ("Slit open/close time", 0.01f, &satl);
  sp_mondrian_change_slit_anim_time.set_value (0.0f);

  sp_mondrian_change_vol.set ("Ball volume", 0.01f, -MILLION, MILLION, &bvl);
  sp_mondrian_change_vol.set_value (0);

  sp_mondrian_num_boxes.set ("N", 1, 0, MILLION, &monl);
  sp_mondrian_num_boxes.draw_more = 0;
  sp_mondrian_num_boxes.variance.ui = 0;

  sp_auto_split_time.set ("", 0.1f, 0.01f, MILLION, &monl);
  sp_auto_split_time.set_text ("Every", SECONDS);

  sp_auto_delete_time.set ("", 0.1f, 0.01f, MILLION, &monl);
  sp_auto_delete_time.set_text ("Every", SECONDS);

  sp_min_split_size.set ("Min split size", 1, 2, MILLION, &monl);

  texstep.set ("Step", 1, 1, 1023, &stepl, 0);
  text_ure.set_text ("din is noise");
  text_ure.typing_lsnr = &mondrian0;

 
#ifndef __WIDGET_MOVE__
  cb_auto_split_box.set_listener (&monl);
  cb_auto_delete_box.set_listener (&monl);
#endif

  options_list* olt [] = {&ol_bouncer, &ol_wrecker, &ol_healer};
  for (int i = 0; i < 3; ++i) olt[i]->set_listener (&bolis);

  button* pb[] = {&sp_mondrian_change_dir.inc, &sp_mondrian_change_dir.dec};
  set_repeat (pb, 2, 0.005);

  recl.typing (lf_file.fld);

  {

    nullt* sp [] = {
      &sp_change_drone_handle_size,
      &sp_change_drone_trail_length,
      &autorotate.rpm,
      &autorotate.deg,
      &autorotate.tps,
      &sp_mondrian_change_vol,
      &sp_mondrian_change_attack_time,
      &sp_mondrian_change_decay_time,
      &sp_mondrian_change_speed,
      &sp_mondrian_change_slit_size,
      &sp_mondrian_change_slit_anim_time,
      &sp_change_drone_vel,
      &sp_change_drone_accel,
      &sp_dam_depth,
      &sp_dfm_depth,
      &sp_dam_bpm,
      &sp_dfm_bpm,
      &sp_drone_vol,
      &dronearrow.shoulder.position,
      &dronearrow.shoulder.width,
      &sp_rotate_drone_vel,
      &sp_drones_per_min,
      &sp_drone_lifetime,
      &sp_orbit_insertion_time,
      &sp_mondrian_change_attack_time,
      &sp_mondrian_change_decay_time,
      &sp_mondrian_change_speed,
      &sp_mondrian_change_dir,
      &sp_mondrian_change_trail_size,
      &sp_mondrian_change_vol,
      &sp_mondrian_change_slit_size,
      &sp_mondrian_change_slit_anim_time,
      &chspeed,
      &chlen,
      &chtrail,
      &autorotate.autoflip.angle,
      &dronearrow.neck,
      &dronearrow.shoulder.width,
      &dronearrow.shoulder.position,
      &dpl,
      0
    };

    int i = 0;
    while (sp[i] != 0) sp[i++]->null = 1;

  }

 

}

void menu::update () {
  position_menu_items ();
  position_tabs ();
}

void menu::position_menu_items () {
  static const int lines = 4;
  int targety = view.ymax - lines * line_height;
  int dy = targety - cb_file.extents.bottom;
  for (int p = 0; p < nitems; ++p) items[p]->move (0, dy, 0);
}

void menu::loadspinners () {
  file_in fi ("spinners");
  ifstream& f = fi ();
  f >> handlesize >> trailsize >> dva.mag >> lifetime >> dronearrowdefaults.neck >> dronearrowdefaults.shoulder.width >> dronearrowdefaults.shoulder.position;
  f >> dva.autorotate.rpm >> dva.autorotate.dps >> dva.autorotate.tps >> dva.autoflip.deg >> gabt;
  f >> riset >> fallt;
  f >> sp_bounces >> sp_rebound;
  f >> gens >> ddpm >> ddpl >> sp_curve_every >> dva.autopause.every >> dva.autopause.f0r;
  f >> uis.settings_scr.binaural.sep;
}

void menu::savespinners () {
  file_out fo ("spinners");
  ofstream& f = fo ();
  f << handlesize << trailsize << dva.mag << lifetime << dronearrowdefaults.neck << dronearrowdefaults.shoulder.width << dronearrowdefaults.shoulder.position;
  f << dva.autorotate.rpm << dva.autorotate.dps << dva.autorotate.tps << dva.autoflip.deg << gabt;
  f << riset << fallt << sp_bounces << sp_rebound << gens << ddpm << ddpl << sp_curve_every << dva.autopause.every << dva.autopause.f0r << uis.settings_scr.binaural.sep << endl;
}


void menu::setup () {
  dlog << "*** setting up menu ***" << endl;
  show = screen_mousex = screen_mousey = 0;
  setup_items ();
  widget_load ("d_menu", items, nitems);
  loadspinners ();
  initcolorsliders ();
  b_menu.set_listener (&mbl);
  dlog << "+++ menu setup complete +++" << endl;
}

void menu::set_pos (int x, int y) {
  b_menu.set_pos (x, y);
}

void menu::draw () {

  b_menu.draw ();

  if (show) {

    // draw bg
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glColor4f (0.0f, 0.0f, 0.0f, opacity);
      glRecti (bg_extents.left, bg_extents.bottom, bg_extents.right, bg_extents.top);
    glDisable (GL_BLEND);

    glPolygonMode (GL_FRONT, GL_LINE);
    glColor3f (0.5f, 0.5f, 0.5f);
    glRecti (bg_extents.left, bg_extents.bottom, bg_extents.right, bg_extents.top);
    glPolygonMode (GL_FRONT, GL_FILL);

    // draw items
    for (int i = 0; i < num_tabs; ++i) tabs[i]->draw ();
    if (cur_tab) {
      vector<widget*>& ti = tab_members [cur_tab];
      for (int i = 0, j = ti.size (); i < j; ++i) ti[i]->draw ();
      if (cur_tab == &cb_mkb_voice && din0.phrasor0.state == phrasor::playing) s_phrase_position.set_val (din0.phrasor0.amount);
    }
  }
}

void menu::setup_tabs (ui* scr) {
  checkbutton* com [] = {&cb_file, &cb_instrument, &cb_editors, 0};
  checkbutton* mkb [] = {&cb_mkb_voice, &cb_mkb_drone_tools, &cb_mkb_drone_params, &cb_mkb_ranges, &cb_mkb_misc, 0};
  checkbutton* eds [] = {&cb_ed_tools, &cb_ed_curves, 0};
  checkbutton* mon [] = {&cb_mon_tools, &cb_mon_parameters, &cb_mon_ballops, &cb_mon_boxops, &cb_mon_misc, 0};
  checkbutton* bd [] = {&cb_binaural_drones_tools, &cb_binaural_drones_edit, 0};
  int clear_existing_tabs = 1;
  setup_tabs (com, clear_existing_tabs);
  if (scr == &din0) { // microtonal keyboard
    setup_tabs (mkb);
  } else if (scr == &keybd2) { // keyboard-keyboard
  } else if (scr == &mondrian0) { // mondrian
    setup_tabs (mon);
  } else if (scr == &binaural_drones0) { // binaural drones
    setup_tabs (bd);
  } else { // is an editor
    setup_tabs (com, clear_existing_tabs);
    setup_tabs (eds);
    next_tab = &cb_ed_tools;
    next_tab_instr = get_current_instrument ();
  }
  position_tabs ();
}

void menu::setup_tabs (checkbutton** tlist, int clear) {
  if (clear) tabs.clear ();
  int i = 0;
  checkbutton* ci = tlist[i];
  while (ci) {
    tabs.push_back (ci);
    ci = tlist[++i];
  }
  num_tabs = tabs.size ();
}

void menu::position_tabs () {

  if (num_tabs) {

    int x = cb_file.posx, y = cb_file.posy, spacing = 2 * fnt.spacing.word;
    int i = 1, j = 0;
    for (; i < num_tabs;) {
      x = x + get_char_width (tabs[j]->text) + spacing;
      tabs[i]->set_pos (x, y);
      ++i;
      j = i - 1;
    }

    int ss = x + get_char_width (tabs[j]->text) - cb_file.posx;
    separators.main.set_extents (ss);
    separators.dp0.set_extents (ss);
    separators.dp1.set_extents (ss);

    calc_bg ();

    checkbutton* lt = tabs[num_tabs - 1];
    menu_mousex = lt->extents.right + 1;
    menu_mousey = view.ymax - lt->posy;

  }
}

void menu::remove_from_tab (checkbutton* cb, widget* w) {
  vector<widget*>& tw = tab_members [cb];
  vector<widget*>::iterator end = tw.end (), i = find (tw.begin(), end, w);
  if (i != end) tw.erase (i);
}

void menu::add_to_tab (checkbutton* cb, widget* w) {
  vector<widget*>& tw = tab_members[cb];
  vector<widget*>::iterator end = tw.end (), i = find (tw.begin(), end, w);
  if (i == end) tw.push_back (w);
}

void menu::populatecurvestab (curve_editor* ed) {
  destroycurvestab ();
  vector<curve_info>& cis = ed->curveinfo;
  vector<widget*>& tc = tab_members [&cb_ed_curves];
  int lh = line_height, x = cb_file.extents.left, y = separators.main.extents.bottom - lh;
  for (int i = 0, j = ed->curves; i < j; ++i) {
    curve_info& ci = cis[i];
    checkbutton* cb = new checkbutton;
    tc.push_back (cb);
    cb->id = i;
    cb->set_text (ci.curve->name);
    cb->set_state (ci.visible, 0);
    cb->set_pos (x, y);
    cb->set_listener (&curvesl);
    y -= lh;
  }
}

void menu::destroycurvestab () {
  vector<widget*>& tw = tab_members [&cb_ed_curves];
  for (int i = 1, j = tw.size (); i < j; ++i) {
    widget* wi = tw[i];
    delete wi;
  }
  tw.clear ();
  tw.push_back (&separators.main);
}

void menu::set_drone_params_items (int s, int e) {
  vector<widget*>& tw = tab_members[&cb_mkb_drone_params];
  tw.clear ();
  tw.push_back (&separators.main);
  DECL_DRONE_PARAMS
  for (int i = 0; i < 10; ++i) {
    add_to_tab (&cb_mkb_drone_params, wdrone_params[i]);
  }
  for (int i = s; i < e; ++i) {
    add_to_tab (&cb_mkb_drone_params, wdrone_params[i]);
  }
  calc_bg ();
}

int menu::handle_input () {

  if (b_menu.handle_input ()) return 1;

  if (show) {

    if (wheel && !widget::HOVER) move_items (0, wheel * wheely);

    // find current tab
    for (int i = 0; i < num_tabs; ++i) tabs[i]->handle_input ();

    if (cur_tab) { // handle tab's items
      vector<widget*>& tm = tab_members [cur_tab];
      for (int i = 0, j = tm.size (); i < j; ++i) if (tm[i]->handle_input ()) return 1;
    }

  }

  return 0;

}

void menu::toggle (int mouse_warp) {

  static int removedpluginbrowser;

  show = !show;

  if (show) {

    b_menu.set_text ("Close menu");

    if (b_close.visible) b_close.call_listener ();

    if (uis.current->inst) {
      if (uis.current == &din0) {
        din0.dinfo.gravity.hide ();
        uis.remove (&mkb_selector);
      }
      else if (uis.current == &mondrian0)
        uis.remove (&mon_selector);
    } else {
      uis.remove (&CRVED->capturer);
      uis.remove (&CRVED->pomo);
      removedpluginbrowser = uis.remove (&uis.plugin__browser);
      if (CRVED->fft_enabled) uis.remove (&fft0);
      if (CRVED->curcrvchgd) CRVED->setup_tools_menu ();
    }

    style_listener* sl [] = {&gater_style_lis, &am_style_lis, &fm_style_lis};
    for (int i = 0; i < 3; ++i) sl[i]->get_style ();

    screen_mousex = mousex;
    screen_mousey = mousey;
    ::warp_mouse (menu_mousex, menu_mousey);

    if (next_tab && (next_tab_instr == get_current_instrument())) {
      changed (*next_tab);
      cur_tab = next_tab;
      next_tab = 0;
      next_tab_instr = 0;
    }

  } else {
    b_menu.set_text ("Menu");
    menu_mousex = mousex;
    menu_mousey = mousey;
    if (uis.current->inst) {
      if (uis.current == &din0 ) {
        if (MENU.cb_show_gravity.state) din0.dinfo.gravity.show ();
        if (!din0.adding && !b_close.visible) uis.add (&din0, &mkb_selector);
      }
      else if (uis.current == &mondrian0)
        uis.widgets_of [&mondrian0].push_back (&mon_selector);
    } else {
      uis.widgets_of[CRVED].push_back (&CRVED->capturer);
      uis.widgets_of[CRVED].push_back (&CRVED->pomo);
      if (removedpluginbrowser) uis.widgets_of[CRVED].push_back (&uis.plugin__browser);
      if (CRVED->fft_enabled) uis.widgets_of[CRVED].push_back (&fft0);
    }
    if (mouse_warp) warp_mouse (screen_mousex, screen_mousey);
  }

  uis.update_bottom_line ();

}

void menu::set_ball_ops (ball* b) {

  ball_op* ops [ball_op::NUM_OPS] = {&b->op_turn, &b->op_speed, &b->op_teleport, &b->op_clone, &b->op_transform};
  checkbutton* cbn [ball_op::NUM_OPS] = {&cb_turn, &cb_speed, &cb_teleport, &cb_clone, &cb_transform};
  for (int i = 0; i < ball_op::NUM_OPS; ++i) cbn[i]->set_state (ops[i]->alarm.active, 0);

  turn& trn = b->op_turn;
  sp_turn_min.set_value (-trn.rd.min);
  sp_turn_max.set_value (trn.rd.max);

  speed& spd = b->op_speed;
  sp_speed_min.set_value (-spd.rd.min);
  sp_speed_max.set_value (spd.rd.max);
  sp_max_speed.set_value (spd.max);

  teleport& tel = b->op_teleport;
  sp_tel_radius.set_value (tel.radius);

  Clone& clo = b->op_clone;
  sp_clone_offset.set_value (clo.offset);
  sp_max_clones.set_value (clo.max);
  sp_max_balls.set_value (Clone::max_balls);
  cb_clone_can_clone.set_state (clo.clone_can_clone);

  Transform& tf = b->op_transform;

  ball_op* bpa [] = {&trn, &spd, &tel, &clo, &tf};
  spinner<float>* spa [] = {&sp_turn_every, &sp_speed_every, &sp_tel_every, &sp_clone_every, &sp_transform_every};
  for (int i = 0; i < ball_op::NUM_OPS; ++i) spa[i]->set_value (bpa[i]->alarm.triggert);
 
}

void menu::clear_ball_ops () {
  checkbutton* cbn [ball_op::NUM_OPS] = {&cb_turn, &cb_speed, &cb_teleport, &cb_clone, &cb_transform};
  for (int i = 0; i < ball_op::NUM_OPS; ++i) cbn[i]->set_state (0, 0);
}

CLICKED_BUTTON(menu, b_menu_lis) {
  cons << YELLOW << "You can use right click to open / close the menu and mouse wheel to scroll" << eol;
  TOGGLEMENU
}

CLICKED_BUTTON(menu, b_inst_lis) {
  int i = b.id;
  scope.save_current_instrument ();
  CURRENT_INSTRUMENT = i;
  INSTRUMENT = INSTRUMENTS[i];
  load_instrument ();
}

CLICKED_BUTTON(menu, b_ed_lis) {
  int i = b.id;
  ui* ed = uis.uis[i];
  uis.load_editor (ed);
  setup_plugin_labels ();
  if (curve_picker.visible) curve_picker.hide();
}

void misc_listener::clicked (button& b) {
  if (&b == MENUP.b_exit_din)
    try_quit ();
  else if (&b == MENUP.b_turn_off_ui) {
    turn_off_ui ();
    return;
  } else
    din0.set_key_to_pitch_at_cursor ();
  TOGGLEMENU
}

void menu::move_items (int dx, int dy) {
  for (int i = 0; i < nitems; ++i) {
    widget* wi = items[i];
    wi->move (dx, dy, 0);
  }

  if (CRVED) {
    vector<widget*>& vc = tab_members[&cb_ed_curves];
    int n = vc.size ();
    for (int i = 1; i < n; ++i) vc[i]->move (dx, dy, 0);
  }

  calc_bg ();
}

void menu::changed (checkbutton& tb) { // current tab has changed

  cur_tab = &tb;
  tb.turn_on (DONT_CALL_LISTENER); // must be always on

  if (cur_tab == last_tab) return;

  if (last_tab) last_tab->turn_off (DONT_CALL_LISTENER);

  last_tab = cur_tab;

  calc_bg ();

  opacity = 0.9f;
  if (&tb == &cb_mon_ballops || &tb == &cb_mon_boxops || &tb == &cb_mon_misc) opacity = 0.5f;

  // save last tab to reload when loading new instrument
  extern checkbutton* LAST_TABS [];
  checkbutton* com [] = {&cb_file, &cb_instrument, &cb_editors, &cb_ed_tools, &cb_ed_curves}; // ignore these tabs
  for (int i = 0; i < 5; ++i) if (com[i] == cur_tab) return;
  LAST_TABS [CURRENT_INSTRUMENT] = cur_tab;

}


VALUE_CHANGED(menu,ancopalis) {
  drone::anchoropacity = MENU.ancopa.value;
  cons << "Drone anchor opacity = " << drone::anchoropacity << eol;
}

VALUE_CHANGED(menu,sp_stiff_lis) {
  drone::STIFFNESS = MENU.sp_stiff.value;
  clamp (0.0f, drone::STIFFNESS, 1.0f);
  cons << "Connection stiffness = " << drone::STIFFNESS << eol;
}

VALUE_CHANGED(menu,gabt_lis) {
  drone::gabt = MENU.gabt.value;
  din0.gab.setgabt ();
  cons << YELLOW << "Mute/unmute and Drone <> Noise time = " << drone::gabt << SECONDS << eol;
}

VALUE_CHANGED(menu,sp_dam_depth_lis) {
  din0.change_drone_depth (modulator::AM, MENU.sp_dam_depth);
}

VALUE_CHANGED(menu,sp_dfm_depth_lis) {
  din0.change_drone_depth (modulator::FM, MENU.sp_dfm_depth);
}

VALUE_CHANGED(menu,sp_dam_pos_lis) {
  MENU.sp_dam_pos.set_value (f);
  din0.change_drone_modpos (modulator::AM, MENU.sp_dam_pos);
}

VALUE_CHANGED(menu,sp_dfm_pos_lis) {
  MENU.sp_dfm_pos.set_value (f);
  din0.change_drone_modpos (modulator::FM, MENU.sp_dfm_pos);
}

VALUE_CHANGED(menu,sp_dam_bpm_lis) {
  din0.change_drone_bpm (modulator::AM, MENU.sp_dam_bpm);
}

VALUE_CHANGED(menu,sp_dfm_bpm_lis) {
  din0.change_drone_bpm (modulator::FM, MENU.sp_dfm_bpm);
}

VALUE_CHANGED(menu,sp_am_depth_lis) {
  din0.change_depth (din::AM, MENU.sp_am_depth());
}

VALUE_CHANGED(menu,sp_fm_depth_lis) {
  din0.change_depth (din::FM, MENU.sp_fm_depth());
}

VALUE_CHANGED(menu,sp_am_bpm_lis) {
  float v = MENU.sp_am_bpm.value;
  v = din0.am.set_bpm (v);
  cons << YELLOW << "Voice AM bpm = " << v << " or " << v / 60.0f << " hz" << eol;
}

VALUE_CHANGED(menu,sp_fm_bpm_lis) {
  float v = MENU.sp_fm_bpm.value;
  v = din0.fm.set_bpm (v);
  cons << YELLOW << "Voice FM bpm = " << v << " or " << v / 60.0f << " hz" << eol;
}

VALUE_CHANGED(menu,sp_browse_drone_lis) {
  din0.browsed_drone = MENU.sp_browse_drone.value;
  din0.browse_drone (0);
}

VALUE_CHANGED (menu,sp_bounces_lis) {
  int& n = din0.dinfo.bounce.n;
  n = MENU.sp_bounces.value;
  if (n == -1) {
    cons << RED << "Drones will not bounce from bottom of the microtonal keyboard!" << eol;
    checkbutton& cb = MENU.sp_bounces.variance.cb;
    cb.set_state (0, 0);
  } else cons << GREEN << "Max bounces = " << n << eol;
}

VALUE_CHANGED(menu,sp_rebound_lis) {
  din0.dinfo.bounce.speed = MENU.sp_rebound.value;
  cons << GREEN << "Rebound Max Speed %  = " << din0.dinfo.bounce.speed << eol;
}

void menu::changed (field& f) {

  if (&f == &sp_mesh_rows.f_value) {
    din0.dinfo.rows = sp_mesh_rows.value;
    if (cb_sync_rows_cols.state) {
      din0.dinfo.cols = din0.dinfo.mesh_vars.dpp = din0.dinfo.rows;
      sp_mesh_cols.set_value (din0.dinfo.cols);
      sp_drones_per_pend.set_value (din0.dinfo.mesh_vars.dpp);
    }
    mkb_selector.set_mesh (din0.meshh.create, din0.dinfo.rows, din0.dinfo.cols);
    picked (ol_mesh_point.option, 0);
  } else if (&f == &sp_mesh_cols.f_value) {
    din0.dinfo.cols = sp_mesh_cols.value;
    if (cb_sync_rows_cols.state) {
      din0.dinfo.rows = din0.dinfo.mesh_vars.dpp = din0.dinfo.cols;
      sp_mesh_rows.set_value (din0.dinfo.rows);
      sp_drones_per_pend.set_value (din0.dinfo.mesh_vars.dpp);
    }
    mkb_selector.set_mesh (din0.meshh.create, din0.dinfo.rows, din0.dinfo.cols);
    picked (ol_mesh_point.option, 0);
  } else if (&f == &sp_mesh_dur.f_value) {
    float t = sp_mesh_dur.value;
    din0.dinfo.mesh_vars.duration = t;
    cons << "Make mesh in " << t << SECONDS << eol;
  } else if (&f == &f_mesh_xy) {
    int r, c;
    tokenizer z (f.text);
    z >> r >> c;
    if (clamp (0, r, din0.dinfo.rows-1) || clamp (0, c, din0.dinfo.cols-1) ) {
      sprintf (BUFFER, "%d %d", r, c);
      f_mesh_xy.set_text(BUFFER);
    }
    proximity_orderer::ROW = r;
    proximity_orderer::COL = c;
  } else if (&f == &dp_numdrones.f_value) {
    din0.dinfo.drone_pend.n = MENU.dp_numdrones.value;
    cons << YELLOW << "Number of Drones = " << din0.dinfo.drone_pend.n << eol;
  } else if (&f == &dp_bpm1.f_value) {
    din0.dinfo.drone_pend.bpm = MENU.dp_bpm1.value;
    cons << YELLOW << "Drone Pendulum BPM = " << din0.dinfo.drone_pend.bpm << eol;
  }
}

void menu::changed (slider<float>& s) {
  din0.phrasor0.set_cur (s());
}

void menu::picked (label& lbl, int dir) {
  const static char* mit [] = {" Tangents are not mirrored", " Tangents are mirrored"};
  const static char* bbs [] = {" Drones bounce ahead", " Drones bounce back", " Drones bounce ahead or back"};
  const static char *sut [] = {" Snap drones to notes", " Position affects vectors"};
  const static char *ofl [] = {" in ascending rows", " in descending rows"," in ascending columns", " in descending columns", " randomly", " nearest to", " farthest from" };
  const static char *wpt [] = {"bottom left", "bottom right", "top left", "top right", "center", "random point", "custom point"};
  const static char *cwt [] = {" Create Drone Mesh", " Create Drone Pendulum"};
  const static char *ort [] = {" Orientation = Horizontal", " Orientation = Vertical"};
  const static char *mir [] = {" Horizontal Flip ", " Vertical Flip ", " Horizontal Flip (Local) ", " Vertical Flip (Local) " };
  const static char *dris [] = {" Drone is Drone", " Drone is Noise", " Drone is Drone or Noise"};

  static const int npt = 7, npt_1 = npt-1;
  static widget* mshw [] = {
    &sp_mesh_rows,
    &sp_mesh_cols,
    &cb_sync_rows_cols,
    &l_drone_order,
    &sp_mesh_dur,
    &b_flip_rows_cols,
    &ol_drone_order,
    &l_use_drone_pend,
    &sp_drones_per_pend,
    &l_apply_to,
    &cb_am_bpm,
    &cb_fm_bpm,
    &b_set_to_mesh_rows,
    &b_set_to_mesh_cols,
    &ol_mesh_point,
    &f_mesh_xy,
  };
  static widget* dpw [] = {
    &dp_orient,
    &dp_numdrones,
    &dp_bpm1,
  };

  if (&lbl == &ol_mirror_tangents.option) {
    CRVED->mirror_tangents = !CRVED->mirror_tangents;
    lbl.set_text (mit[CRVED->mirror_tangents]);
  } else if (&lbl == &ol_curve_style.option) {
    CRVED->toggle_curve_style ();
  } else if (&lbl == &ol_bounce_style.option) {
    din0.dinfo.bounce.style += dir;
    wrap<int> (din_info::bouncet::AHEAD, din0.dinfo.bounce.style, din_info::bouncet::RANDOM);
    lbl.set_text (bbs[din0.dinfo.bounce.style]);
  } else if (&lbl == &ol_set_unset_toggle.option) {
    din0.dinfo.set_unset_toggle = !din0.dinfo.set_unset_toggle;
    lbl.set_text (sut[din0.dinfo.set_unset_toggle]);
    b_toggle.set_pos (lbl.extents.right + 20, b_toggle.posy);
    b_set.set_pos (b_toggle.extents.right + 10, b_toggle.posy);
    b_unset.set_pos (b_set.extents.right + 10, b_toggle.posy);
  } else if (&lbl == &ol_drone_order.option) {
    din0.dinfo.mesh_vars.order += dir;
    wrap (0, din0.dinfo.mesh_vars.order, LAST_ORDERER);
    lbl.set_text (ofl[din0.dinfo.mesh_vars.order]);
    if (din0.dinfo.mesh_vars.order > 4) {
      add_to_tab (&cb_mkb_drone_tools, &ol_mesh_point);
      add_to_tab (&cb_mkb_drone_tools, &f_mesh_xy);
      ol_mesh_point.set_pos (lbl.extents.right + 10, lbl.extents.bottom);
      f_mesh_xy.set_pos (ol_mesh_point.option.extents.right + 10, f_mesh_xy.extents.bottom);
    } else {
      remove_from_tab (&cb_mkb_drone_tools, &ol_mesh_point);
      remove_from_tab (&cb_mkb_drone_tools, &f_mesh_xy);
    }
  } else if (&lbl == &ol_mesh_point.option) {
    din0.dinfo.mesh_vars.point += dir;
    wrap (0, din0.dinfo.mesh_vars.point, npt_1);
    sprintf (BUFFER, " %s @ ", wpt[din0.dinfo.mesh_vars.point]);
    lbl.set_text (BUFFER);
    int cols_1 = din0.dinfo.cols - 1;
    int rows_1 = din0.dinfo.rows - 1;
    rnd<int> rdr (0, rows_1), rdc (0, cols_1);
    int ROW [] = {0, 0, rows_1, rows_1, din0.dinfo.rows / 2, rdr(), proximity_orderer::ROW};
    int COL [] = {0, cols_1, 0, cols_1, din0.dinfo.cols / 2, rdc(), proximity_orderer::COL};
    proximity_orderer::ROW = ROW [din0.dinfo.mesh_vars.point];
    proximity_orderer::COL = COL [din0.dinfo.mesh_vars.point];
    sprintf (BUFFER, "%d %d", proximity_orderer::ROW, proximity_orderer::COL);
    f_mesh_xy.set_text (BUFFER);
    f_mesh_xy.set_pos (lbl.extents.right + 10, f_mesh_xy.extents.bottom);
  } else if (&lbl == &ol_create_this.option) {
    din0.dinfo.create_this += dir;
    wrap (0, din0.dinfo.create_this, 1);
    int cw = din0.dinfo.create_this;
    lbl.set_text (cwt[cw]);
    if (cw) { // drone pendulum
      for (int i = 0; i < 16; ++i) remove_from_tab (&cb_mkb_drone_tools, mshw[i]);
      for (int i = 0; i < 3; ++i) add_to_tab (&cb_mkb_drone_tools, dpw[i]);
    } else { // mesh
      for (int i = 0; i < 3; ++i) remove_from_tab (&cb_mkb_drone_tools, dpw[i]);
      int j = 14; if (din0.dinfo.mesh_vars.order > 4) j = 16;
      for (int i = 0; i < j; ++i) add_to_tab (&cb_mkb_drone_tools, mshw[i]);
    }
    calc_bg ();
  } else if (&lbl == &dp_orient.option) {
    din0.dinfo.drone_pend.orient += dir;
    wrap (0, din0.dinfo.drone_pend.orient, 1);
    int o = din0.dinfo.drone_pend.orient;
    lbl.set_text (ort[o]);
  } else if (&lbl == &ol_mirror.option) {
    CRVED->axis += dir;
    wrap<int> (curve_editor::MIRROR_X, CRVED->axis, curve_editor::MIRROR_BBY);
    lbl.set_text (mir[CRVED->axis]);
    calc_bg ();
  } else if (&lbl == &ol_drone_is.option) {
    drone::IS += dir;
    wrap<int> (drone::DRONE, drone::IS, drone::RANDOM);
    const char* di = dris [drone::IS];
    lbl.set_text (di);
  }
}

void menu::calc_bg () {
  if (cur_tab && num_tabs) {
    vector<widget*>& v = tab_members [cur_tab];
    if (v.size () == 0) return;
    widget* w0 = v[0];
    bg_extents.left = cb_file.extents.left;
    bg_extents.right = bg_extents.left;
    bg_extents.bottom = w0->extents.bottom;
    bg_extents.top = tabs[0]->extents.top;
    for (int i = 0, j = v.size (); i < j; ++i) {
      widget* wi = v[i];
      if (wi->extents.left < bg_extents.left) bg_extents.left = wi->extents.left;
      if (wi->extents.right > bg_extents.right) bg_extents.right = wi->extents.right;
      if (wi->extents.bottom < bg_extents.bottom) bg_extents.bottom = wi->extents.bottom;
      if (wi->extents.top > bg_extents.top) bg_extents.top = wi->extents.top;
    }
    static const int GUTTER = 5;
    bg_extents.resize (GUTTER, GUTTER);
  }

}

void menu::show_editors (ui* inst) {
  // refer to weds
  int starts [] = {0, 4, 15, 18};
  int ends [] = {4, 15, 18, 19};
  int starti = starts[CURRENT_INSTRUMENT], endi = ends[CURRENT_INSTRUMENT];
  vector<widget*>& tw = tab_members [&cb_editors];
  for (int i = starti; i < endi; ++i) {
    widget* ei = editors[i];
    tw.push_back (ei);
  }
}

void menu::hide_editors () {
  vector<widget*>& tw = tab_members [&cb_editors];
  for (int i = 0; i < 19; ++i) {
    widget* ei = editors[i];
    vector<widget*>::iterator itr = find (tw.begin(), tw.end(), ei);
    if (itr != tw.end()) tw.erase (itr);
  }
}

void menu::update_bpm (const string& name, float value) {
  spinner<float>* psp = bpm_map [name];
  if (psp) psp->set_value (value);
}

void menu::mark_tap_target () {
  interpreter ("set taptarget");
  tokenizer tz (interpreter.result);
  while (1) {
    string target; tz >> target;
    if (target == "") break;
    if (target == "gr") cb_gater.turn_on (DONT_CALL_LISTENER);
    else if (target == "am") cb_am.turn_on (DONT_CALL_LISTENER);
    else if (target == "fm") cb_fm.turn_on (DONT_CALL_LISTENER);
    else if (target == "os") cb_octave_shift.turn_on (DONT_CALL_LISTENER);
  }
}

menu::~menu () {
  dlog << "--- destroying menu ---" << endl;

#ifdef __WIDGET_MOVE__
  widget_save ("d_menu", items, nitems);
#endif
  savespinners ();
  savecolorsliders ();
  destroycurvestab ();

  dlog << "--- destroyed menu ---" << endl;
}

void octave_shift_listener::clicked (button& b) {
  if (&b == MENUP.ab_octave_up || &b == &uis.ab_octave_up) modulate_up (); else modulate_down ();
}

void octave_shift_listener::changed (field& f) {
  float v = 0.0f;
  if (&f == MENUP.sp_octave_shift_bpm.f_value) {
    v = MENU.sp_octave_shift_bpm.value;
    uis.sp_octave_shift_bpm.set_value (v);
  }
  else {
    v = uis.sp_octave_shift_bpm.value;
    MENU.sp_octave_shift_bpm.set_value (v);
  }
  v = octave_shift.set_bpm (v);
  static const string los = "Octave shift BPM = ";
  cons << YELLOW << los << v << eol;
}

void voice_volume_listener::changed (field& f) {
  VOICE_VOLUME = MENU.sp_voice_volume.value;
  static const string vv ("Volume = ");
  cons << YELLOW << vv << VOICE_VOLUME << eol;
}

void drone_master_volume_listener::changed (field& f) {
  din0.setdronemastervolume (MENU.sp_drone_master_vol.value);
}

void gater_bpm_listener::changed (field& f) {
  float v = MENU.sp_gater_bpm.value;
  v = din0.gatr.set_bpm (v);
  static const string gt = "Beater BPM = ";
  cons << YELLOW << gt << v << eol;
}

void drone_handle_size_listener::changed (field& f) {
  din0.change_drone_handle_size (MENU.sp_change_drone_handle_size);
}

void drone_trail_length_listener::changed (field& f) {
  din0.change_drone_trail_points (MENU.sp_change_drone_trail_length);
}

void change_drone_vel_listener::changed (field& f) {
  din0.change_drone_vel (MENU.sp_change_drone_vel);
}

void change_drone_accel_listener::changed (field& f) {
  din0.change_drone_accel (MENU.sp_change_drone_accel);
}

void rotate_drone_vec_listener::changed (field& f) {
  int id = f.id;
  spinner<float>* sp [] = {MENUP.sp_rotate_drone_vel, MENUP.rotaccel};
  rotdir* rd [] = {MENUP.rdvel, MENUP.rdaccel};
  din0.rotate_drone_vec (id, *sp[id], rd[id]->val);
}

void drones_per_min_listener::changed (field& f) {
  din0.change_drones_per_min (MENU.sp_drones_per_min);
}

void menu::dpllis::changed (field& f) {
  din0.change_drones_per_launch (MENU.dpl);
}

void drone_lifetime_listener::changed (field& f) {
  din0.change_drone_lifetime (MENU.sp_drone_lifetime);
}

void orbit_insertion_time_listener::changed (field& f) {
  din0.change_orbit_insertion_time (MENU.sp_orbit_insertion_time);
}

void style_listener::set_style (const string& style) {
  for (int i = 0; i < num_styles; ++i) {
    if (styles[i] == style) {
      id = i;
      string command ("set-style " + what + " " + style);
      interpreter (command);
      oplist.set_text (prefix + style);
    }
  }
}

void style_listener::get_style () {
  string command ("get-style " + what);
  interpreter (command);
  oplist.set_text (prefix + interpreter.result);
}

void style_listener::next_style (int dir) {
  id += dir;
  if (id < 0) id = last_style; else if (id >= num_styles) id = 0;
  set_style (styles[id]);
}

void style_listener::picked (label& lbl, int dir) {
  next_style (dir);
}

void drone_commands_listener::changed (checkbutton& cb) {
  int state = cb.state;
  if (&cb == MENUP.cb_show_vel) din0.dinfo.vel = state; else
  if (&cb == MENUP.cb_show_accel) din0.dinfo.accel = state; else
  if (&cb == MENUP.cb_show_gravity) {
    gravity_t& gr = din0.dinfo.gravity;
    if (state) uis.add (&din0, &gr); else uis.remove (&gr);
  } else
  if (&cb == MENUP.cb_show_anchors) din0.dinfo.anchor = state; else
  if (&cb == MENUP.seloncre) din0.dinfo.seloncre = state;
  else din0.dinfo.mesh_vars.sync = cb.state;
}

void phrase_commands_listener::clicked (button& b) {
  TOGGLEMENU
  if (&b == MENUP.b_record_phrase) din0.do_phrase_recording ();
  else if (&b == MENUP.b_clear_phrases) din0.clear_all_phrases ();
}

void range_data::read_mod () {
  int& a = din0.ranges[din0.dinfo.sel_range].mod.active;
  mod = a;
  a = 0;
}

void range_data::write_mod () {
  din0.ranges[din0.dinfo.sel_range].mod.active = mod;
}

void range_height_listener::clicked (button& b) {
  mouse_slider0.add (MENUP.rhl);
  MENU.rhl.read_mod ();
  activate_mouse_slider ();
}

void range_height_listener::moused (int dh, double scl) {
  din0.height_changed (din0.dinfo.sel_range, dh);
}

void range_height_listener::after_slide () {
  MENU.rhl.write_mod ();
}

void board_height_listener::read_mod () {
  int n = din0.num_ranges;
  moda.resize (n);
  for (int i = 0; i < n; ++i) {
    int& a = din0.ranges[i].mod.active;
    moda[i]=a;
    a = 0;
  }
}

void board_height_listener::write_mod () {
  int n = din0.num_ranges;
  for (int i = 0; i < n; ++i) din0.ranges[i].mod.active = moda[i];
}

void board_height_listener::moused (int dh, double scl) {
  din0.height_changed (-1, dh);
}

void board_height_listener::clicked (button& b) {
  MENU.bhl.name = "Board height";
  MENU.bhl.orient = mouse_slider_listener::Y;
  mouse_slider0.add (MENUP.bhl);
  MENU.bhl.read_mod ();
  activate_mouse_slider ();
}

void board_height_listener::after_slide () {
  MENU.bhl.write_mod ();
}

void set_range_listener::clicked (button& b) {
  if (&b == MENUP.b_selected_to_all) {
    din0.selected_range_to_all (i);
  } else if (&b == MENUP.b_default_to_selected) {
    din0.default_range_to_selected (i);
  } else if (&b == MENUP.b_default_to_all) {
    din0.default_range_to_all (i);
  }
}

void set_range_listener::picked (label& lbl, int dir) {
  i = !i;
  static const char* opts [] = {" Set Width?", " Set Height?"};
  lbl.set_text (opts[i]);
}

void range_width_listener::clicked (button& b) {
  if (&b == MENUP.b_adjust_range_left) {
    MENU.arl.range = din0.dinfo.sel_range;
    MENU.arl.read_mod ();
    mouse_slider0.add (MENUP.arl);
    activate_mouse_slider ();
  } else if (&b == MENUP.b_adjust_range_right) {
    MENU.arr.range = din0.dinfo.sel_range;
    MENU.arr.read_mod ();
    mouse_slider0.add (MENUP.arr);
    activate_mouse_slider ();
  } else {
    MENU.arb.range = din0.dinfo.sel_range;
    MENU.arb.read_mod ();
    mouse_slider0.add (MENUP.arb);
    activate_mouse_slider ();
  }
  din0.adjustranges.set ();
}

void adjust_range_left_listener::moused (int dir, double scl) {
  if (din0.range_left_changed (range, dir, 1)) {
    din0.refresh_drones (0, range);
    din0.find_visible_ranges ();
  }
}

void adjust_range_left_listener::after_slide () {
  MENU.arl.write_mod ();
  din0.adjustranges.unset ();
}

void adjust_range_right_listener::moused (int dir, double scl) {
  if (din0.range_right_changed (range, dir, 1)) {
    din0.refresh_drones (range, din0.last_range);
    din0.find_visible_ranges ();
  }
}

void adjust_range_right_listener::after_slide () {
  MENU.arr.write_mod ();
  din0.adjustranges.unset ();
}

void adjust_range_both_listener::moused (int dir, double scl) {
  int rl = din0.range_left_changed (range, -dir, 1);
  int rr = din0.range_right_changed (range, dir, 1);
  if (rl || rr) {
    din0.refresh_all_drones ();
    din0.find_visible_ranges ();
  }
}

void adjust_range_both_listener::after_slide () {
  MENU.arb.write_mod ();
  din0.adjustranges.unset ();
}

VALUE_CHANGED(menu, sp_range_lis) {
  din0.dinfo.sel_range = MENU.sp_range.value;
  MENU.load_range_mod (din0.dinfo.sel_range);
}

VALUE_CHANGED(menu, sp_ran_mod_width_lis) {
  float v = MENU.sp_ran_mod_width.value;
  din0.ranges [din0.dinfo.sel_range].mod.fm.depth = v;
  sprintf (BUFFER, "Range %d, Modulation Width = %0.3f", din0.dinfo.sel_range, v);
  cons << BUFFER << eol;
}

VALUE_CHANGED(menu, sp_ran_mod_width_bpm_lis) {
  float v = MENU.sp_ran_mod_width_bpm.value;
  din0.ranges[din0.dinfo.sel_range].mod.fm.bv.set_bpm (v);
  sprintf (BUFFER, "Range %d, Modulation Width BPM = %0.3f", din0.dinfo.sel_range, v);
  cons << BUFFER << eol;
}

VALUE_CHANGED(menu, sp_ran_mod_height_lis) {
  float v = MENU.sp_ran_mod_height.value;
  din0.ranges[din0.dinfo.sel_range].mod.am.depth = v;
  sprintf (BUFFER, "Range %d, Modulation Height = %0.3f", din0.dinfo.sel_range, v);
  cons << BUFFER << eol;
}

VALUE_CHANGED(menu, sp_ran_mod_height_bpm_lis) {
  float v = MENU.sp_ran_mod_height_bpm.value;
  din0.ranges[din0.dinfo.sel_range].mod.am.bv.set_bpm (v);
  sprintf (BUFFER, "Range %d, Modulation Height BPM = %0.3f", din0.dinfo.sel_range, v);
  cons << BUFFER << eol;
}

void range_mod_lis::edited (curve_editor* ed, int i) {
  din0.update_range_mod_solvers (i, ed->mix);
  curve_listener::edited (ed, i);
}

void menu::load_range (int r) {
  sp_range.set_value (r);
  load_range_mod (r);
}

void menu::load_range_mod (int r) {
  range& ri = din0.ranges [r];
  cb_mod_ran.set_state (ri.mod.active, 0);
  sp_ran_mod_width.set_value (ri.mod.fm.depth);
  sp_ran_mod_width_bpm.set_value (ri.mod.fm.bv.bpm);
  sp_ran_mod_height.set_value (ri.mod.am.depth);
  sp_ran_mod_height_bpm.set_value (ri.mod.am.bv.bpm);
  ol_fixed.set_text (ol_fixed_lbls [ri.fixed]);
  print_range_info (ri);
}

void snap_drones_listener::picked (label& l, int dir) {
  din0.dinfo.snap.style += dir;
  static const char* ss [] = {" Free", " Slide", " Lock", " Mirror"};
  wrap<int> (din_info::snap_t::FREE, din0.dinfo.snap.style, din_info::snap_t::MIRROR);
  l.set_text (ss[din0.dinfo.snap.style]);
}

void snap_drones_listener::changed (field& f) {
  float v;
  if (&f == MENUP.sp_snap_left.f_value) {
    v = MENU.sp_snap_left.value;
    float dl = v - din0.dinfo.snap.left;
    din0.dinfo.snap.left = v;
    if (din0.dinfo.snap.style > din_info::snap_t::FREE) {
      if (din0.dinfo.snap.style == din_info::snap_t::SLIDE) {
        din0.dinfo.snap.right += dl;
      }
      else if (din0.dinfo.snap.style == din_info::snap_t::LOCK) {
        din0.dinfo.snap.right = din0.dinfo.snap.left;
      }
      else {
        din0.dinfo.snap.right = 1.0f - din0.dinfo.snap.left;
      }
      MENU.sp_snap_right.set_value (din0.dinfo.snap.right);
    }
  } else if (&f == MENUP.sp_snap_right.f_value) {
    v = MENU.sp_snap_right.value;
    float dr = v - din0.dinfo.snap.right;
    din0.dinfo.snap.right = v;
    if (din0.dinfo.snap.style > din_info::snap_t::FREE) {
      if (din0.dinfo.snap.style == din_info::snap_t::LOCK) {
        din0.dinfo.snap.left = din0.dinfo.snap.right;
      } else if (din0.dinfo.snap.style == din_info::snap_t::SLIDE) {
        din0.dinfo.snap.left += dr;
      } else {
        din0.dinfo.snap.left = 1.0f - din0.dinfo.snap.right; // mirror
      }
      MENU.sp_snap_left.set_value (din0.dinfo.snap.left);
    }
  }

  if (din0.dinfo.snap.left > din0.dinfo.snap.right || din0.dinfo.snap.left < 0.0f || din0.dinfo.snap.right > 1.0f)
    cons << RED;
  else
    cons << GREEN;
  sprintf (BUFFER, "Snap left = %0.3f, Snap right = %0.3f", din0.dinfo.snap.left, din0.dinfo.snap.right);
  cons << BUFFER << eol;
}

void scope_listener::changed (field& f) {
  int n = 0;
  if (&f == MENUP.sp_scope_height.f_value) {
    n = MENU.sp_scope_height.value;
    scope.set_height (n);
    static const string ht = "Height = ";
    cons << YELLOW << ht << n << eol;
  } else if (&f == MENUP.sp_scope_samples.f_value) {
    n = MENU.sp_scope_samples.value;
    n = scope.set_num_samples (n);
    static const string ns = "Samples = ";
    cons << YELLOW << ns << n << eol;
  } else {
    float o = MENU.scopacity.value;
    scope.setopacity (o);
    static const string op = "Opacity = ";
    cons << YELLOW << op << o << eol;
  }
}

void scope_listener::changed (checkbutton& cb) {
  scope.visible = cb.state;
}

void scope_listener::setup () {
  if (scope.visible) MENU.cb_scope.turn_on (0); else MENU.cb_scope.turn_off (0);
  MENU.sp_scope_height.set_value (scope.height);
  MENU.sp_scope_samples.set_value (scope.num_samples);
  MENU.scopacity.set_value (scope.opacity);
}

void tap_bpm_listener::changed (tap_display& td) {
  sprintf (BUFFER, "%.3f", td.bpm);
  MENU.l_tap_bpm_value.set_text (BUFFER);
  extern double TAP_BPM; TAP_BPM = td.bpm;
  Tcl_UpdateLinkedVar (interpreter.interp, "tapbpm");
}

void tap_bpm_listener::changed (checkbutton& cb) {
  checkbutton* cbs [] = {MENUP.cb_am, MENUP.cb_fm, MENUP.cb_gater, MENUP.cb_octave_shift};
  const char* targets [] = {"am", "fm", "gr", "os"};
  for (int i = 0; i < 4; ++i) {
    checkbutton* cbi = cbs[i];
    if (&cb == cbi) {
      if (cbi->state)
        sprintf (BUFFER, "add-tap-target %s", targets[i]);
      else
        sprintf (BUFFER, "remove-tap-target %s", targets[i]);
      interpreter (BUFFER);
      return;
    }
  }
  if (&cb == MENUP.cb_auto_reset) {
    if (MENU.cb_auto_reset.state) interpreter ("set resetbeat 1"); else interpreter ("set resetbeat 0");
  }
}

void pan_zoom_listener::clicked (button& b) {
  if (&b == MENUP.abe_left) {CRVED->do_panx (1); cons << YELLOW << "You can press a to move curves left" << eol;}
  else if (&b == MENUP.abe_right) {CRVED->do_panx (-1); cons << YELLOW << "You can press d to move curves right"<< eol;}
  else if (&b == MENUP.abe_up) {CRVED->do_pany (-1); cons << YELLOW << "You can press w to move curves up" << eol;}
  else if (&b == MENUP.abe_down) {CRVED->do_pany (+1); cons << YELLOW << "You can press s to move curves down" << eol;}
  else if (&b == MENUP.pb_zoom_in) {CRVED->do_zoom (-1); cons << YELLOW << "You can press e to zoom in" << eol;}
  else {CRVED->do_zoom (+1);cons << YELLOW << "You can press q to zoom out" << eol;}
}

void snap_listener::clicked (button& b) {
  static int xs [] = {0, 1, 0, 1};
  static int ys [] = {0, 0, 1, 1};
  static const char* mesgs [] = {
    "You can press n to turn off snapping", "You can press x to snap X",
    "You can press y to snap Y", "You can press b to snap both X and Y"
  };
  button* snaps [] = {MENUP.b_snapnone, MENUP.b_snapx, MENUP.b_snapy, MENUP.b_snapboth};
  for (int i = 0; i < 4; ++i) {
    if (&b == snaps[i]) {
      CRVED->set_snap (xs[i], ys[i]);
      cons << YELLOW << mesgs[i] << eol;
      break;
    }
  }
}

void snap_listener::changed (checkbutton& cb) {
  toggle (CRVED->draww.snaps, basic_editor::drawt::snapss);
}

void menu::undoredoviewlis::changed (checkbutton& cb) {
  int& urw = CRVED->undo_redo_win;
  urw = !urw;
  static const char* urws[] = {"Wont", "Will"};
  cons << YELLOW << urws[urw] << " change view on undo or redo" << eol;
}

void menu::drawcursorlis::changed (checkbutton& cb) {
  ::toggle (CRVED->draww.guide, basic_editor::drawt::guides);
}

void menu::set_mirror_tangents (int i) {
  const static char* mit [] = {" Tangents are not mirrored", " Tangents are mirrored"};
  ol_mirror_tangents.option.set_text (mit[i]);
}

void menu::set_repeat (button** B, int n, double dt) {
  for (int i = 0; i < n; ++i) {
    button* bi = B[i];
    bi->click_repeat = 1;
    bi->first_repeat_time = bi->subsequent_repeat_time = dt;
  }
}

void menu::set_pan_repeat (double dt) {
  button* ab [] = {&abe_left, &abe_right, &abe_up, &abe_down, &abm_left, &abm_right, &abm_up, &abm_down};
  set_repeat (ab, 8, dt);
}

void menu::set_zoom_repeat (double dt) {
  button* zb [] = {&pb_zoom_in, &mb_zoom_out, &bm_zoom_in, &bm_zoom_out};
  set_repeat (zb, 4, dt);
}

void menu::curve_ops_listener::clicked (button& b) {
  int toggle = 1;
  if (&b == MENUP.b_undo) {
    cons << YELLOW << "You can press z to undo!" << eol;
    CRVED->do_undo ();
    toggle = 0;
  } else
  if (&b == MENUP.b_redo) {
    cons << YELLOW << "You can press LSHIFT + z to redo!" << eol;
    CRVED->do_redo ();
    toggle = 0;
  } else
  if (&b == MENUP.abl_left) {
    cons << YELLOW << "You can press 9 to load previous curve from library" << eol;
    CRVED->do_load_curve (-1);
    toggle = 0;
  } else
  if (&b == MENUP.abl_right) {
    cons << YELLOW << "You can press 0 to load next curve from library" << eol;
    CRVED->do_load_curve (+1);
    toggle = 0;
  }
  if (&b == MENUP.b_insert_vertex) {
    CRVED->insert_using_menu ();
  } else if (&b == MENUP.b_delete_vertex) {
    CRVED->remove_using_menu ();
  } else if (&b == MENUP.b_stop_rotating) {
    MENU.sp_curve_rpm.set_value (0);
    CRVED->set_rpm (0);
    cons << "Stopped rotating curve" << eol;
  } else if (&b == MENUP.ed_flip_rotation) {
    int& dir = CRVED->curveinfo[CRVED->curcrv].dir;
    dir *= -1;
    static const char* cac [] = {"clockwise", "", "anti-clockwise"};
    cons << "Curve rotation is " << cac[dir+1] << eol;
  } else if (&b == MENUP.ed_autoflip_reset) {
    CRVED->curveinfo[CRVED->curcrv].totang = 0.0f;
    cons << "Autoflip reset on curve rotation" << eol;
  } else if (&b == MENUP.b_fold_tangents) {
    CRVED->fold_tangents_using_menu ();
  } else if (&b == MENUP.ol_mirror.option) {
    CRVED->mirror_using_menu ();
  } else if (&b == MENUP.b_copy) {
    CRVED->copy_using_menu ();
  } else if (&b == MENUP.ol_paste.option) {
    CRVED->paste_using_menu ();
  } else if (&b == MENUP.b_swap_curves) {
    CRVED->swap ();
  } else if (&b == MENUP.b_pick_curve) {
    CRVED->do_pick_curve ();
  }
  // to library
  else if (&b == MENUP.b_add_curve) {
    CRVED->add_curve ();
  } else if (&b == MENUP.b_replace_curve) {
    CRVED->replace_curve ();
  } else if (&b == MENUP.b_delete_curve) {
    CRVED->delete_curve ();
  } else if (&b == MENUP.b_draw_replacement_curve) {
    CRVED->draw_replacement_curve_using_menu ();
  } else if (&b == MENUP.b_start_capture) {
    CRVED->start_mouse_capture_from_menu ();
  } else if (&b == MENUP.b_assign_capture) {
    CRVED->assign_mouse_capture_from_menu ();
  } else if (&b == MENUP.size_tangent) {
    CRVED->try_sizing_tangent ();
  } else if (&b == MENUP.pinunpin) {
    CRVED->pinunpin_using_menu ();
  }
  if (toggle) TOGGLEMENU
}

void menu::curve_ops_listener::moused (int dir, double scl) {
  CRVED->size_tangent (dir * scl);
}

void menu::curve_ops_listener::changed (checkbutton& cb) {
  int tog = 0;
  int cbstate = cb.state;
  if (&cb == MENUP.cb_ed_autoflip) {
    curve_info& ci = CRVED->curveinfo[CRVED->curcrv];
    ci.autoflip = cbstate;
    ci.totang = 0.0f;
  } else if (&cb == MENUP.sp_curve_every.variance.cb) {
    curve_info& ci = CRVED->curveinfo[CRVED->curcrv];
    ci.randoflip = cbstate;
  } else if (&cb == MENUP.cb_mark_segments) {
    CRVED->marksegments = cbstate;
  } else if (&cb == MENUP.cb_label_vertices) {
    CRVED->label_vertices = cbstate;
  } else if (&cb == MENUP.cb_show_waveform_samples) {
    CRVED->toggle_waveform_samples_display ();
  } else if (&cb == MENUP.cb_draw_curve) {
    CRVED->drawcurve = cbstate;
  } else if (&cb == MENUP.draweditables) {
    CRVED->draweditables = cbstate;
  } else {
    CRVED->overlay = cbstate;
    string n (get_current_instrument()->name);
    if (cbstate)
      cons << GREEN << "Overlaid the " << n << eol;  
    else
      cons << RED << "Removed " << n << " from overlay." << eol;
    tog = 1;
  }
  if (tog) TOGGLEMENU
}

void menu::curve_ops_listener::changed (field& f) {
  if (&f == MENUP.sp_curve_rpm.f_value) {
    CRVED->set_rpm (MENU.sp_curve_rpm.value);
  } else if (&f == MENUP.sp_curve_every.f_value) {
    curve_info& ci = CRVED->curveinfo[CRVED->curcrv];
    ci.every0 = MENU.sp_curve_every.value;
    ci.every = ci.every0.deg;
    static const char* ae = "Autoflip every ", *deg = DEGREES;
    cons << ae << ci.every0.deg << deg << eol;
  } else if (&f == MENUP.sp_curve_every.variance.fld) {
    curve_info& ci = CRVED->curveinfo[CRVED->curcrv];
    ci.rd = MENU.sp_curve_every.variance.rd;
  } else if (&f == MENUP.crvwav.hz.f_value) {
    CRVED->set_hz (MENU.crvwav.hz.value);
  } else if (&f == MENUP.crvwav.periods.f_value) {
    CRVED->set_periods (MENU.crvwav.periods.value);
  } else if (&f == MENUP.crvwav.time.f_value) {
    curve_samples::nsec = MENU.crvwav.time.value;
  } else if (&f == MENUP.sp_curve_limit.f_value) {
    CRVED->set_limit (MENU.sp_curve_limit.value);
  } else {
    if (f.text == "") f.set_text ("nameless");
    CRVED->set_picked_curve_name (f.text);
    vector<widget*>& tc = MENU.tab_members [MENUP.cb_ed_curves];
    widget* wcc = tc [1 + CRVED->curcrv]; // 0 = separator
    checkbutton* cb = dynamic_cast<checkbutton*>(wcc);
    cb->set_text (f.text);
  }
}

void menu::curve_ops_listener::after_slide () {
  CRVED->quick_nothing ();
}

void recording_listener::typing (field& f) {
  string fname (recorder0.folder + f.text);
  string cmd ("file exists " + fname);
  interpreter (cmd); int result; stringstream ss; ss << interpreter.result; ss >> result;
  if (result) MENU.b_save.set_text ("Overwrite"); else MENU.b_save.set_text ("Save");
  recorder0.fname = f.text;
}

void recording_listener::changed (checkbutton& cb) {
  int state = cb.state;
  if (state == 0) { // show recording save section of file menu
    MENU.changed (MENU.cb_file);
    if (MENU.show == 0) TOGGLEMENU
  } else { // close file menu
    if (MENU.show == 1) TOGGLEMENU
  }
  justset (uis.cb_record, state);
  justset (MENU.cb_record, state);
}

void mondrian_listener::handle_split (int& var, int dir, float t) {
  switch (var) {
    case 0: // into 2 boxes
      mondrian0.split_rect (dir, t);
      break;
    case 1: // into notes
      mondrian0.multi_split_rect (dir);
      break;
    case 2: // into n x n grid
      mondrian0.multi_split_rect (mondrian0.num_boxes, dir);
  }
}

void mondrian_listener::clicked (button& b) {
  int toggle = 1;
  if (&b == MENUP.b_split_horizontal) handle_split (hsplit, split::HORIZONTAL, mondrian0.win.mousey);
  else if (&b == MENUP.b_split_vertical) handle_split (vsplit, split::VERTICAL, mondrian0.win.mousex);
  else if (&b == MENUP.b_add_balls) mondrian0.do_add_balls (mondrian0.added_ball_type);
  else if (&b == MENUP.b_add_remove_slits) mondrian0.start_slitting ();
  else if (&b == MENUP.b_modulate_balls_up) {if (!mondrian0.modulate_balls (+1)) cons << RED << "Please select some balls!" << eol;}
  else if (&b == MENUP.b_modulate_balls_down) {if (!mondrian0.modulate_balls (-1)) cons << RED << "Please select some balls!" << eol;}
  else if (&b == MENUP.b_select_all_targets) {mondrian0.select_all_targets ();toggle=0;}
  else if (&b == MENUP.b_invert_selected_targets) {mondrian0.invert_selected_targets ();toggle=0;}
  else if (&b == MENUP.b_select_targets_in_box) {mondrian0.select_box_targets ();toggle=0;}
  else if (&b == MENUP.b_delete_box) mondrian0.delete_current_rect ();
  else if (&b == MENUP.b_delete_all_boxes) mondrian0.delete_all_rects = 1;
  else if (&b == MENUP.b_freeze_balls) mondrian0.freeze_balls (mondrian0.get_balls());
  else if (&b == MENUP.b_thaw_balls) mondrian0.thaw_balls (mondrian0.get_balls());
  else if (&b == MENUP.b_delete_all_targets) mondrian0.delete_all_targets ();
  else if (&b == MENUP.b_delete_selected_targets) mondrian0.delete_selected_targets ();
  else if (&b == MENUP.b_move_selected_balls) mondrian0.do_move_balls ();
  else if (&b == MENUP.b_toggle_wreckers) mondrian0.toggle_balls_type (ball::WRECKER);
  else if (&b == MENUP.b_toggle_healers) mondrian0.toggle_balls_type (ball::HEALER);
  else if (&b == MENUP.b_toggle_bouncers) mondrian0.toggle_balls_type (ball::BOUNCER);
  else if (&b == MENUP.b_switch_ball_type) mondrian0.switch_balls_type ();
  else if (&b == MENUP.b_select_wreckers) mondrian0.select_type (ball::WRECKER);
  else if (&b == MENUP.b_select_healers) mondrian0.select_type (ball::HEALER);
  else if (&b == MENUP.b_remove_slits_on_edge) mondrian0.remove_slits_on_current_edge ();
  else if (&b == MENUP.b_toggle_slit_anim) mondrian0.toggle_slit_anim ();
  else if (&b == MENUP.b_clear_modulations) mondrian0.clear_modulations (mondrian0.get_balls());
  else if (&b == MENUP.b_auto_change_direction_clockwise) {mondrian0.set_auto_rotate (-1);}
  else if (&b == MENUP.b_auto_change_direction_anti_clockwise) {mondrian0.set_auto_rotate (1);}
  else if (&b == MENUP.b_stop_auto_changing_direction) {mondrian0.set_auto_rotate (0);}
  else if (&b == MENUP.b_flip_direction) {mondrian0.flip_velocity();}
  else if (&b == MENUP.b_make_random_color) mondrian0.randomise_box_color();
  else if (&b == MENUP.b_make_note_grid) mondrian0.make_note_grid ();
  else if (&b == MENUP.b_make_nxn_grid) mondrian0.make_nxn_grid ();
  else if (&b == MENUP.b_ball_trig) mondrian0.toggle_triggered_sound ();
  else if (&b == MENUP.abm_left) {mondrian0.do_panx (1); toggle=0;}
  else if (&b == MENUP.abm_right) {mondrian0.do_panx (-1); toggle=0;}
  else if (&b == MENUP.abm_up) {mondrian0.do_pany (+1);toggle=0;}
  else if (&b == MENUP.abm_down) {mondrian0.do_pany (-1); toggle=0;}
  else if (&b == MENUP.bm_zoom_in) {mondrian0.do_zoom(-1); toggle=0;}
  else if (&b == MENUP.bm_zoom_out) {mondrian0.do_zoom(+1); toggle=0;}
  if (toggle) TOGGLEMENU
}

void mondrian_listener::changed (checkbutton& cb) {
  if (&cb == MENUP.cb_auto_split_box) {
    mondrian0.auto_split_rect.active = cb.state;
    if (cb.state) mondrian0.auto_split_rect.start ();
  } else if (&cb == MENUP.cb_auto_delete_box) {
    mondrian0.auto_del_rect.active = cb.state;
    if (cb.state) mondrian0.auto_del_rect.start ();
  } else if (&cb == MENUP.cb_draw_boxes) {
    mondrian0.draw__boxes = cb.state;
  } else if (&cb == MENUP.cb_fill_boxes) {
    mondrian0.fill_boxes = cb.state;
  } else if (&cb == MENUP.cb_draw_notes) {
    mondrian0.draw__notes = cb.state;
  } else if (&cb == MENUP.cb_label_notes) {
    mondrian0.label_notes = cb.state;
  } else if (&cb == MENUP.cb_label_hz_vol) {
    mondrian0.label_hz_vol = cb.state;
  } else if (&cb == MENUP.cb_draw_ball_position) {
    mondrian0.draw_ball.position = cb.state;
  } else if (&cb == MENUP.cb_draw_ball_heading) {
    mondrian0.draw_ball.heading = cb.state;
  } else if (&cb == MENUP.cb_draw_ball_trails) {
    mondrian0.draw_ball.trails = cb.state;
  } else if (&cb == MENUP.cb_mondrian_auto_adjust_voices) {
    mondrian0.auto_adjust_voices = cb.state;
  }
}

void ball_speed_listener::changed (field& f) {
  mondrian0.change_speed (MENU.sp_mondrian_change_speed, MENU.sp_mondrian_change_speed.dir_delta ());
}

void ball_direction_listener::changed (field& f) {
  mondrian0.rotate_velocity (MENU.sp_mondrian_change_dir.dir);
}

void ball_volume_listener::changed (field& f) {
  mondrian0.change_ball_vol_mult (MENU.sp_mondrian_change_vol);
}

void trail_length_listener:: changed (field& f) {
  mondrian0.change_trail_size (MENU.sp_mondrian_change_trail_size);
}

void ball_attack_time_listener:: changed (field& f) {
  mondrian0.change_attack_time_kb (MENU.sp_mondrian_change_attack_time);
}

void ball_decay_time_listener:: changed (field& f) {
  mondrian0.change_decay_time_kb (MENU.sp_mondrian_change_decay_time);
}

void slit_size_listener:: changed (field& f) {
  mondrian0.change_slit_size (MENU.sp_mondrian_change_slit_size);
}

void slit_anim_time_listener:: changed (field& f) {
  mondrian0.change_slit_anim_time (MENU.sp_mondrian_change_slit_anim_time);
}

void note_poly_radius_listener::changed (field& f) {
  mondrian0.set_note_poly_radius (MENU.sp_mondrian_change_note_poly_radius.value);
}

void note_poly_points_listener::changed (field& f) {
  mondrian0.set_note_poly_points (MENU.sp_mondrian_change_note_poly_points.value);
}

void mondrian_listener::changed (field& f) {

  if (&f == MENUP.sp_mondrian_change_speed.f_delta) {
    float ds = MENU.sp_mondrian_change_speed.value;
    mondrian0.delta_speed = ds;
  } else if (&f == MENUP.sp_mondrian_min_voices.f_value) {
    int v = MENU.sp_mondrian_min_voices.value;
    mondrian0.min_voices = v;
    uis.update_bottom_line ();
    cons << YELLOW << "Min voices = " << mondrian0.min_voices << eol;
  } else if (&f == MENUP.sp_mondrian_num_boxes.f_value) {
    mondrian0.num_boxes = MENU.sp_mondrian_num_boxes.value;
    cons << YELLOW << "Number of boxes = " << mondrian0.num_boxes << eol;
  } else if (&f == MENUP.sp_auto_split_time.f_value) {
    float t = MENU.sp_auto_split_time.value;
    mondrian0.auto_split_rect.triggert = t;
    cons << YELLOW << "Split box every = " << t << " secs" << eol;
  } else if (&f == MENUP.sp_auto_delete_time.f_value) {
    float t = MENU.sp_auto_delete_time.value;
    mondrian0.auto_del_rect.triggert = t;
    cons << YELLOW << "Delete box every = " << t << " secs" << eol;
  } else {
    int mss = MENU.sp_min_split_size.value;
    mondrian::min_split_size = mss;
    cons << YELLOW << "Min split size = " << mss << eol;
  }
}

void mondrian_listener::handle_auto_pick_box (options_list& ol, int dir, int& v) {
  v += dir;
  wrap<int> (rect::EARLIEST, v, rect::BALLED);
  ol.set_text (pick_box_types[v]);
}

void mondrian_listener::picked (label& lbl, int dir) {
  if (&lbl == MENUP.ol_auto_pick_box_split.option) {
    handle_auto_pick_box (MENU.ol_auto_pick_box_split, dir, mondrian0.split_leaf);
  } else if (&lbl == MENUP.ol_auto_pick_box_delete.option) {
    handle_auto_pick_box (MENU.ol_auto_pick_box_delete, dir, mondrian0.delete_leaf);
  } else if (&lbl == MENUP.ol_auto_split_at.option) {
    mondrian0.auto_split_at += dir;
    if (mondrian0.auto_split_at < split::NOTES) mondrian0.auto_split_at = split::ANYWHERE;
    else if (mondrian0.auto_split_at > split::ANYWHERE) mondrian0.auto_split_at = split::NOTES;
    MENU.ol_auto_split_at.set_text (auto_split_at_types [mondrian0.auto_split_at]);
  } else if (&lbl == MENUP.ol_auto_split_orient.option) {
    mondrian0.auto_split_orient += dir;
    if (mondrian0.auto_split_orient < split::HORIZONTAL) mondrian0.auto_split_orient = split::BOTH;
    else if (mondrian0.auto_split_orient > split::BOTH) mondrian0.auto_split_orient = split::HORIZONTAL;
    MENU.ol_auto_split_orient.set_text (auto_split_orient_types [mondrian0.auto_split_orient]);
  } else if (&lbl == MENUP.ol_ball_types.option) {
    mondrian0.added_ball_type += dir;
    if (mondrian0.added_ball_type < ball::BOUNCER)
      mondrian0.added_ball_type = ball::HEALER;
    else if (mondrian0.added_ball_type > ball::HEALER)
      mondrian0.added_ball_type = ball::BOUNCER;
    MENU.ol_ball_types.set_text (ball::types_str[mondrian0.added_ball_type]);
  } else if (&lbl == MENUP.ol_split_types_h.option) {
    hsplit += dir;
    check_split_type (MENU.ol_split_types_h, hsplit);
  } else if (&lbl == MENUP.ol_split_types_v.option) {
    vsplit += dir;
    check_split_type (MENU.ol_split_types_v, vsplit);
  } else if (&lbl == MENUP.ol_selection_targets.option) {
    mondrian0.clear_selected_targets ();
    mondrian0.sel_tar = !mondrian0.sel_tar;
    MENU.ol_selection_targets.set_text (selection_targets[mondrian0.sel_tar]);
    static const char* bb [] = {"Select all balls", "Select balls in box", "Invert selected balls", "Delete all balls", "Delete selected balls"};
    static const char* bs [] = {"Select all slits", "Select slits in box", "Invert selected slits", "Remove all slits", "Remove selected slits"};
    const char** pb [] = {bs, bb};
    button* bt [] = {
      MENUP.b_select_all_targets, MENUP.b_select_targets_in_box,
      MENUP.b_invert_selected_targets, MENUP.b_delete_all_targets,
      MENUP.b_delete_selected_targets
    };
    const char** cb = pb[mondrian0.sel_tar];
    for (int i = 0; i < 5; ++i) bt[i]->set_text (cb[i]);
  }
}

void mondrian_listener::check_split_type (options_list& ol, int& o) {
  if (o < 0) o = MAX_SPLIT_TYPES; else if (o > MAX_SPLIT_TYPES) o = 0;
  ol.set_text (split_types[o]);
}


binaural_drones_listener::binaural_drones_listener () : select_rule (GREATER_THAN_EQUAL), select_what (0) {
  val[EQUAL] = "0";
  val[GREATER_THAN_EQUAL] = val[LESSER_THAN_EQUAL]= "100";
  val[ID] = "1 2 1";
  val[RANGE] = "100 200";
  just = binaural_drone::CENTER;
}

void binaural_drones_listener::changed (field& f) {
  float v = float (f);
  if (&f == MENUP.lf_bd_start_pitch.fld) {
    binaural_drones0.starting_pitch = v;
  } else if (&f == MENUP.lf_master_volume.fld) {
    float ov = binaural_drones0.master_volume * 100.0f;
    float mv = v / 100.0f;
    binaural_drones0.master_volume = mv;
    stringstream cmd;
    cmd << "set-all-binaurals-volume " << mv;
    interpreter (cmd.str());
    sprintf (BUFFER, "Master Volume from %0.2f%% to %0.2f%% : please wait or ESC to abort", ov, v);
    cons << YELLOW << BUFFER << eol;
  } else if (&f == MENUP.lf_vol_fade_time.fld) {
    binaural_drones0.vol_fader.set_duration (v);
  } else if (&f == MENUP.lf_pitch_fade_time.fld) {
    binaural_drones0.pitch_fader.set_duration (v);
  } else if (&f == MENUP.lf_modulation_amount.fld) {
    if (v < 1.0f) {
      v = 1.0f;
      MENU.lf_modulation_amount.fld.set_text (v);
    }
    binaural_drones0.modulation_amount = v;
  } else if (&f == MENUP.lf_bd_spacing.fld) {
    binaural_drones0.spacing = v;
  } else if (&f == MENUP.bdf_value) {
    val[select_rule] = MENU.bdf_value.text;
    MENU.bdl.clicked (MENU.bbd_select2);
  } else if (&f == MENUP.lf_vol.fld) {
    float vp = v / 100.0f;
    sprintf (BUFFER, "set-selected-binaurals-volume %f", vp);
    interpreter (BUFFER);
  } else if (&f == MENUP.lf_l.fld) {
    set_hz (binaural_drone::LEFT, v);
  } else if (&f == MENUP.lf_r.fld) {
    set_hz (binaural_drone::RIGHT, v);
  } else if (&f == MENUP.lf_sep.fld) {
    int j = 0;
    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];
        bi->set_sep (v);
        ++j;
      }
    }
    if (j)
      binaural_drones0.pitch_fader.start ("Separation Hz set");
    else
      cons << RED << "Please select some binaural drone pairs" << eol;
  }
}

void binaural_drones_listener::set_hz (int w, float v) {

  int n = MENU.il_binaural_drones.num_selected ();
  if (n == 0) {
    cons << RED << "Please select some binaural drone pairs" << eol;
    return;
  }

  if (n == 1) {
    int i = MENU.il_binaural_drones.get_first ();
    binaural_drone* bi = binaural_drones0.binaural_drones[i];
    bi->set_hz (w, v); // v is absolute
    binaural_drones0.pitch_fader.start ("Hz set");
  } else {
    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 ohz [] = {bi->l_hz, bi->r_hz};
        bi->set_hz (w, ohz[w] + v); // v is relative
      }
    }
    binaural_drones0.pitch_fader.start ("Hz change");
  }
}

void binaural_drones_listener::picked (label& lbl, int dir) {
  if (&lbl == MENUP.ol_justification.option) {
    int j = binaural_drones0.change_justification (dir);
    MENU.ol_justification.set_text (justs[j]);
  } else if (&lbl == MENUP.ol_key_note.option) {
    int k = binaural_drones0.change_key_note (dir);
    const string kn [] = {"start pitch", "from scale"};
    MENU.ol_key_note.set_text (" Key note is " + kn[k]);
  } else if (&lbl == MENUP.ol_select_what.option) {
    select_what += dir;
    wrap<int> (binaural_drone::LEFT, select_what, binaural_drone::VOLUME);
    const string sc [] = {"L ", "R ", "Separation ", "Volume "};
    MENU.ol_select_what.set_text (sc[select_what]);
    MENU.ol_select_rule.set_pos (MENU.ol_select_what.extents.right, MENU.ol_select_rule.posy);
    MENU.bdf_value.set_pos (MENU.ol_select_rule.extents.right, MENU.bdf_value.posy);
  } else if (&lbl == MENUP.ol_select_rule.option) {
    select_rule += dir;
    wrap<int> (EQUAL, select_rule, ID);
    const string sr [] = {" = ", " >= ", " <= ", " <> ", " id "};
    MENU.ol_select_rule.set_text (sr[select_rule]);
    MENU.bdf_value.set_text (val[select_rule]);
    MENU.bdf_value.set_pos (MENU.ol_select_rule.extents.right, MENU.bdf_value.posy);
  } else if (&lbl == MENUP.ol_just.option) {
    just += dir;
    wrap<int> (binaural_drone::LEFT, just, binaural_drone::CENTER);
    MENU.ol_just.set_text (justs[just]);
    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];
        bi->set_just (just);
      }
    }
  }
}

void binaural_drones_listener::selected (item_list& il, int s) {
  int ns = il.num_selected ();
  if (ns == 1) {
    binaural_drone* bs = binaural_drones0.binaural_drones[il.get_first()];
    bs->sel = 1;
    sprintf (BUFFER, "%0.3f", bs->vol*100.0);
    MENU.lf_vol.set_text (BUFFER);
    sprintf (BUFFER, "%0.3f", bs->l_hz);
    MENU.lf_l.set_text (BUFFER);
    sprintf (BUFFER, "%0.3f", bs->r_hz);
    MENU.lf_r.set_text (BUFFER);
    sprintf (BUFFER, "%0.3f", bs->sep);
    MENU.lf_sep.set_text (BUFFER);
    MENU.lf_l.set_label ("L (Hz) ");
    MENU.lf_r.set_label ("R (Hz) ");
    just = bs->just;
    MENU.ol_just.set_text (justs[just]);
  } else {
    if (ns) {
      MENU.lf_l.set_label ("dL (Hz) ");
      MENU.lf_r.set_label ("dR (Hz) ");
    }
    MENU.lf_vol.fld.set_text (0.0f);
    MENU.lf_l.fld.set_text (0.0f);
    MENU.lf_r.fld.set_text (0.0f);
    MENU.lf_sep.fld.set_text (0.0f);
    just = binaural_drone::CENTER;
    MENU.ol_just.set_text (justs[just]);
    for (int i = 0, n = il.n; i < n; ++i) {
      binaural_drone* bi = binaural_drones0.binaural_drones[i];
      bi->sel = il.items[i].sel;
    }
  }
  cons << GREEN << "Selected " << ns << " binaural drone pairs" << eol;
}

void binaural_drones_listener::changed (checkbutton& cb) {
  binaural_drones0.close_octave = MENU.cb_close_octave.state;
}

void menu::update_binaurals_list () {
  il_binaural_drones.set_pos (cb_file.extents.left, bbd_select_all.extents.bottom);
  calc_bg ();
}

ball_ops_listener::ball_ops_listener () {
  op_id = 0;
}

void ball_ops_listener::picked (label& lbl, int dir) {
  if (&lbl == MENUP.ol_browse_balls.option) {
    mondrian0.browse_ball (dir);
    return;
  } else {
    label* olt [] = {MENUP.ol_bouncer.option, MENUP.ol_wrecker.option, MENUP.ol_healer.option};
    for (int i = 0; i < 3; ++i) {
      if (&lbl == olt[i]) {
        int& t = Transform::rules [i];
        t += dir;
        if (t < ball::BOUNCER) t = ball::INVALID;
        else if (t > ball::INVALID) t = ball::BOUNCER;
        sprintf (BUFFER, "%s becomes %s", ball::types_str[i], ball::types_str[t]);
        olt[i]->set_text (BUFFER);
        return;
      }
    }
  }
}

void ball_ops_listener::clicked (button& b) {
}

void ball_ops_listener::changed (checkbutton& cb) {
  ball* b = mondrian0.get_one_selected_ball ();
  if (b) {
    ball_op* ops [] = {&b->op_turn, &b->op_speed, &b->op_teleport, &b->op_clone, &b->op_transform};
    checkbutton* cbn [] = {MENUP.cb_turn, MENUP.cb_speed, MENUP.cb_teleport, MENUP.cb_clone, MENUP.cb_transform};
    for (int i = 0; i < ball_op::NUM_OPS; ++i) {
      if (&cb == cbn[i]) {
        ball_op* opi = ops[i];
        // if (cb.state) opi->alarm.start (); else opi->alarm.stop ();
        if (cb.state) opi->start (b); else opi->alarm.stop ();

        break;
      }
    }

    /*if (cb.state && (&cb == MENUP.cb_speed)) {
      MENU.sp_max_speed.set_value (b->V);
      b->op_speed.max = b->V;
    }*/


    b->op_clone.clone_can_clone = MENU.cb_clone_can_clone.state;

  } else {
    cons << RED << "Please select a ball!" << eol;
  }
}

void ball_ops_listener::changed (field& f) {
  if (&f == MENUP.sp_max_balls.f_value) {
    int i = MENU.sp_max_balls.value;
    Clone::max_balls = i;
    cons << YELLOW << "Max balls = " << Clone::max_balls << eol;
  } else {
    ball* b = mondrian0.get_one_selected_ball ();
    if (b) {
      if (&f == MENUP.sp_turn_every.f_value) {
        float t = MENU.sp_turn_every.value;
        b->op_turn.alarm.triggert = t;
        sprintf (BUFFER, "Turn every %0.3f seconds", t);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_turn_min.f_value) {
        float minn = MENU.sp_turn_min.value, maxx;
        if (MENU.cb_turn_sync.state) {
          maxx = minn;
          MENU.sp_turn_max.set_value (maxx);
        } else maxx = b->op_turn.rd.max;
        b->op_turn.rd.set (-minn, maxx);
        sprintf (BUFFER, "Turn Clockwise upto %0.3f degrees | Anti-clockwise upto %0.3f degrees", minn, maxx);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_turn_max.f_value) {
        float minn, maxx = MENU.sp_turn_max.value;
        if (MENU.cb_turn_sync.state) {
          MENU.sp_turn_min.set_value (maxx);
          minn = -maxx;
        } else minn = b->op_turn.rd.min;
        b->op_turn.rd.set (minn, maxx);
        sprintf (BUFFER, "Turn Clockwise upto %0.3f degrees | Anti-clockwise upto %0.3f degrees", -minn, maxx);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_speed_min.f_value) {
        float minn = MENU.sp_speed_min.value, maxx;
        if (MENU.cb_speed_sync.state) {
          maxx = minn;
          MENU.sp_speed_max.set_value (maxx);
        } else maxx = b->op_speed.rd.max;
        b->op_speed.rd.set (-minn, maxx);
        sprintf (BUFFER, "Accelerate = %0.3f | Brake = %0.3f", maxx, minn);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_speed_max.f_value) {
        float minn, maxx = MENU.sp_speed_max.value;
        if (MENU.cb_speed_sync.state) {
          MENU.sp_speed_min.set_value (maxx);
          minn = -maxx;
        } else minn = b->op_speed.rd.min;
        b->op_speed.rd.set (minn, maxx);
        sprintf (BUFFER, "Accelerate = %0.3f | Brake = %0.3f", maxx, -minn);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_speed_every.f_value) {
        float m = MENU.sp_speed_every.value;
        b->op_speed.alarm.triggert = m;
        sprintf (BUFFER, "Speed every %0.3f seconds", m);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_max_speed.f_value) {
        float m = MENU.sp_max_speed.value;
        b->op_speed.max = m;
        sprintf (BUFFER, "Max speed = %0.3f", m);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_tel_every.f_value) {
        float s = MENU.sp_tel_every.value;
        b->op_teleport.alarm.triggert = s;
        sprintf (BUFFER, "Teleport every %0.3f seconds", s);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_tel_radius.f_value) {
        float r = MENU.sp_tel_radius.value;
        b->op_teleport.radius = r;
        sprintf (BUFFER, "Max Teleport distance = %0.3f", r);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_clone_every.f_value) {
        float m = MENU.sp_clone_every.value;
        b->op_clone.alarm.triggert = m;
        sprintf (BUFFER, "Clone every %0.3f seconds", m);
        cons << YELLOW << BUFFER << eol;
      } else
      if (&f == MENUP.sp_max_clones.f_value) {
        int i = MENU.sp_max_clones.value;
        b->op_clone.n = b->op_clone.max = i;
        cons << "Max clones = " << i << eol;
      } else
      if (&f == MENUP.sp_clone_offset.f_value) {
        float m = MENU.sp_clone_offset.value;
        b->op_clone.offset = m;
        cons << YELLOW << "Clone offset = " << m << eol;
      } else
      if (&f == MENUP.sp_transform_every.f_value) {
        float m = MENU.sp_transform_every.value;
        b->op_transform.alarm.triggert = m;
        sprintf (BUFFER, "Transform every %0.3f seconds", m);
        cons << YELLOW << BUFFER << eol;
      }
    } else {
      cons << RED << "Please select a ball! " << eol;
    }
  }
}

void arrowlis::changed (field& f) {
  if (&f == MENUP.dronearrow.shoulder.position.f_value) {
    din0.change_drone_arrow (MENU.dronearrow.shoulder.position, 0);
  } else if (&f == MENUP.dronearrow.shoulder.width.f_value) {
    din0.change_drone_arrow (MENU.dronearrow.shoulder.width, 1);
  } else {
    din0.change_drone_arrow (MENU.dronearrow.neck, 2);
  }
}

void defarrowlis::changed (field& f) {
  drone::arrowt::U = MENU.dronearrowdefaults.shoulder.position.value;
  drone::arrowt::V = MENU.dronearrowdefaults.shoulder.width.value;
  drone::arrowt::K = MENU.dronearrowdefaults.neck.value;
  /*cons << GREEN <<
    "Drone arrow defaults: Neck = " << drone::arrowt::K <<
    ", Shoulder pos = " << drone::arrowt::U <<
    ", Shoulder width = " << drone::arrowt::V << eol;*/

  MENU.dronearrowdefaults.arrow.calc ();
}

void defarrowlis::changed (checkbutton& cb) {
  MENU.dronearrowdefaults.arrow.calc ();
}

void drawrrow::calc () {
  int cap = MENU.dronearrowdefaults.cap.state;
  int n = 12;
  make_arrow (pts, 0, cap, n, x, y, ux, uy, vx, vy,
    MENU.dronearrowdefaults.shoulder.position.value,
    MENU.dronearrowdefaults.shoulder.width.value,
    MENU.dronearrowdefaults.neck.value);
  npts = n / 2;
}

void drone::arrowt::reset () {
  u = MENU.dronearrowdefaults.shoulder.position ();
  v = MENU.dronearrowdefaults.shoulder.width ();
  t = MENU.dronearrowdefaults.neck ();
  cap = MENU.dronearrowdefaults.cap.state;
}

void arrowlis::clicked (button& b) {
  if (&b == MENUP.dronearrow.cap)
    din0.capdronearrows (1);
  else
    din0.capdronearrows (0);
  TOGGLEMENU
}



void range_defaults_lis::changed (field& f) {
  if (&f == MENUP.sp_default_width.f_value) {
    extern int WIDTH;
    WIDTH = MENU.sp_default_width.value;
    cons << "Default Range Width = " << WIDTH << eol;
  } else {
    extern int HEIGHT;
    HEIGHT = MENU.sp_default_height.value;
    cons << "Default Range Height = " << HEIGHT << eol;
  }
}

void change_note_listener::moused (int dx, double scl) {
  int dir = sign (dx);
  din0.change_range_note (id, dir * scl * delta0);
}

void change_note_listener::clicked (button& b) {
  if (&b == MENUP.b_change_note_left) {
    mouse_slider0.add (MENUP.cnl);
  } else {
    mouse_slider0.add (MENUP.cnr);
  }
  activate_mouse_slider (0, 0);
}

void change_note_both_listener::clicked (button& b) {
  mouse_slider0.add (MENUP.cnl);
  mouse_slider0.add (MENUP.cnr);
  activate_mouse_slider (0, 0);
}

void change_note_options_listener::picked (label& lbl, int dir) {
  if (&lbl == MENUP.ol_change_note.option) {
    static const string chg (" Change "), nte ("Note > ");
    din0.dinfo.change_note = !din0.dinfo.change_note;
    if (din0.dinfo.change_note)
      MENU.add_to_tab (MENUP.cb_mkb_ranges, MENUP.ol_change_note_style);
    else
      MENU.remove_from_tab (MENUP.cb_mkb_ranges, MENUP.ol_change_note_style);
    string cn (din_info::cnno[din0.dinfo.change_note]);
    lbl.set_text (chg+cn);
    print_range_info (din0.ranges[din0.dinfo.sel_range]);
  } else {
    din0.dinfo.change_note_style = !din0.dinfo.change_note_style;
    set (lbl, din0.dinfo.change_note_style);
  }
}

void change_note_options_listener::set (label& lbl, int v) {
  lbl.set_text (din_info::cnn_opts[v]);
  extern scale_info all_notes;
  scale_info* ptr_si [] = {&din0.scaleinfo, &all_notes};
  din0.ptr_scaleinfo = ptr_si[v];
}

void menu::pitch_vol_dis_pix_lis::changed (checkbutton& cb) {
  if (&cb == MENUP.cb_pitch_dis) {
    din0.dinfo.dist.pitch = cb.state;
  } else {
    din0.dinfo.dist.vol = cb.state;
  }
}

void menu::pitch_vol_dis_pix_lis::changed (field& f) {
  din0.dinfo.dist.pix = MENU.sp_lev_sz.value;
  cons << "Pixels per level = " << din0.dinfo.dist.pix << eol;
}

void prep_drone_xform (mouse_slider_listener* L, int shift = 0, int scalestyle = mouse_slider::scalet::CHANGING) {
  din0.freeze_orbiters ();
  mouse_slider0.add (L);
  activate_mouse_slider (shift, scalestyle);
}

CLICKED_BUTTON (menu, movlis) {
  din0.start_moving_drones ();
  TOGGLEMENU
}

CLICKED_BUTTON (menu, raillis) {
  _SELECTED_DRONES_EXIST_
  mouse_slider0.add (MENUP.raill1);
  activate_mouse_slider ();
}

MOUSED (menu, raillis1) {
  din0.rail (dir, scl);
}

AFTER_SLIDE(menu, raillis1) {}

CLICKED_BUTTON (menu, strafelis) {
  _SELECTED_DRONES_EXIST_
  mouse_slider0.add (MENUP.strafel1);
  activate_mouse_slider ();
}

MOUSED (menu, strafelis1) {
  din0.strafe (dir, scl);
}

AFTER_SLIDE(menu, strafelis1) {}

CLICKED_BUTTON (menu, b_set_xform_center_lis) {
  din0.calc_drones_centroid ();
  TOGGLEMENU
}

CLICKED_BUTTON (menu, b_move_drones_under_gravity_lis) {
  din0.set_drones_under_gravity ();
  TOGGLEMENU
}

CLICKED_BUTTON (menu,b_scale_drones_lis) {
  MENU.ms_sdl.name = "Scale";
  if (din0.prep_scale_drones ()) {
    int shift = -2; // for 0.01f scaling, assuming base = 10 on mouse slider
    prep_drone_xform (MENUP.ms_sdl, shift, mouse_slider::scalet::CHANGING);
  } else cons << RED_PSD << eol;
}

AFTER_SLIDE (menu,ms_scale_drones_lis) {
  din0.thaw_orbiters ();
}

MOUSED (menu,ms_scale_drones_lis) {
  din0.scale_drones (dir * scl);
  din0.scale_drones ();
}

CLICKED_BUTTON (menu,b_rotate_drones_lis) {
  MENU.ms_rdl.name = "Rotate";
  if (din0.prep_rotate_drones ()) prep_drone_xform (MENUP.ms_rdl); else cons << RED << "Please select some drones!" << eol;
}

AFTER_SLIDE (menu,ms_rotate_drones_lis) {
  din0.thaw_orbiters ();
}

MOUSED (menu,ms_rotate_drones_lis) {
  din0.angle += (dir * scl * PI_BY_180);
  din0.rotate_drones ();
}

CLICKED_BUTTON (menu,set_to_mesh_rows_lis) {
  MENU.sp_drones_per_pend.set_value (din0.dinfo.rows);
  MENU.dppl.changed (MENU.sp_drones_per_pend.f_value);
}

CLICKED_BUTTON (menu,set_to_mesh_cols_lis) {
  MENU.sp_drones_per_pend.set_value (din0.dinfo.cols);
  MENU.dppl.changed (MENU.sp_drones_per_pend.f_value);
}

CLICKED_CHECKBUTTON (menu,cb_am_bpm_lis) {
  din0.dinfo.mesh_vars.apply_to.am = cb.state;
  din0.dinfo.mesh_vars.apply_to.calc_active ();
}

CLICKED_CHECKBUTTON (menu,cb_fm_bpm_lis) {
  din0.dinfo.mesh_vars.apply_to.fm = cb.state;
  din0.dinfo.mesh_vars.apply_to.calc_active ();
}

CLICKED_BUTTON(menu,b_set_unset_toggle_lis) {
  if (&b == MENUP.b_toggle) {
    if (din0.dinfo.set_unset_toggle) din0.pos_afx_vel (-1); else din0.snap_drones (-1);
  } else if (&b == MENUP.b_set) {
    if (din0.dinfo.set_unset_toggle) din0.pos_afx_vel (1); else din0.snap_drones (1);
  } else {
    if (din0.dinfo.set_unset_toggle) din0.pos_afx_vel (0); else din0.snap_drones (0);
  }
  TOGGLEMENU
}

VALUE_CHANGED (menu,drones_per_pend_lis) {
  din0.dinfo.mesh_vars.dpp = MENU.sp_drones_per_pend.value;
  cons << "Drones per pendulum = " << din0.dinfo.mesh_vars.dpp << eol;
}

PICKED_OPTION (menu, ol_fixed_lis) {
  range& rs = din0.ranges [din0.dinfo.sel_range];
  rs.fixed += dir;
  wrap<int> (range::LEFT, rs.fixed, range::RIGHT);
  MENU.ol_fixed.set_text (ol_fixed_lbls[rs.fixed]);
}

CLICKED_CHECKBUTTON (menu,cb_draw_mesh_lis) {
  din0.meshh.draw = cb.state;
  TOGGLEMENU
}

VALUE_CHANGED (menu,lf_conn_steps_lis) {
  din0.calc_stepz (f.text);
  cons << YELLOW << "Click connect to connect drones" << eol;
}

void menu::lf_conn_steps_lis::typing (field& f) {
  static const int d = 12;
  MENU.cb_conn_wrap.set_pos (f.extents.right + d, MENU.cb_conn_wrap.posy);
  MENU.trackcon.set_pos (MENU.cb_conn_wrap.extents.right + d, MENU.trackcon.posy);
}

CLICKED_CHECKBUTTON (menu, cb_conn_wrap_lis) {
  cons << YELLOW << "Click connect to connect drones" << eol;
}

CLICKED_BUTTON (menu, b_connect_lis) {
  if (din0.connect_drones ()) TOGGLEMENU
}

CLICKED_BUTTON (menu, b_disconnect_lis) {
  if (din0.disconnect_drones ()) TOGGLEMENU
}

void get_color::update_data () {
  data.clr[0] = color (MENU.s_red_min(), MENU.s_green_min(), MENU.s_blue_min());
  data.clr[1] = color (MENU.s_red_max(), MENU.s_green_max(), MENU.s_blue_max());
}

SLIDER_CHANGED(menu,sc_color_lis) {
  get_color::update_data ();
  din0.color_selected_drones ();
  MENU.b_close.set_pos (mousex, MENU.b_close.posy);
}

START_SLIDE(menu, ss_color_lis, slider<float>) {
  if (MENU.b_close.visible == 0) {
    DECL_COLOR_SLIDERS
    button& b_close = detach_from_menu (slrs, COLOR_SLIDERS_N, MENU.s_red_max.posx, MENU.s_red_max.posy);
    LISTEN (b_close, MENUP.cscll)
  }
}

CLICKED_BUTTON(menu, sc_close_lis) {
  DECL_COLOR_SLIDERS
  attach_to_menu (slrs, COLOR_SLIDERS_N);
}

PICKED_OPTION(menu,ol_color_lis) {
  colorer_t& colorer = MENU.colorer;
  colorer += dir;
  l.set_text (colorer_t::s_schemes[colorer.i]);
  din0.color_selected_drones ();
}

PICKED_OPTION(menu,ol_paste_lis) {
  static const char* types [] = {" Paste", " Paste Append"};
  int& id = MENU.ol_paste.id;
  id = !id;
  MENU.ol_paste.set_text (types[id]);
}

void menu::initdroneparamtabs (checkbutton& cb) {
  MENU.cb_modulation.set_state (0,0);
  MENU.cb_visual.set_state (0,0);
  MENU.cb_motion.set_state (0,0);
  MENU.cb_chuck.set_state (0,0);
  MENU.cb_defaults.set_state (0,0);
  cb.set_state (1, 0);
}

//
// drone params ui
//

CLICKED_CHECKBUTTON (menu,cb_modulation_lis) {
  MENU.initdroneparamtabs (cb);
  MENU.set_drone_params_items (9, 33);
}

CLICKED_CHECKBUTTON (menu,cb_motion_lis) {
  MENU.initdroneparamtabs (cb);
  MENU.set_drone_params_items (33, 82);
}

CLICKED_CHECKBUTTON (menu,cb_defaults_lis) {
  MENU.initdroneparamtabs (cb);
  MENU.set_drone_params_items (82, 127);
}

CLICKED_CHECKBUTTON (menu,cb_chuck_lis) {
  MENU.initdroneparamtabs (cb);
  MENU.set_drone_params_items (127, 135);
}

CLICKED_CHECKBUTTON (menu,cb_visual_lis) {
  MENU.initdroneparamtabs (cb);
  MENU.set_drone_params_items (135, DRONE_PARAMS_N);
}

CLICKED_BUTTON (menu, b_abort_octave_shift_lis) {
  abort_octave_shift (get_current_instrument());
}

CLICKED_BUTTON (menu, b_arrow_reset_lis) {
  din0.reset_drone_arrows ();
}

void menu::handle_voice_tab_items (const char* viv) {

  cb_mkb_voice.set_text (viv);

  widget* wvoice [] = {
    &sp_am_depth,
    &sp_fm_depth,
    &sp_am_bpm,
    &sp_fm_bpm,
    &ol_am_style,
    &ol_fm_style,
  };

  if (din0.dinfo.voice_is_voice) {
    for (int i = 0; i < 6; ++i) add_to_tab (&cb_mkb_voice, wvoice[i]);
  } else {
    for (int i = 0; i < 6; ++i) remove_from_tab (&cb_mkb_voice, wvoice[i]);
  }

  calc_bg ();

}

void print_range_info (range& ri) {
  note& L = ri.notes[0];
  note& R = ri.notes[1];
  sprintf (BUFFER, "Note > %s | Left = %s @ %0.3f Hz, Right = %s @ %0.3f Hz | Range %d", din_info::cnno[din0.dinfo.change_note], L.name.c_str(), L.hz, R.name.c_str(), R.hz, din0.dinfo.sel_range);
  cons << CYAN << BUFFER << eol;
}

button& detach_from_menu (widget** wa, int n, int posx, int posy) {

  button& close = MENU.b_close;
  close.set_pos (posx, posy - line_height);
  close.show ();

  if (MENU.show) MENU.toggle (DONT_MOUSE_WARP);

  vector<widget*>& vw = uis.widgets_of [uis.current];
  vw.insert (vw.begin(), &close);

  if (n == 1)
    vw.insert (vw.begin(), wa[0]);
  else
    for (int i = 0; i < n; ++i) vw.insert (vw.begin(), wa[i]);

  warp_mouse (MENU.menu_mousex, MENU.menu_mousey);

  return close;

}

void attach_to_menu (widget** wa, int n) {
  uis.remove (MENUP.b_close);
  MENU.b_close.hide ();
  if (n == 1) uis.remove (wa[0]); else for (int i = 0; i < n; ++i) uis.remove (wa[i]);
  if (uis.current == &din0) uis.add (&din0, &mkb_selector);
}

CLICKED_BUTTON (menu, b_mute_lis) {
  din0.gab.set (&din0, 0.0f, "muting drones");
  TOGGLEMENU
}

CLICKED_BUTTON (menu, b_unmute_lis) {
  din0.gab.set (&din0, 1.0f, "unmuting drones");
  TOGGLEMENU
}

VALUE_CHANGED (menu, sp_drone_vol_lis) {
  din0.setdronevol (MENU.sp_drone_vol);
}

CLICKED_BUTTON (menu, drone2noiselis) {
  din0.drone2noise ();
  TOGGLEMENU
}

CLICKED_BUTTON (menu, noise2dronelis) {
  din0.noise2drone ();
  TOGGLEMENU
}

void menu::setvelaccel::setup () {
  lbl.set_text ("Set");
  whats.option.set_text (" Velocity");
  whats.set_listener (this);
  button* b[] = {&zero, &vert, &hor, &vel, &accel, &zero};
  const char* l[] = {"0", "Vertical", "Horizontal", "Velocity", "Acceleration"};
  for (int i = 0; i < 5; ++i) {
    button& bi = *b[i];
    bi.id = i;
#ifndef __WIDGET_MOVE__
    bi.set_listener (this);
#endif
    bi.set_text (l[i]);
  }
  neg.set_text ("-ve");
  perp.set_size (line_height / 2);
#ifdef __WIDGET_MOVE__
  widget* w[] = {&lbl, &whats, &neg, &perp, &zero, &vert, &hor, &vel, &accel};
  makehier (w, 9);
  for (int i = 0; i < 9; ++i) w[i]->set_moveable(1);
#endif
}

void menu::setvelaccel::clicked (button& b) {
  din0.setvelaccel (what, b.id, neg.state, perp.state);
  TOGGLEMENU
}

void menu::setvelaccel::picked (label& l, int dir) {
  static const char* lbl [] = {" Velocity", " Acceleration", " Both"};
  what += dir;
  wrap<int> (menu::autorott::VELOCITY, what, menu::autorott::BOTH);
  l.set_text (lbl[what]);
}


void menu::autorott::clicked (button& b) {
  if (&b == &smooth) {
    mov.id = autorotator::SMOOTH;
    din0.setautorotparam (MENU.autorotate.which, autorott::MOVEMENT);
  } else {
    mov.id = autorotator::TICK;
    din0.setautorotparam (MENU.autorotate.which, autorott::MOVEMENT);
  }
  TOGGLEMENU
}

void menu::autorott::picked (label& lbl, int dir) {
  static const char* strs [] = {" Velocity", " Acceleration", " Both"};
  which += dir;
  wrap<int> (menu::autorott::VELOCITY, which, menu::autorott::BOTH);
  lbl.set_text (strs[which]);
}

void menu::defvelaccelui::checksync (float v, anglet& t, spinner<float>& sp) {
  if (sync.state) {
    t = v;
    sp.set_value (v);
  }
}


void menu::autorott::setup () {

  title.set_text ("Auto rotate");
  which = VELOCITY;
  whichl.option.set_text (" Velocity");
  mov.set_text ("Movement?");
  whichl.set_listener (this);
  {
    button* b[] = {&start, &stop, &toggle, &clockwise, &anticlockwise, &smooth, &tick};
    static const char* l [] = {"Start", "Stop", "Toggle", "Clockwise", "Anti-clockwise", "Smooth", "Ticked"};
    click_listener* clk [] = {&startl, &stopl, &togl, &clkl, &aclkl, this, this};
    for (int i = 0; i < 7; ++i) {
      button& bi = *b[i];
      bi.set_text (l[i]);
#ifndef __WIDGET_MOVE__
      bi.set_listener (clk[i]);
#endif
    }
  }
  {
    widget* af [] = {&autoflip.lbl, &autoflip.unset, &autoflip.set, &autoflip.toggle, &autoflip.angle, 0};
    widget* ap [] = {&autopause.lbl, &autopause.unset, &autopause.set, &autopause.toggle, &autopause.every, &autopause.f0r, 0};
    widget* rfp [] = {&rndflipause.whichl.option, &rndflipause.unset, &rndflipause.set, &rndflipause.toggle, 0};
    static const char* l [] = {"Unset", "Set", "Toggle"};
    for (int i = 0, j = 1; i < 3; ++i, ++j) {
      button& bf = (button&) *af[j];
      button& bp = (button&) *ap[j];
      button& brfp = (button&) *rfp[j];
      bf.set_text (l[i]);
      bp.set_text (l[i]);
      brfp.set_text (l[i]);
#ifndef __WIDGET_MOVE__
      bf.id = bp.id = brfp.id = i;
      bf.set_listener (&autoflip.sutl);
      bp.set_listener (&autopause.sutl);
      brfp.set_listener (&rndflipause.sutl);
#endif
    }
#ifdef __WIDGET_MOVE__
    makehier (af);
    makehier (ap);
    makehier (rfp);
#endif
    rndflipause.optl.picked (rndflipause.whichl.option, 0);
    rndflipause.whichl.set_listener (&rndflipause.optl);
  }
 
  rpm.set ("RPM", 1.0f, 0.0f, MILLIONF, &rpml);
  deg.set ("Degrees per Tick", 1.0f, -MILLIONF, MILLIONF, &degl);
  tps.set ("Ticks per Second", 1.0f, -MILLIONF, MILLIONF, &tpsl);
  autoflip.lbl.set_text ("Auto flip");
  autoflip.angle.set (1.0f, 0.0f, MILLIONF, &autoflip.angl);
  autoflip.angle.set_text ("Every", DEGREES);
  autopause.lbl.set_text ("Auto pause");
  autopause.every.set (0.1f, 0.0f, MILLIONF, &autopause.everyl);
  autopause.f0r.set (0.1f, 0.0f, MILLIONF, &autopause.f0rl);
  autopause.every.set_text ("Every", SECONDS);
  autopause.f0r.set_text ("For", SECONDS);

#ifdef __WIDGET_MOVE__
  widget* w[] = {
    &title, &whichl,
    &start, &stop, &toggle,
    &mov,
    &smooth, &tick, &clockwise, &anticlockwise,  
    &rpm, &deg, &tps,
    0
  };
  makehier (w);
#endif
}

void menu::defvelaccelui::setup () {

  whichl.set_listener (this);

  spinner2<float>* sp [] = {
    &mag,
    &autorotate.rpm,
    &autorotate.dps,
    &autorotate.tps,
    &autoflip.deg,
    &autopause.every,
    &autopause.f0r,
  };

  const char* nam [] = {
    "Magnitude",
    "RPM",
    "Degrees per Tick",
    "Ticks Per Second",
    "Every", // autoflip (degrees)
    "Every", // autopause (seconds)
    "For",
  };

  change_listener<field>* chgl [] = {
    &magl,
    &rpml,
    &dpsl,
    &tpsl,
    &degl,
    &everyl,
    &f0rl
  };

  float mins [] = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f};
  float dels [] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.01f, 0.01f};

  int nsp = 7;
  for (int i = 0; i < nsp; ++i) {
    spinner2<float>& spi = *sp[i];
    spi.set (nam[i], dels[i], mins[i], MILLIONF, chgl[i]);
    spi.lis[2] = &varl;
    spi.variance.cb.id = i;
    spi.variance.fld.id = i;
    spi.variance.cb.set_listener (&chkl);
  }

  clockwise.set (1.0f, 0.0f, MILLIONF, &clockl);
  anticlockwise.set (1.0f, 0.0f, MILLIONF, &aclockl);
  clockwise.set_text ("Clockwise", DEGREES);
  anticlockwise.set_text ("Anti-clockwise", DEGREES);

  autoflip.deg.set_text ("Every", DEGREES);

  autopause.every.set_text ("Every", SECONDS);
  autopause.f0r.set_text ("For", SECONDS);
  autopause.tar.set_listener (&tarl);

  ldir.set_text ("Direction");
  odir.set_listener (&dirl);

  checkbutton* cb [] = {
    &neg,
    &randomize,
    &autorotate.cb,
    &autorotate.uet.deg,
    &autorotate.uet.tps,
    &autoflip.cb,
    &autopause.cb,
  };

  for (int i = 0, j = 7; i < 7; ++i, ++j) {
    checkbutton& cbi = *cb[i];
    cbi.id = j;
    cbi.set_listener (&chkl);
  }

  autorotate.setup ();

#ifdef __WIDGET_MOVE__
  autoflip.setup ();
  autopause.setup ();
  widget* w[] = {
    &whichl,
    &mag,
    &ldir, &neg, &odir,
    &randomize, &clockwise, &anticlockwise, &sync,
    0
  };
  makehier (w);
#endif

}

void menu::defvelaccelui::load () {

  defvelaccel* dval [] = {&drone::v0, &drone::a0};
  cur = dval [which];
  whichl.set_text (cur->name);

  neg.set_state (cur->neg, 0);
  static const char* s [] = {" Horizontal", " Vertical", " Mouse"};
  odir.set_text (s[cur->dir]);
  MENU.dva.idir = cur->dir;

  randomize.set_state (cur->rndrot, 0);
  clockwise.set_value (cur->clock.deg);
  anticlockwise.set_value (cur->anticlock.deg);
  sync.set_state (cur->sync, 0);

  spinner2<float>* sp [] = {
    MENUP.dva.mag,
    MENUP.dva.autorotate.rpm,
    MENUP.dva.autorotate.dps,
    MENUP.dva.autorotate.tps,
    MENUP.dva.autoflip.deg,
    MENUP.dva.autopause.every,
    MENUP.dva.autopause.f0r,
  };

  valt* vt [] = {
    &cur->mag,
    &cur->autos.rot.rpm,
    &cur->autos.rot.dps,
    &cur->autos.rot.tps,
    &cur->autos.flip.deg,
    &cur->autos.pause.every,
    &cur->autos.pause.f0r,
  };

  for (int i = 0; i < 7; ++i) {
    spinner2<float>& spi = *sp[i];
    valt& vti = *vt[i];
    spi.set_value (vti.val);
    spi.variance.rd = vti.rd;
    spi.variance.setfld ();
    spi.variance.cb.set_state (vti.rndrd, 0);
  }

  autorotate.load ();

  autoflip.cb.set_state (cur->autos.flip.yes);

  autopause.cb.set_state (cur->autos.pause.yes);
  autopause.tar.set_text (autopauset::tars[cur->autos.pause.tar]);

}

void menu::defvelaccelui::picked (label& lbl, int p) {
  which = !which;
  load ();
}

VALUE_CHANGED (menu::defvelaccelui, varlis) {
  spinner2<float>* sp [] = {
    MENUP.dva.mag,
    MENUP.dva.autorotate.rpm,
    MENUP.dva.autorotate.dps,
    MENUP.dva.autorotate.tps,
    MENUP.dva.autoflip.deg,
    MENUP.dva.autopause.every,
    MENUP.dva.autopause.f0r,
  };

  defvelaccel& dva = *MENU.dva.cur;
  valt* vt [] = {
    &dva.mag,
    &dva.autos.rot.rpm,
    &dva.autos.rot.dps,
    &dva.autos.rot.tps,
    &dva.autos.flip.deg,
    &dva.autos.pause.every,
    &dva.autos.pause.f0r,
  };

  int id = f.id;
  vt[id]->rd = sp[id]->variance.rd;
}

defvelaccel& getdva () {
  defvelaccel* dval [] = {&drone::v0, &drone::a0};
  return *dval [MENU.dva.which];
}

VALUE_CHANGED (menu::defvelaccelui, maglis) {
  float v = MENU.dva.mag.value;
  MENU.dva.cur->mag.val = v;
  cons << "Magnitude of " << MENU.dva.cur->name << " = " << v << eol;
}

VALUE_CHANGED (menu::defvelaccelui, rpmlis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autorotate.rpm.value;
  dva.autos.rot.rpm = v;
  cons << dva.name << " Auto rotate RPM = " << v << eol;
}

VALUE_CHANGED (menu::defvelaccelui, dpslis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autorotate.dps.value;
  dva.autos.rot.dps = v;
  cons << dva.name << " Degrees per Tick = " << v << eol;
}
VALUE_CHANGED (menu::defvelaccelui, tpslis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autorotate.tps.value;
  dva.autos.rot.tps = v;
  cons << dva.name << " Ticks per Second = " << v << eol;
}

VALUE_CHANGED (menu::defvelaccelui, deglis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autoflip.deg.value;
  dva.autos.flip.deg = v;
  cons << dva.name << " Auto flip every " << v << DEGREES << eol;
}

VALUE_CHANGED (menu::defvelaccelui, everylis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autopause.every.value;
  dva.autos.pause.every = v;
  cons << dva.name << " Auto pause every " << v << SECONDS << eol;
}

VALUE_CHANGED (menu::defvelaccelui, f0rlis) {
  defvelaccel& dva = getdva ();
  float v = MENU.dva.autopause.f0r.value;
  dva.autos.pause.f0r = v;
  cons << dva.name << " Auto pause for " << v << SECONDS << eol;
}

VALUE_CHANGED (menu::defvelaccelui, clocklis) {
  float v = MENU.dva.clockwise.value;
  MENU.dva.cur->clock = v;
  MENU.dva.checksync (v, MENU.dva.cur->anticlock, MENU.dva.anticlockwise);
  MENU.dva.cur->setrotrd ();
  cons << MENU.dva.cur->name << " Clockwise Rotation = " << v << DEGREES << eol;
}

VALUE_CHANGED (menu::defvelaccelui, anticlocklis) {
  float v = MENU.dva.anticlockwise.value;
  MENU.dva.cur->anticlock = v;
  MENU.dva.checksync (v, MENU.dva.cur->clock, MENU.dva.clockwise);
  MENU.dva.cur->setrotrd ();
  cons << MENU.dva.cur->name << " Anti-clockwise Rotation = " << v << DEGREES << eol;
}

PICKED_OPTION (menu::defvelaccelui, tarlis) {
  defvelaccel& dva = getdva ();
  int& tar = dva.autos.pause.tar;
  tar = !tar;
  l.set_text (menu::defvelaccelui::autopauset::tars[tar]);
}

PICKED_OPTION (menu::defvelaccelui, dirlis) {
  int& idir = MENU.dva.idir;
  idir += dir;
  wrap<int> (menu::defvelaccelui::HORIZONTAL, idir, menu::defvelaccelui::MOUSE);
  MENU.dva.cur->dir = idir;
  static const char* s [] = {" Horizontal", " Vertical", " Mouse"};
  l.set_text (s[idir]);
}

int gethandlesize () { return MENU.handlesize(); }

int gettrailsize () {return MENU.trailsize(); }

void initlaunch (drone* pd) {
  drone& d = *pd;
  d.lpm = MENU.ddpm ();
  d.launch_every.triggert = ppm2t (d.lpm);
  d.dpl = MENU.ddpl ();
}

void get (float& g, float& ux, float& uy, defvelaccel& dva) {
  g = dva.mag ();
  point<int> mous (din0.delta_mousex, din0.delta_mousey);
  if (mous.x == 0 && mous.y == 0) {
    ux = 0;
    uy = 1;
  } else {
    unit_vector<float> (ux, uy, mous.x, -mous.y);
  }
  float dirxa [] = {1, 0, ux};
  float dirya [] = {0, 1, uy};
  float nega [] = {1, -1};
  float neg = nega [dva.neg];
  ux = neg * dirxa [dva.dir];
  uy = neg * dirya [dva.dir];
  if (dva.rndrot) rotate_vector (ux, uy, dva.rotrd());
}


CLICKED_CHECKBUTTON (menu::defvelaccelui, chklis) {
  defvelaccel& dva = *MENU.dva.cur;
  int* val [] = {
    &dva.mag.rndrd,
    &dva.autos.rot.rpm.rndrd,
    &dva.autos.rot.dps.rndrd,
    &dva.autos.rot.tps.rndrd,
    &dva.autos.flip.deg.rndrd,
    &dva.autos.pause.every.rndrd,
    &dva.autos.pause.f0r.rndrd,
    &dva.neg,
    &dva.rndrot,
    &dva.autos.rot.yes,
    &dva.autos.rot.uet.deg,
    &dva.autos.rot.uet.tps,
    &dva.autos.flip.yes,
    &dva.autos.pause.yes,
  };
  *val[cb.id] = cb.state;
}

CLICKED_BUTTON (menu::autorott, startlis) {
  din0.setautorot (MENU.autorotate.which, 1);
}

CLICKED_BUTTON (menu::autorott, stoplis) {
  din0.setautorot (MENU.autorotate.which, 0);
}

CLICKED_BUTTON (menu::autorott, togglis) {
  din0.setautorot (MENU.autorotate.which, 0, 1);
}

CLICKED_BUTTON (menu::autorott, clockwiselis) {
  din0.setautorotdir (MENU.autorotate.which, -1);
}

CLICKED_BUTTON (menu::autorott, anticlockwiselis) {
  din0.setautorotdir (MENU.autorotate.which, 1);
}

VALUE_CHANGED (menu::autorott, rpmlis) {
  din0.setautorotparam (MENU.autorotate.which, menu::autorott::RPM);
}

VALUE_CHANGED (menu::autorott, deglis) {
  din0.setautorotparam (MENU.autorotate.which, menu::autorott::DEG);
}

VALUE_CHANGED (menu::autorott, tpslis) {
  din0.setautorotparam (MENU.autorotate.which, menu::autorott::TPS);
}

CLICKED_BUTTON (menu::autorott::autoflipt, sutlis) {
  static int togs [] = {0, 0, 1};
  din0.setautoflip (MENU.autorotate.which, b.id, togs[b.id]);
}

VALUE_CHANGED (menu::autorott::autoflipt, anglis) {
  din0.setautoflipangle (MENU.autorotate.which);
}

VALUE_CHANGED (menu::autorott::autopauset, everylis) {
  din0.setautopauseparam (MENU.autorotate.which, 0);
}

VALUE_CHANGED (menu::autorott::autopauset, f0rlis) {
  din0.setautopauseparam (MENU.autorotate.which, 1);
}

CLICKED_BUTTON (menu::autorott::autopauset, sutlis) {
  static int togs[] = {0, 0, 1};
  din0.setautopause (MENU.autorotate.which, b.id, togs[b.id]);
}

CLICKED_BUTTON (menu::autorott::rndflipauset, sutlis) {
  static int togs[] = {0, 0, 1};
  menu::autorott& ar = MENU.autorotate;
  din0.setrndflipause (ar.which, ar.rndflipause.which, b.id, togs[b.id]);
}

PICKED_OPTION (menu::autorott::rndflipauset, rndflipauselis) {
  static const char* opts [] = {
    " Randomize Auto Flip Degrees",
    " Randomize Auto Pause : Every",
    " Randomize Auto Pause : For"
  };
  int& which = MENU.autorotate.rndflipause.which;
  which += dir;
  wrap (0, which, 2);
  MENU.autorotate.rndflipause.whichl.set_text (opts[which]);
}

PICKED_OPTION (menu, ol_add_wand_lis) {
  static const char* aws [] = {" Add", " Wand"};
  din0.dinfo.wand = !din0.dinfo.wand;
  MENU.ol_add_wand.set_text (aws[din0.dinfo.wand]);
}

PICKED_OPTION (menu, ol_drones_are_lis) {
  static const char* das [] = {"are immortal", "are mortal", "reincarnate"};
  drone::ARE += dir;
  wrap<int> (drone::IMMORTAL, drone::ARE, drone::REINCARNATE);
  sprintf (BUFFER, " Drones %s", das[drone::ARE]);
  MENU.ol_drones_are.set_text (BUFFER);
}

VALUE_CHANGED (menu,risel) {
  din0.dinfo.drone_rise_time = MENU.riset.value;
  cons << "Drone rise time = " << din0.dinfo.drone_rise_time << eol;
}

VALUE_CHANGED (menu,falll) {
  din0.dinfo.drone_fall_time = MENU.fallt.value;
  cons << "Drone fall time = " << din0.dinfo.drone_fall_time << eol;
}

VALUE_CHANGED(menu,lifetimel) {
  drone::LIFETIME = MENU.lifetime.value;
  cons << "Drone life time = " << drone::LIFETIME << SECONDS << eol;
}

VALUE_CHANGED(menu,handlesizel) {
  drone::HANDLESIZE = MENU.handlesize.value;
  cons << GREEN << "Default drone handle size = " << drone::HANDLESIZE << eol;
}

VALUE_CHANGED(menu,trailsizel) {
  TRAILSIZE = MENU.trailsize.value;
  cons << GREEN << "Default Drone / Ball trail size = " << TRAILSIZE << eol;
}

VALUE_CHANGED(menu,ddpmlis) {
  int& ddpm = din0.dinfo.dpm;
  ddpm = MENU.ddpm.value;
  cons << GREEN << "Default drone launches per minute = " << ddpm << eol;
}

VALUE_CHANGED(menu,ddpllis) {
  int& ddpl = din0.dinfo.dpl;
  ddpl = MENU.ddpl.value;
  cons << GREEN << "Default drones per launch = " << ddpl << eol;
}

VALUE_CHANGED (menu,sp_wand_dist_lis) {
  int d = MENU.sp_wand_dist.value;
  drone::wand.set (d);
  cons << YELLOW << "Wand distance = " << d << eol;
}

VALUE_CHANGED(menu,speedl) {
  din0.changechuckspeed (MENU.chspeed);
}

VALUE_CHANGED(menu,lengthl) {
  din0.changechucklength (MENU.chlen);
}

VALUE_CHANGED(menu,traill) {
  din0.change_drone_trail_points (MENU.chtrail);
}

VALUE_CHANGED(menu,angleperframel) {
  drone::chuckt::apt += MENU.chapt();
  RESETALLCHUCKTRAILS
  cons << YELLOW << "Angle per turn = " << drone::chuckt::apt.deg << DEGREES << eol;
}


CLICKED_BUTTON(menu,mortalizel) {
  din0.mortalize_drones ();
  TOGGLEMENU
}

CLICKED_BUTTON(menu,reincarnatel) {
  #define REINCARNATE 1
  din0.mortalize_drones (REINCARNATE);
  TOGGLEMENU
}

CLICKED_BUTTON(menu,immortalizel) {
  din0.immortalize_drones ();
  TOGGLEMENU
}

CLICKED_BUTTON(menu,chuckl) {
  din0.chuck ();
  TOGGLEMENU
}

CLICKED_BUTTON(menu,chflipl) {
  din0.flipchuckspeed ();
  TOGGLEMENU
}

CLICKED_BUTTON(menu,chtogl) {
  din0.togchuckspeed ();
  TOGGLEMENU
}

CLICKED_CHECKBUTTON(menu,autoresettrailslis) {
  drone::chuckt::autoresettrails = cb.state;
  RESETALLCHUCKTRAILS
}

PICKED_OPTION(menu,anchoredl) {
  drone::anchored = !drone::anchored;
  static const char* lbl [] = {" Drone is launched", " Drone is anchored"};
  MENU.anchored.set_text (lbl[drone::anchored]);
}

void menu::moddirs::clicked (button& cb) {
  DECL_BUTTONS
  for (int i = 0; i < 4; ++i) {
    if (&cb == b[i]) {
      din0.setmoddir (what, i);
      break;
    }
  }
  TOGGLEMENU
}

void menu::moddirs::setup (const string& l) {
  DECL_BUTTONS
  lbl.set_text (l);
  const char* txt [] = {"Vertical", "Horizontal", "Velocity", "Acceleration"};
  for (int i = 0; i < 4; ++i) b[i]->set_text (txt[i]);
#ifdef __WIDGET_MOVE__
  widget* u[] = {&lbl, &vert, &hor, &vel, &accel, 0};
  makefam (u);
#endif
}

void menu::defvelaccelui::autorotatet::setup () {
  dir.set_listener (&dirl);
  mov.set_listener (&movl);
#ifdef __WIDGET_MOVE__
  widget* w[] = {
    &cb,
    &dir,
    &mov,
    &rpm,
    &dps,
    &uet.deg,
    &tps,
    &uet.tps,
    0
  };
  makehier (w);
#endif
}

void menu::defvelaccelui::autorotatet::load () {
  defvelaccel& dva = getdva ();
  defvelaccel::autost::rott& rot = dva.autos.rot;
  cb.set_state (rot.yes, 0);
  uet.deg.set_state (rot.uet.deg, 0);
  uet.tps.set_state (rot.uet.tps, 0);
  static const char* diropts [] = {" Clockwise", " Anti clockwise", " Clockwise or Anti-clockwise"};
  static const char* movopts [] = {" Smooth", " Ticked", " Smooth or Ticked"};
  dir.set_text (diropts[rot.dir]);
  mov.set_text (movopts[rot.mov]);
}

#ifdef __WIDGET_MOVE__
void menu::defvelaccelui::autoflipt::setup () {
  widget* w[] = {
    &cb,
    &deg,
    0
  };
  makehier (w);
}

void menu::defvelaccelui::autopauset::setup () {
  widget* w[] = {
    &cb,
    &every,
    &f0r,
    &tar,
    0
  };
  makehier (w);
}
#endif

PICKED_OPTION (menu::defvelaccelui::autorotatet, dirlis) {
  defvelaccel& dva = getdva ();
  dva.autos.rot.dir += dir;
  wrap<int> (autorotator::CLOCKWISE, dva.autos.rot.dir, autorotator::RANDOM);
  static const char* opts [] = {" Clockwise", " Anti clockwise", " Clockwise or Anti-clockwise"};
  l.set_text (opts[dva.autos.rot.dir]);
}

PICKED_OPTION (menu::defvelaccelui::autorotatet, movlis) {
  defvelaccel& dva = getdva ();
  dva.autos.rot.mov += dir;
  wrap<int> (autorotator::SMOOTH, dva.autos.rot.mov, autorotator::RANDOM);
  static const char* opts [] = {" Smooth", " Ticked", " Smooth or Ticked"};
  l.set_text (opts[dva.autos.rot.mov]);
}

CLICKED_CHECKBUTTON (menu::defvelaccelui::autorotatet, chklis) {
  defvelaccel& dva = getdva ();
  dva.autos.rot.yes = cb.state;
}

drone::bouncet::bouncet () {
  n = 0;
  max = -1;
}

drone::genn::genn () {
  n = 0;
  max = MENU.gens ();
}

std::ostream& operator<< (std::ostream& f, colorer_t& clr) {
  color_data_t& cd = clr.schemes[clr.i]->data;
  color &c0 = cd.clr[0], &c1 = cd.clr[1];
  f << c0.r << spc << c0.g << spc << c0.b << spc << c1.r << spc << c1.g << spc << c1.b;
  return f;
}


std::istream& operator>> (std::istream& f, colorer_t& clr) {
  color_data_t& cd = clr.schemes[0]->data;
  color &c0 = cd.clr[0], &c1 = cd.clr[1];
  f >> c0.r >> c0.g >> c0.b >> c1.r >> c1.g >> c1.b;
  return f;
}

void menu::initcolorsliders () {
  file_in fi ("color-sliders");
  ifstream& f = fi ();
  f >> colorer;
  color_data_t& cd = colorer.schemes[0]->data;
  color &c0 = cd.clr[0], &c1 = cd.clr[1];
  float c [] = {c0.r, c0.g, c0.b, c1.r, c1.g, c1.b};
  slider<float>* s [] = {&s_red_min, &s_green_min, &s_blue_min, &s_red_max, &s_green_max, &s_blue_max};
  for (int i = 0; i < COLOR_SLIDERS_M; ++i) s[i]->set_val (c[i]);
}
void menu::savecolorsliders () {
  file_out fo ("color-sliders");
  ofstream& f = fo ();
  f << colorer << endl;
}

CLICKED_BUTTON (menu, am2fm2amlis) {
  din0.set_drone_am2fm2am_bpm (b.id);
}


CLICKED_CHECKBUTTON (menu, curveslistl) {
  if (!CRVED->setcrvvis (cb.id, cb.state)) cb.turn_on (DONT_CALL_LISTENER);
}

void curve_editor::setup_tools_menu () {
  curve_info& ci = curveinfo [curcrv];
  multi_curve* crv = ci.curve;
  MENU.sp_curve_rpm.set_value (ci.lastrpm);
  MENU.sp_curve_every.variance.cb.set_state (ci.randoflip);
  MENU.sp_curve_every.variance.rd = ci.rd;
  MENU.sp_curve_every.variance.setfld ();
  MENU.sp_curve_every.set_value (ci.every0.deg);
  MENU.cb_ed_autoflip.set_state (ci.autoflip);
  MENU.lf_curve_name.set_text (crv->name);
  MENU.sp_curve_limit.set_value (crv->limit);
  MENU.picked (MENU.ol_mirror.option, 0);
  set_curve_style (crv);
  curcrvchgd = 0;
}


PICKED_OPTION (menu, moverailstrafelis) {
  static const char* opts [] = {" Move", " Rail", " Strafe"};
  click_listener* clk [] = {MENUP.bmdl, MENUP.raill, MENUP.strafel};
  int& id = MENU.moverailstrafe.id;
  button& option = MENU.moverailstrafe.option;
  id += dir;
  wrap (0, id, 2);
  option.set_text (opts[id]);
  LISTEN(option, clk[id]);
}

float getlifetime () {
  return MENU.lifetime ();
}

VALUE_CHANGED (menu,texsteplis) {
  mondrian0.patstep = MENU.texstep();
  mondrian0.fillpatbuf ();
  cons << GREEN << "Texture step = " <<  mondrian0.patstep << eol;
}

PICKED_OPTION (menu, revmodl) {
  static const char* opts [] = {"Both", "AM", "FM"};
  int& revmod = din0.dinfo.revmod;
  revmod += dir;
  wrap (0, revmod, 2);
  sprintf (BUFFER, " Reverse %s", opts[revmod]);
  l.set_text (BUFFER);
}

CLICKED_BUTTON (menu, revclkl) {
  din0.reverse_drone_modulation ();
}

VALUE_CHANGED (menu,sep_bin_lis) {
  din0.change_drone_sep (MENU.sep_bin);
}