/*
 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
 * Copyright (C) 2009-2015 David Robillard <d@drobilla.net>
 * Copyright (C) 2009-2019 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
 * Copyright (C) 2013-2016 Nick Mainsbridge <mainsbridge@gmail.com>
 * Copyright (C) 2013-2018 Colin Fletcher <colin.m.fletcher@googlemail.com>
 * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
 * Copyright (C) 2014-2016 Tim Mayberry <mojofunk@gmail.com>
 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
 * Copyright (C) 2018 Len Ovens <len@ovenwerks.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef WAF_BUILD
#include "gtk2ardour-config.h"
#endif

#include <cairo/cairo.h>

#include <boost/algorithm/string.hpp>

#include <gtkmm/liststore.h>
#include <gtkmm/stock.h>
#include <gtkmm/scale.h>

#include "gtkmm2ext/keyboard.h"
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/gtk_ui.h"
#include "gtkmm2ext/window_title.h"

#include "pbd/file_utils.h"
#include "pbd/fpu.h"
#include "pbd/cpus.h"
#include "pbd/unwind.h"
#include "pbd/i18n.h"

#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/clip_library.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/dB.h"
#include "ardour/parameter_descriptor.h"
#include "ardour/port_manager.h"
#include "ardour/plugin_manager.h"
#include "ardour/profile.h"
#include "ardour/rc_configuration.h"
#include "ardour/transport_master_manager.h"

#include "control_protocol/control_protocol.h"

#include "waveview/wave_view.h"

#include "widgets/paths_dialog.h"
#include "widgets/tooltips.h"

#include "actions.h"
#include "ardour_ui.h"
#include "ardour_dialog.h"
#include "ardour_ui.h"
#include "ardour_window.h"
#include "color_theme_manager.h"
#include "gui_thread.h"
#include "keyboard.h"
#include "meter_patterns.h"
#include "midi_tracer.h"
#include "plugin_scan_dialog.h"
#include "rc_option_editor.h"
#include "sfdb_ui.h"
#include "transport_masters_dialog.h"
#include "ui_config.h"
#include "utils.h"

using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace PBD;
using namespace ARDOUR;
using namespace ARDOUR_UI_UTILS;
using namespace ArdourWidgets;

class ClickOptions : public OptionEditorMiniPage
{
public:
	ClickOptions (RCConfiguration* c)
		: _rc_config (c)
		, _click_browse_button (_("Browse..."))
		, _click_emphasis_browse_button (_("Browse..."))
	{
		// TODO get rid of GTK -> use OptionEditor Widgets
		Table* t = &table;
		Label* l;
		int row = 0;

		l = manage (left_aligned_label (_("Emphasis on first beat")));
		_use_emphasis_on_click_check_button.add (*l);
		t->attach (_use_emphasis_on_click_check_button, 1, 3, row, row + 1, FILL);
		_use_emphasis_on_click_check_button.signal_toggled().connect (
		    sigc::mem_fun (*this, &ClickOptions::use_emphasis_on_click_toggled));
		++row;

		l = manage (left_aligned_label (_("Use built-in default sounds")));
		_use_default_click_check_button.add (*l);
		t->attach (_use_default_click_check_button, 1, 3, row, row + 1, FILL);
		_use_default_click_check_button.signal_toggled().connect (
		    sigc::mem_fun (*this, &ClickOptions::use_default_click_toggled));
		++row;

		l = manage (left_aligned_label (_("Audio file:")));
		t->attach (*l, 1, 2, row, row + 1, FILL);
		t->attach (_click_path_entry, 2, 3, row, row + 1, FILL);
		_click_browse_button.signal_clicked ().connect (
		    sigc::mem_fun (*this, &ClickOptions::click_browse_clicked));
		t->attach (_click_browse_button, 3, 4, row, row + 1, FILL);
		++row;

		l = manage (left_aligned_label (_("Emphasis audio file:")));
		t->attach (*l, 1, 2, row, row + 1, FILL);
		t->attach (_click_emphasis_path_entry, 2, 3, row, row + 1, FILL);
		_click_emphasis_browse_button.signal_clicked ().connect (
		    sigc::mem_fun (*this, &ClickOptions::click_emphasis_browse_clicked));
		t->attach (_click_emphasis_browse_button, 3, 4, row, row + 1, FILL);
		++row;

		_click_fader = new FaderOption (
				"click-gain",
				_("Gain level"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_click_gain),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_click_gain)
				);

		_click_fader->add_to_page (this);
		_click_fader->set_state_from_config ();

		_click_path_entry.signal_activate().connect (sigc::mem_fun (*this, &ClickOptions::click_changed));
		_click_emphasis_path_entry.signal_activate().connect (sigc::mem_fun (*this, &ClickOptions::click_emphasis_changed));

		if (_rc_config->get_click_sound ().empty() &&
		    _rc_config->get_click_emphasis_sound().empty()) {
			_use_default_click_check_button.set_active (true);
			_use_emphasis_on_click_check_button.set_active (_rc_config->get_use_click_emphasis ());

		} else {
			_use_default_click_check_button.set_active (false);
			_use_emphasis_on_click_check_button.set_active (false);
		}
	}

	void parameter_changed (string const & p)
	{
		if (p == "click-sound") {
			_click_path_entry.set_text (_rc_config->get_click_sound());
		} else if (p == "click-emphasis-sound") {
			_click_emphasis_path_entry.set_text (_rc_config->get_click_emphasis_sound());
		} else if (p == "use-click-emphasis") {
			bool x = _rc_config->get_use_click_emphasis ();
			_use_emphasis_on_click_check_button.set_active (x);
		} else if (p == "click-gain") {
			_click_fader->set_state_from_config ();
		}
	}

	void set_state_from_config ()
	{
		parameter_changed ("click-sound");
		parameter_changed ("click-emphasis-sound");
		parameter_changed ("use-click-emphasis");
	}

private:

	void click_browse_clicked ()
	{
		SoundFileChooser sfdb (_("Choose Click"));

		sfdb.show_all ();
		sfdb.present ();

		if (sfdb.run () == RESPONSE_OK) {
			click_chosen (sfdb.get_filename());
		}
	}

	void click_chosen (string const & path)
	{
		_click_path_entry.set_text (path);
		_rc_config->set_click_sound (path);
	}

	void click_changed ()
	{
		click_chosen (_click_path_entry.get_text ());
	}

	void click_emphasis_browse_clicked ()
	{
		SoundFileChooser sfdb (_("Choose Click Emphasis"));

		sfdb.show_all ();
		sfdb.present ();

		if (sfdb.run () == RESPONSE_OK) {
			click_emphasis_chosen (sfdb.get_filename());
		}
	}

	void click_emphasis_chosen (string const & path)
	{
		_click_emphasis_path_entry.set_text (path);
		_rc_config->set_click_emphasis_sound (path);
	}

	void click_emphasis_changed ()
	{
		click_emphasis_chosen (_click_emphasis_path_entry.get_text ());
	}

	void use_default_click_toggled ()
	{
		if (_use_default_click_check_button.get_active ()) {
			_rc_config->set_click_sound ("");
			_rc_config->set_click_emphasis_sound ("");
			_click_path_entry.set_sensitive (false);
			_click_emphasis_path_entry.set_sensitive (false);
			_click_browse_button.set_sensitive (false);
			_click_emphasis_browse_button.set_sensitive (false);
		} else {
			_click_path_entry.set_sensitive (true);
			_click_emphasis_path_entry.set_sensitive (true);
			_click_browse_button.set_sensitive (true);
			_click_emphasis_browse_button.set_sensitive (true);
		}
	}

	void use_emphasis_on_click_toggled ()
	{
		if (_use_emphasis_on_click_check_button.get_active ()) {
			_rc_config->set_use_click_emphasis(true);
		} else {
			_rc_config->set_use_click_emphasis(false);
		}
	}

	RCConfiguration* _rc_config;
	CheckButton _use_default_click_check_button;
	CheckButton _use_emphasis_on_click_check_button;
	Entry _click_path_entry;
	Entry _click_emphasis_path_entry;
	Button _click_browse_button;
	Button _click_emphasis_browse_button;
	FaderOption* _click_fader;
};

class UndoOptions : public OptionEditorComponent
{
public:
	UndoOptions (RCConfiguration* c) :
		_rc_config (c),
		_limit_undo_button (_("Limit undo history to")),
		_save_undo_button (_("Save undo history of"))
	{
		// TODO get rid of GTK -> use OptionEditor SpinOption
		_limit_undo_spin.set_range (0, 512);
		_limit_undo_spin.set_increments (1, 10);

		_save_undo_spin.set_range (0, 512);
		_save_undo_spin.set_increments (1, 10);

		_limit_undo_button.signal_toggled().connect (sigc::mem_fun (*this, &UndoOptions::limit_undo_toggled));
		_limit_undo_spin.signal_value_changed().connect (sigc::mem_fun (*this, &UndoOptions::limit_undo_changed));
		_save_undo_button.signal_toggled().connect (sigc::mem_fun (*this, &UndoOptions::save_undo_toggled));
		_save_undo_spin.signal_value_changed().connect (sigc::mem_fun (*this, &UndoOptions::save_undo_changed));
	}

	void parameter_changed (string const & p)
	{
		if (p == "history-depth") {
			int32_t const d = _rc_config->get_history_depth();
			_limit_undo_button.set_active (d != 0);
			_limit_undo_spin.set_sensitive (d != 0);
			_limit_undo_spin.set_value (d);
		} else if (p == "save-history") {
			bool const x = _rc_config->get_save_history ();
			_save_undo_button.set_active (x);
			_save_undo_spin.set_sensitive (x);
		} else if (p == "save-history-depth") {
			_save_undo_spin.set_value (_rc_config->get_saved_history_depth());
		}
	}

	void set_state_from_config ()
	{
		parameter_changed ("save-history");
		parameter_changed ("history-depth");
		parameter_changed ("save-history-depth");
	}

	void limit_undo_toggled ()
	{
		bool const x = _limit_undo_button.get_active ();
		_limit_undo_spin.set_sensitive (x);
		int32_t const n = x ? 16 : 0;
		_limit_undo_spin.set_value (n);
		_rc_config->set_history_depth (n);
	}

	void limit_undo_changed ()
	{
		_rc_config->set_history_depth (_limit_undo_spin.get_value_as_int ());
	}

	void save_undo_toggled ()
	{
		bool const x = _save_undo_button.get_active ();
		_rc_config->set_save_history (x);
	}

	void save_undo_changed ()
	{
		_rc_config->set_saved_history_depth (_save_undo_spin.get_value_as_int ());
	}

	void add_to_page (OptionEditorPage* p)
	{
		int const n = p->table.property_n_rows();
		Table* t = & p->table;

		t->resize (n + 2, 3);

		Label* l = manage (left_aligned_label (_("commands")));
		HBox* box = manage (new HBox());
		box->set_spacing (4);
		box->pack_start (_limit_undo_spin, false, false);
		box->pack_start (*l, true, true);
		t->attach (_limit_undo_button, 1, 2, n, n +1, FILL);
		t->attach (*box, 2, 3, n, n + 1, FILL | EXPAND);

		l = manage (left_aligned_label (_("commands")));
		box = manage (new HBox());
		box->set_spacing (4);
		box->pack_start (_save_undo_spin, false, false);
		box->pack_start (*l, true, true);
		t->attach (_save_undo_button, 1, 2, n + 1, n + 2, FILL);
		t->attach (*box, 2, 3, n + 1, n + 2, FILL | EXPAND);
	}

	Gtk::Widget& tip_widget() {
		return _limit_undo_button; // unused
	}

private:
	RCConfiguration* _rc_config;
	CheckButton _limit_undo_button;
	SpinButton _limit_undo_spin;
	CheckButton _save_undo_button;
	SpinButton _save_undo_spin;
};


static const struct {
	const char *name;
	guint modifier;
} modifiers[] = {

	{ "Unmodified", 0 },

#ifdef __APPLE__

	/* Command = Meta
	   Option/Alt = Mod1
	*/
	{ "Key|Shift", GDK_SHIFT_MASK },
	{ "Command", GDK_MOD2_MASK },
	{ "Control", GDK_CONTROL_MASK },
	{ "Option", GDK_MOD1_MASK },
	{ "Command-Shift", GDK_MOD2_MASK|GDK_SHIFT_MASK },
	{ "Command-Option", GDK_MOD2_MASK|GDK_MOD1_MASK },
	{ "Command-Control", GDK_MOD2_MASK|GDK_CONTROL_MASK },
	{ "Command-Option-Control", GDK_MOD2_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK },
	{ "Option-Control", GDK_MOD1_MASK|GDK_CONTROL_MASK },
	{ "Option-Shift", GDK_MOD1_MASK|GDK_SHIFT_MASK },
	{ "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
	{ "Shift-Command-Option", GDK_MOD5_MASK|GDK_SHIFT_MASK|GDK_MOD2_MASK },

#else
	{ "Key|Shift", GDK_SHIFT_MASK },
	{ "Control", GDK_CONTROL_MASK },
	{ "Alt", GDK_MOD1_MASK },
	{ "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
	{ "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
	{ "Control-Windows", GDK_CONTROL_MASK|GDK_MOD4_MASK },
	{ "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
	{ "Alt-Windows", GDK_MOD1_MASK|GDK_MOD4_MASK },
	{ "Alt-Shift", GDK_MOD1_MASK|GDK_SHIFT_MASK },
	{ "Alt-Shift-Windows", GDK_MOD1_MASK|GDK_SHIFT_MASK|GDK_MOD4_MASK },
	{ "Mod2", GDK_MOD2_MASK },
	{ "Mod3", GDK_MOD3_MASK },
	{ "Windows", GDK_MOD4_MASK },
	{ "Mod5", GDK_MOD5_MASK },
#endif
	{ 0, 0 }
};


class KeyboardOptions : public OptionEditorMiniPage
{
public:
	KeyboardOptions ()
		: _delete_button_adjustment (3, 1, 12)
		, _delete_button_spin (_delete_button_adjustment)
		, _edit_button_adjustment (3, 1, 5)
		, _edit_button_spin (_edit_button_adjustment)
		, _insert_note_button_adjustment (3, 1, 5)
		, _insert_note_button_spin (_insert_note_button_adjustment)
	{
		// TODO get rid of GTK -> use OptionEditor Widgets

		const std::string restart_msg = _("\nChanges to this setting will only persist after your project has been saved.");
		/* internationalize and prepare for use with combos */

		vector<string> dumb;
		for (int i = 0; modifiers[i].name; ++i) {
			dumb.push_back (S_(modifiers[i].name));
		}

		set_popdown_strings (_edit_modifier_combo, dumb);
		_edit_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::edit_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_edit_modifier_combo,
				(string_compose (_("<b>Recommended Setting: %1 + button 3 (right mouse button)</b>%2"),  Keyboard::primary_modifier_name (), restart_msg)));

		Table* t = &table;

		int row = 0;
		int col = 0;

		Label* l = manage (left_aligned_label (_("Select Keyboard layout:")));
		l->set_name ("OptionsLabel");

		vector<string> strs;

		for (map<string,string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
			strs.push_back (bf->first);
		}

		set_popdown_strings (_keyboard_layout_selector, strs);
		_keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
		_keyboard_layout_selector.signal_changed().connect (sigc::mem_fun (*this, &KeyboardOptions::bindings_changed));

		t->attach (*l, col + 1, col + 2, row, row + 1, FILL, FILL);
		t->attach (_keyboard_layout_selector, col + 2, col + 3, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 0;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("When Clicking:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, col, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		l = manage (left_aligned_label (_("Edit using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_edit_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		l = manage (new Label (_("+ button")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col + 3, col + 4, row, row + 1, FILL, FILL);
		t->attach (_edit_button_spin, col + 4, col + 5, row, row + 1, SHRINK , FILL);

		_edit_button_spin.set_name ("OptionsEntry");
		_edit_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::edit_button_changed));

		++row;
		col = 1;

		set_popdown_strings (_delete_modifier_combo, dumb);
		_delete_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_delete_modifier_combo,
				(string_compose (_("<b>Recommended Setting: %1 + button 3 (right mouse button)</b>%2"), Keyboard::tertiary_modifier_name (), restart_msg)));

		l = manage (left_aligned_label (_("Delete using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_delete_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		l = manage (new Label (_("+ button")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col + 3, col + 4, row, row + 1, FILL, FILL);
		t->attach (_delete_button_spin, col + 4, col + 5, row, row + 1, SHRINK, FILL);

		_delete_button_spin.set_name ("OptionsEntry");
		_delete_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_button_changed));

		++row;
		col = 1;

		set_popdown_strings (_insert_note_modifier_combo, dumb);
		_insert_note_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::insert_note_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_insert_note_modifier_combo,
				(string_compose (_("<b>Recommended Setting: %1 + button 1 (left mouse button)</b>%2"), Keyboard::primary_modifier_name (), restart_msg)));

		l = manage (left_aligned_label (_("Insert note using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_insert_note_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		l = manage (new Label (_("+ button")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col + 3, col + 4, row, row + 1, FILL, FILL);
		t->attach (_insert_note_button_spin, col + 4, col + 5, row, row + 1, SHRINK, FILL);

		_insert_note_button_spin.set_name ("OptionsEntry");
		_insert_note_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::insert_note_button_changed));

		++row;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("When Beginning a Drag:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* copy modifier */
		set_popdown_strings (_copy_modifier_combo, dumb);
		_copy_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::copy_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_copy_modifier_combo,
						    (string_compose (_("<b>Recommended Setting: %1</b>%2"),
#ifdef __APPLE__
								     Keyboard::secondary_modifier_name (),
#else
								     Keyboard::primary_modifier_name (),
#endif
								     restart_msg)));

		l = manage (left_aligned_label (_("Copy items using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_copy_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

				++row;
		col = 1;

		/* slip_contents */
		set_popdown_strings (_slip_contents_combo, dumb);
		_slip_contents_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::slip_contents_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_slip_contents_combo,
				(string_compose (_("<b>Recommended Setting: %1-%2</b>%3"), Keyboard::primary_modifier_name (), Keyboard::tertiary_modifier_name (), restart_msg)));

		l = manage (left_aligned_label (_("Slip Contents using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_slip_contents_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* constraint modifier */
		set_popdown_strings (_constraint_modifier_combo, dumb);
		_constraint_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::constraint_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_constraint_modifier_combo,
						    (string_compose (_("<b>Recommended Setting: %1</b>%2"),
#ifdef __APPLE__
								     Keyboard::primary_modifier_name (),
#else
								     Keyboard::tertiary_modifier_name (),
#endif
								     restart_msg)));

		l = manage (left_aligned_label (_("Constrain drag using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_constraint_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* push points */
		set_popdown_strings (_push_points_combo, dumb);
		_push_points_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::push_points_modifier_chosen));

		std::string mod_str = string_compose (X_("%1-%2"), Keyboard::primary_modifier_name (), Keyboard::level4_modifier_name ());
		Gtkmm2ext::UI::instance()->set_tip (_push_points_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), mod_str, restart_msg)));

		l = manage (left_aligned_label (_("Push points using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_push_points_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("When Beginning a Trim:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* anchored trim */
		set_popdown_strings (_trim_anchored_combo, dumb);
		_trim_anchored_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_anchored_modifier_chosen));

		mod_str = string_compose (X_("%1-%2"), Keyboard::primary_modifier_name (), Keyboard::tertiary_modifier_name ());
		Gtkmm2ext::UI::instance()->set_tip (_trim_anchored_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), mod_str, restart_msg)));

		l = manage (left_aligned_label (_("Anchored trim using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		++col;
		t->attach (_trim_anchored_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* jump trim disabled for now
		set_popdown_strings (_trim_jump_combo, dumb);
		_trim_jump_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_jump_modifier_chosen));

		l = manage (left_aligned_label (_("Jump after trim using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		++col;
		t->attach (_trim_jump_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;
		*/

		/* note resize relative */
		set_popdown_strings (_note_size_relative_combo, dumb);
		_note_size_relative_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::note_size_relative_modifier_chosen));
		Gtkmm2ext::UI::instance()->set_tip (_note_size_relative_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), Keyboard::tertiary_modifier_name (), restart_msg))); // XXX 2ndary

		l = manage (left_aligned_label (_("Resize notes relatively using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		++col;
		t->attach (_note_size_relative_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);

		++row;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("While Dragging:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* ignore snap */
		set_popdown_strings (_snap_modifier_combo, dumb);
		_snap_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::snap_modifier_chosen));
#ifdef __APPLE__
		mod_str = string_compose (X_("%1-%2"), Keyboard::level4_modifier_name (), Keyboard::tertiary_modifier_name ());
#else
		mod_str = Keyboard::secondary_modifier_name();
#endif
		Gtkmm2ext::UI::instance()->set_tip (_snap_modifier_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), mod_str, restart_msg)));

		l = manage (left_aligned_label (_("Ignore snap using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_snap_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* snap delta */
		set_popdown_strings (_snap_delta_combo, dumb);
		_snap_delta_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::snap_delta_modifier_chosen));
#ifdef __APPLE__
		mod_str = Keyboard::level4_modifier_name ();
#else
		mod_str = string_compose (X_("%1-%2"), Keyboard::secondary_modifier_name (), Keyboard::level4_modifier_name ());
#endif
		Gtkmm2ext::UI::instance()->set_tip (_snap_delta_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), mod_str, restart_msg)));

		l = manage (left_aligned_label (_("Snap relatively using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_snap_delta_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("While Trimming:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* trim_overlap */
		set_popdown_strings (_trim_overlap_combo, dumb);
		_trim_overlap_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_overlap_modifier_chosen));

		Gtkmm2ext::UI::instance()->set_tip (_trim_overlap_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), Keyboard::tertiary_modifier_name (), restart_msg)));

		l = manage (left_aligned_label (_("Resize overlapped regions using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_trim_overlap_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		++row;

		l = manage (left_aligned_label (string_compose ("<b>%1</b>", _("While Dragging Control Points:"))));
		l->set_name ("OptionEditorHeading");
		l->set_use_markup (true);
		t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);

		++row;
		col = 1;

		/* fine adjust */
		set_popdown_strings (_fine_adjust_combo, dumb);
		_fine_adjust_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::fine_adjust_modifier_chosen));

		mod_str = string_compose (X_("%1-%2"), Keyboard::primary_modifier_name (), Keyboard::secondary_modifier_name ()); // XXX just 2ndary ?!
		Gtkmm2ext::UI::instance()->set_tip (_fine_adjust_combo,
				(string_compose (_("<b>Recommended Setting: %1</b>%2"), mod_str, restart_msg)));

		l = manage (left_aligned_label (_("Fine adjust using:")));
		l->set_name ("OptionsLabel");

		t->attach (*l, col, col + 1, row, row + 1, FILL, FILL);
		t->attach (_fine_adjust_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);

		OptionEditorHeading* h = new OptionEditorHeading (_("Reset"));
		h->add_to_page (this);

		RcActionButton* rb = new RcActionButton (_("Reset to recommended defaults"),
				sigc::mem_fun (*this, &KeyboardOptions::reset_to_defaults));
		rb->add_to_page (this);

		set_state_from_config ();
	}

	void parameter_changed (string const &)
	{
		/* XXX: these aren't really config options... */
	}

	void set_state_from_config ()
	{
		_delete_button_adjustment.set_value (Keyboard::delete_button());
		_insert_note_button_adjustment.set_value (Keyboard::insert_note_button());
		_edit_button_adjustment.set_value (Keyboard::edit_button());

		for (int x = 0; modifiers[x].name; ++x) {
			if (modifiers[x].modifier == (guint) ArdourKeyboard::trim_overlap_modifier ()) {
				_trim_overlap_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == Keyboard::delete_modifier ()) {
				_delete_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == Keyboard::edit_modifier ()) {
				_edit_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == Keyboard::insert_note_modifier ()) {
				_insert_note_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) Keyboard::CopyModifier) {
				_copy_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) ArdourKeyboard::constraint_modifier ()) {
				_constraint_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) ArdourKeyboard::push_points_modifier ()) {
				_push_points_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) ArdourKeyboard::slip_contents_modifier ()) {
				_slip_contents_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) ArdourKeyboard::trim_anchored_modifier ()) {
				_trim_anchored_combo.set_active_text (S_(modifiers[x].name));
			}
#if 0
			if (modifiers[x].modifier == (guint) Keyboard::trim_jump_modifier ()) {
				_trim_jump_combo.set_active_text (S_(modifiers[x].name));
			}
#endif
			if (modifiers[x].modifier == (guint) ArdourKeyboard::note_size_relative_modifier ()) {
				_note_size_relative_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) Keyboard::snap_modifier ()) {
				_snap_modifier_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) Keyboard::snap_delta_modifier ()) {
				_snap_delta_combo.set_active_text (S_(modifiers[x].name));
			}
			if (modifiers[x].modifier == (guint) ArdourKeyboard::fine_adjust_modifier ()) {
				_fine_adjust_combo.set_active_text (S_(modifiers[x].name));
			}
		}
	}

	void add_to_page (OptionEditorPage* p)
	{
		int const n = p->table.property_n_rows();
		p->table.resize (n + 1, 3);
		p->table.attach (box, 1, 3, n, n + 1, FILL | EXPAND, SHRINK, 0, 0);
	}

private:

	void bindings_changed ()
	{
		string const txt = _keyboard_layout_selector.get_active_text();

		/* XXX: config...?  for all this keyboard stuff */

		for (map<string,string>::iterator i = Keyboard::binding_files.begin(); i != Keyboard::binding_files.end(); ++i) {
			if (txt == i->first) {
				if (Keyboard::load_keybindings (i->second)) {
					Keyboard::save_keybindings ();
				}
			}
		}
	}

	void edit_modifier_chosen ()
	{
		string const txt = _edit_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_edit_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void delete_modifier_chosen ()
	{
		string const txt = _delete_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_delete_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void copy_modifier_chosen ()
	{
		string const txt = _copy_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_copy_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void insert_note_modifier_chosen ()
	{
		string const txt = _insert_note_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_insert_note_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void snap_modifier_chosen ()
	{
		string const txt = _snap_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_snap_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void snap_delta_modifier_chosen ()
	{
		string const txt = _snap_delta_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				Keyboard::set_snap_delta_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void constraint_modifier_chosen ()
	{
		string const txt = _constraint_modifier_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_constraint_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void slip_contents_modifier_chosen ()
	{
		string const txt = _slip_contents_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_slip_contents_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void trim_overlap_modifier_chosen ()
	{
		string const txt = _trim_overlap_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_trim_overlap_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void trim_anchored_modifier_chosen ()
	{
		string const txt = _trim_anchored_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_trim_anchored_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void fine_adjust_modifier_chosen ()
	{
		string const txt = _fine_adjust_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_fine_adjust_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void push_points_modifier_chosen ()
	{
		string const txt = _push_points_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_push_points_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void note_size_relative_modifier_chosen ()
	{
		string const txt = _note_size_relative_combo.get_active_text();

		for (int i = 0; modifiers[i].name; ++i) {
			if (txt == S_(modifiers[i].name)) {
				ArdourKeyboard::set_note_size_relative_modifier (modifiers[i].modifier);
				break;
			}
		}
	}

	void delete_button_changed ()
	{
		Keyboard::set_delete_button (_delete_button_spin.get_value_as_int());
	}

	void edit_button_changed ()
	{
		Keyboard::set_edit_button (_edit_button_spin.get_value_as_int());
	}

	void insert_note_button_changed ()
	{
		Keyboard::set_insert_note_button (_insert_note_button_spin.get_value_as_int());
	}

	void reset_to_defaults ()
	{
		/* when clicking*/
		Keyboard::set_edit_modifier (Keyboard::PrimaryModifier);
		Keyboard::set_edit_button (3);
		Keyboard::set_delete_modifier (Keyboard::TertiaryModifier);
		Keyboard::set_delete_button (3);
		Keyboard::set_insert_note_modifier (Keyboard::PrimaryModifier);
		Keyboard::set_insert_note_button (1);

		/* when beginning a drag */
#ifdef __APPLE__
		Keyboard::set_copy_modifier (Keyboard::SecondaryModifier);
#else
		Keyboard::set_copy_modifier (Keyboard::PrimaryModifier);
#endif

#ifdef __APPLE__
		ArdourKeyboard::set_constraint_modifier (Keyboard::PrimaryModifier);
#else
		ArdourKeyboard::set_constraint_modifier (Keyboard::TertiaryModifier);
#endif
		ArdourKeyboard::set_push_points_modifier (Keyboard::PrimaryModifier | Keyboard::Level4Modifier);

		/* when beginning a trim */
		ArdourKeyboard::set_slip_contents_modifier (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier);
		ArdourKeyboard::set_trim_anchored_modifier (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier);
		ArdourKeyboard::set_note_size_relative_modifier (Keyboard::TertiaryModifier); // XXX better: 2ndary

		/* while dragging */
#ifdef __APPLE__
		Keyboard::set_snap_modifier (Keyboard::TertiaryModifier);
#else
		Keyboard::set_snap_modifier (Keyboard::SecondaryModifier);
#endif
#ifdef __APPLE__
		Keyboard::set_snap_delta_modifier (Keyboard::Level4Modifier);
#else
		Keyboard::set_snap_delta_modifier (Keyboard::SecondaryModifier | Keyboard::Level4Modifier);
#endif

		/* while trimming */
		ArdourKeyboard::set_trim_overlap_modifier (Keyboard::TertiaryModifier);

		/* while dragging ctrl points */
		ArdourKeyboard::set_fine_adjust_modifier (/*Keyboard::PrimaryModifier | */Keyboard::SecondaryModifier); // XXX

		set_state_from_config ();
	}

	ComboBoxText _keyboard_layout_selector;
	ComboBoxText _edit_modifier_combo;
	ComboBoxText _delete_modifier_combo;
	ComboBoxText _copy_modifier_combo;
	ComboBoxText _insert_note_modifier_combo;
	ComboBoxText _snap_modifier_combo;
	ComboBoxText _snap_delta_combo;
	ComboBoxText _constraint_modifier_combo;
	ComboBoxText _slip_contents_combo;
	ComboBoxText _trim_overlap_combo;
	ComboBoxText _trim_anchored_combo;
	ComboBoxText _trim_jump_combo;
	ComboBoxText _fine_adjust_combo;
	ComboBoxText _push_points_combo;
	ComboBoxText _note_size_relative_combo;
	Adjustment _delete_button_adjustment;
	SpinButton _delete_button_spin;
	Adjustment _edit_button_adjustment;
	SpinButton _edit_button_spin;
	Adjustment _insert_note_button_adjustment;
	SpinButton _insert_note_button_spin;

};

class FontScalingOptions : public HSliderOption
{
	public:
		FontScalingOptions ()
			: HSliderOption ("font-scale", _("GUI and Font scaling"),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_font_scale),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_font_scale),
					50, 250, 1, 5,
					1024, false)
	{
		const std::string dflt = _("100%");
		const std::string dbl = _("200%");
		const std::string empty = X_(""); // despite gtk-doc saying so, NULL does not work as reference

		_hscale.set_name("FontScaleSlider");
		_hscale.set_draw_value(false);
		_hscale.add_mark(50,  Gtk::POS_TOP, empty);
		_hscale.add_mark(60,  Gtk::POS_TOP, empty);
		_hscale.add_mark(70,  Gtk::POS_TOP, empty);
		_hscale.add_mark(80,  Gtk::POS_TOP, empty);
		_hscale.add_mark(90,  Gtk::POS_TOP, empty);
		_hscale.add_mark(100, Gtk::POS_TOP, dflt);
		_hscale.add_mark(125, Gtk::POS_TOP, empty);
		_hscale.add_mark(150, Gtk::POS_TOP, empty);
		_hscale.add_mark(175, Gtk::POS_TOP, empty);
		_hscale.add_mark(200, Gtk::POS_TOP, dbl);
		_hscale.add_mark(250, Gtk::POS_TOP, empty);

		set_note (_("Adjusting the scale requires an application restart for fully accurate re-layout."));
	}

	void changed ()
	{
		HSliderOption::changed ();
		/* XXX: should be triggered from the parameter changed signal */
		UIConfiguration::instance().reset_dpi ();
	}
};

class PluginScanTimeOutSliderOption : public HSliderOption
{
public:
	PluginScanTimeOutSliderOption (RCConfiguration* c)
		: HSliderOption ("vst-scan-timeout", _("Scan Time Out"),
				sigc::mem_fun (*c, &RCConfiguration::get_plugin_scan_timeout),
				sigc::mem_fun (*c, &RCConfiguration::set_plugin_scan_timeout),
				1, 900, 50, 50)
	{
		_label->set_alignment (1.0, 0.5); // match buttons below
		_hscale.set_digits (0);
		_hscale.set_draw_value(false);
		_hscale.add_mark ( 10,  Gtk::POS_TOP, _("1 sec"));
		_hscale.add_mark (150,  Gtk::POS_TOP, _("15 sec"));
		_hscale.add_mark (300,  Gtk::POS_TOP, _("30 sec"));
		_hscale.add_mark (450,  Gtk::POS_TOP, _("45 sec"));
		_hscale.add_mark (600,  Gtk::POS_TOP, _("1 min"));
		_hscale.add_mark (900,  Gtk::POS_TOP, _("1'30\""));

		Gtkmm2ext::UI::instance()->set_tip(_hscale,
			 _("Specify the default timeout for plugin instantiation. Plugins that require more time to load will be ignored. A value of 0 disables the timeout."));
	}
};

class ClipLevelOptions : public HSliderOption
{
public:
	ClipLevelOptions ()
		: HSliderOption (X_("waveform-clip-level"),
		                 _("Waveform Clip Level (dBFS)"),
		                 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_waveform_clip_level),
		                 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_waveform_clip_level),
		                 -50.0, -0.5, 0.1, 1.0, /* units of dB */
		                 1.0,
		                 false)
	{
	}

	void parameter_changed (string const & p)
	{
		if (p == "waveform-clip-level") {
			ArdourWaveView::WaveView::set_clip_level (UIConfiguration::instance().get_waveform_clip_level());
		}
		if (p == "show-waveform-clipping") {
			_hscale.set_sensitive (UIConfiguration::instance().get_show_waveform_clipping ());
		}
	}

	void set_state_from_config ()
	{
		parameter_changed ("waveform-clip-level");
		parameter_changed ("show-waveform-clipping");
	}
};

class BufferingOptions : public OptionEditorComponent
{
	public:
		BufferingOptions (RCConfiguration* c)
			: _rc_config (c)
			, _label (_("Preset:"))
			, _playback ("playback-buffer-seconds", _("Playback (seconds of buffering)"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_audio_playback_buffer_seconds),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_audio_playback_buffer_seconds),
					1, 60, 1, 4)
			, _capture ("capture-buffer-seconds", _("Recording (seconds of buffering)"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_audio_capture_buffer_seconds),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_audio_capture_buffer_seconds),
					1, 60, 1, 4)
		{
			// TODO use  ComboOption
			vector<string> presets;

			/* these must match the order of the enums for BufferingPreset */
			presets.push_back (_("Small sessions (4-16 tracks)"));
			presets.push_back (_("Medium sessions (16-64 tracks)"));
			presets.push_back (_("Large sessions (64+ tracks)"));
			presets.push_back (_("Custom (set by sliders below)"));

			set_popdown_strings (_buffering_presets_combo, presets);
			_buffering_presets_combo.signal_changed().connect (sigc::mem_fun (*this, &BufferingOptions::preset_changed));

			_label.set_name ("OptionsLabel");
			_label.set_alignment (0, 0.5);
		}

		void
		add_to_page (OptionEditorPage* p)
		{
			add_widgets_to_page (p, &_label, &_buffering_presets_combo);
			_playback.add_to_page (p);
			_capture.add_to_page (p);
		}

		void parameter_changed (string const & p)
		{
			if (p == "buffering-preset") {
				switch (_rc_config->get_buffering_preset()) {
					case Small:
						_playback.set_sensitive (false);
						_capture.set_sensitive (false);
						_buffering_presets_combo.set_active (0);
						break;
					case Medium:
						_playback.set_sensitive (false);
						_capture.set_sensitive (false);
						_buffering_presets_combo.set_active (1);
						break;
					case Large:
						_playback.set_sensitive (false);
						_capture.set_sensitive (false);
						_buffering_presets_combo.set_active (2);
						break;
					case Custom:
						_playback.set_sensitive (true);
						_capture.set_sensitive (true);
						_buffering_presets_combo.set_active (3);
						break;
				}
			}
			_playback.parameter_changed (p);
			_capture.parameter_changed (p);
		}

		void set_state_from_config ()
		{
			parameter_changed ("buffering-preset");
			_playback.set_state_from_config();
			_capture.set_state_from_config();
		}

		Gtk::Widget& tip_widget() { return _buffering_presets_combo; }

	private:

		void preset_changed ()
		{
			int index = _buffering_presets_combo.get_active_row_number ();
			if (index < 0) {
				return;
			}
			switch (index) {
				case 0:
					_rc_config->set_buffering_preset (Small);
					break;
				case 1:
					_rc_config->set_buffering_preset (Medium);
					break;
				case 2:
					_rc_config->set_buffering_preset (Large);
					break;
				case 3:
					_rc_config->set_buffering_preset (Custom);
					break;
				default:
					error << string_compose (_("programming error: unknown buffering preset string, index = %1"), index) << endmsg;
					break;
			}
		}

		RCConfiguration* _rc_config;
		Label         _label;
		HSliderOption _playback;
		HSliderOption _capture;
		ComboBoxText  _buffering_presets_combo;
};

class PortSelectOption : public OptionEditorComponent, public sigc::trackable {
public:
	PortSelectOption (RCConfiguration* c, SessionHandlePtr* shp, std::string const & tooltip, std::string const & parameter_name, std::string const & label, DataType dt, PortFlags pf)
		: _rc_config (c)
                , _shp (shp)
                , _label (label)
                , _ignore_change (false)
                , data_type (dt)
                , port_flags (pf)
	{
		_store = ListStore::create (_port_columns);
		_combo.set_model (_store);
		_combo.pack_start (_port_columns.short_name);

		set_tooltip (_combo, tooltip);

		_combo.signal_map ().connect (sigc::mem_fun (*this, &PortSelectOption::on_map));
		_combo.signal_unmap ().connect (sigc::mem_fun (*this, &PortSelectOption::on_unmap));
		_combo.signal_changed ().connect (sigc::mem_fun (*this, &PortSelectOption::port_changed));
	}

	void add_to_page (OptionEditorPage* p)
	{
		add_widgets_to_page (p, &_label, &_combo);
	}

	Gtk::Widget& tip_widget()
	{
		return _combo;
	}

	void parameter_changed (string const & p)
	{
		if (p == parameter_name) {
			update_selection ();
		}
	}

	void set_state_from_config ()
	{
		parameter_changed (parameter_name);
	}

protected:
	struct PortColumns : public Gtk::TreeModel::ColumnRecord {
		PortColumns() {
			add (short_name);
			add (full_name);
		}
		Gtk::TreeModelColumn<std::string> short_name;
		Gtk::TreeModelColumn<std::string> full_name;
	};

	RCConfiguration*  _rc_config;
	SessionHandlePtr* _shp;
	Label             _label;
	Gtk::ComboBox     _combo;
	bool              _ignore_change;
	std::string        parameter_name;
	ARDOUR::DataType   data_type;
	ARDOUR::PortFlags  port_flags;
	PortColumns       _port_columns;

	Glib::RefPtr<Gtk::ListStore> _store;
	PBD::ScopedConnectionList    _engine_connection;

	void on_map ()
	{
		AudioEngine::instance()->PortRegisteredOrUnregistered.connect (
				_engine_connection,
				invalidator (*this),
				boost::bind (&PortSelectOption::update_port_combo, this),
				gui_context());

		AudioEngine::instance()->PortPrettyNameChanged.connect (
				_engine_connection,
				invalidator (*this),
				boost::bind (&PortSelectOption::update_port_combo, this),
				gui_context());
	}

	void on_unmap ()
	{
		_engine_connection.drop_connections ();
	}


	void update_port_combo ()
	{
		vector<string> ports;
		ARDOUR::AudioEngine::instance()->get_ports ("", data_type, port_flags, ports);

		PBD::Unwinder<bool> uw (_ignore_change, true);
		_store->clear ();

		TreeModel::Row row;
		row = *_store->append ();
		row[_port_columns.full_name] = string();
		row[_port_columns.short_name] = _("Disconnected");

		for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
			row = *_store->append ();
			row[_port_columns.full_name] = *p;
			std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
			if (pn.empty ()) {
				pn = (*p).substr ((*p).find (':') + 1);
			}
			row[_port_columns.short_name] = pn;
		}

		update_selection ();
	}

	virtual void update_selection() = 0;
	virtual void port_changed () = 0;
};

class LTCPortSelectOption : public PortSelectOption
{
public:
	LTCPortSelectOption (RCConfiguration* c, SessionHandlePtr* shp)
		: PortSelectOption (c, shp,
		                       _("The LTC generator output will be auto-connected to this port when a session is loaded."),
		                       X_("ltc-output-port"),
		                       _("LTC Output Port:"),
		                       ARDOUR::DataType::AUDIO,
		                       ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal)) {
		/* cannot call from parent due to the method being pure virtual */
		update_port_combo ();
	}

	void port_changed ()
	{
		if (_ignore_change) {
			return;
		}
		TreeModel::iterator active = _combo.get_active ();
		string new_port = (*active)[_port_columns.full_name];
		_rc_config->set_ltc_output_port (new_port);

		if (!_shp->session()) {
			return;
		}
		std::shared_ptr<Port> ltc_port = _shp->session()->ltc_output_port ();
		if (!ltc_port) {
			return;
		}
		if (ltc_port->connected_to (new_port)) {
			return;
		}

		ltc_port->disconnect_all ();
		if (!new_port.empty()) {
			ltc_port->connect (new_port);
		}
	}

	void update_selection ()
	{
		int n;
		Gtk::TreeModel::Children children = _store->children();
		Gtk::TreeModel::Children::iterator i = children.begin();
		++i; /* skip "Disconnected" */

		std::string const& pn = _rc_config->get_ltc_output_port ();
		std::shared_ptr<Port> ltc_port;
		if (_shp->session()) {
			ltc_port = _shp->session()->ltc_output_port ();
		}

		PBD::Unwinder<bool> uw (_ignore_change, true);

		/* try match preference with available port-names */
		for (n = 1;  i != children.end(); ++i, ++n) {
			string port_name = (*i)[_port_columns.full_name];
			if (port_name == pn) {
				_combo.set_active (n);
				return;
			}
		}

		/* Set preference to current port connection
		 * (LTC is auto-connected at session load).
		 */
		if (ltc_port) {
			i = children.begin();
			++i; /* skip "Disconnected" */
			for (n = 1;  i != children.end(); ++i, ++n) {
				string port_name = (*i)[_port_columns.full_name];
				if (ltc_port->connected_to (port_name)) {
					_combo.set_active (n);
					return;
				}
			}
		}

		if (pn.empty ()) {
			_combo.set_active (0); /* disconnected */
		} else {
			/* The port is currently not available, retain preference */
			TreeModel::Row row = *_store->append ();
			row[_port_columns.full_name] = pn;
			row[_port_columns.short_name] = (pn).substr ((pn).find (':') + 1);
			_combo.set_active (n);
		}
	}

};

class TriggerPortSelectOption : public PortSelectOption
{
public:
	TriggerPortSelectOption (RCConfiguration* c, SessionHandlePtr* shp)
		: PortSelectOption (c, shp,
		                    _("TriggerBoxes will be connected to this port when it is set."),
		                    X_("default-trigger-input-port"),
		                    _("Default trigger input:"),
		                    ARDOUR::DataType::MIDI,
		                    ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal)) {
		/* cannot call from parent due to the method being pure virtual */
		update_port_combo ();
	}

	void port_changed ()
	{
		if (_ignore_change) {
			return;
		}
		TreeModel::iterator active = _combo.get_active ();
		string new_port = (*active)[_port_columns.full_name];
		_rc_config->set_default_trigger_input_port (new_port);
		/* everything that needs it will pick up the new port via ParameterChanged */
	}

	void update_selection ()
	{
		int n;
		Gtk::TreeModel::Children children = _store->children();
		Gtk::TreeModel::Children::iterator i = children.begin();
		++i; /* skip "Disconnected" */

		std::string const& pn = _rc_config->get_default_trigger_input_port ();
		std::shared_ptr<Port> port;

		PBD::Unwinder<bool> uw (_ignore_change, true);

		/* try match preference with available port-names */
		for (n = 1;  i != children.end(); ++i, ++n) {
			string port_name = (*i)[_port_columns.full_name];
			if (port_name == pn) {
				_combo.set_active (n);
				return;
			}
		}

		if (pn.empty ()) {
			_combo.set_active (0); /* disconnected */
		} else {
			/* The port is currently not available, retain preference */
			TreeModel::Row row = *_store->append ();
			row[_port_columns.full_name] = pn;
			row[_port_columns.short_name] = (pn).substr ((pn).find (':') + 1);
			_combo.set_active (n);
		}
	}

};

class ControlSurfacesOptions : public OptionEditorMiniPage
{
	public:
		ControlSurfacesOptions ()
			: _ignore_view_change (0)
		{
			_store = ListStore::create (_model);
			_view.set_model (_store);
			_view.append_column_editable (_("Enable"), _model.enabled);
			_view.append_column (_("Control Surface Protocol"), _model.name);
			_view.get_column(1)->set_resizable (true);
			_view.get_column(1)->set_expand (true);

			Gtk::HBox* edit_box = manage (new Gtk::HBox);
			edit_box->set_spacing(3);
			edit_box->show ();

			Label* label = manage (new Label);
			label->set_text (_("Edit the settings for selected protocol (it must be ENABLED first):"));
			edit_box->pack_start (*label, false, false);
			label->show ();

			edit_button = manage (new Button(_("Show Protocol Settings")));
			edit_button->signal_clicked().connect (sigc::mem_fun(*this, &ControlSurfacesOptions::edit_btn_clicked));
			edit_box->pack_start (*edit_button, true, true);
			edit_button->set_sensitive (false);
			edit_button->show ();

			int const n = table.property_n_rows();
			table.resize (n + 2, 3);
			table.attach (_view, 0, 3, n, n + 1);
			table.attach (*edit_box, 0, 3, n + 1, n + 2);

			ControlProtocolManager& m = ControlProtocolManager::instance ();
			m.ProtocolStatusChange.connect (protocol_status_connection, MISSING_INVALIDATOR,
					boost::bind (&ControlSurfacesOptions::protocol_status_changed, this, _1), gui_context());

			_store->signal_row_changed().connect (sigc::mem_fun (*this, &ControlSurfacesOptions::view_changed));
			_view.signal_button_press_event().connect_notify (sigc::mem_fun(*this, &ControlSurfacesOptions::edit_clicked));
			_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ControlSurfacesOptions::selection_changed));
		}

		void parameter_changed (std::string const &)
		{

		}

		void set_state_from_config ()
		{
			_store->clear ();

			ControlProtocolManager& m = ControlProtocolManager::instance ();
			for (auto const& i : m.control_protocol_info) {
				TreeModel::Row r = *_store->append ();
				r[_model.name] = i->name;
				r[_model.enabled] = 0 != i->protocol;
				r[_model.protocol_info] = i;
			}
		}

	private:

		void protocol_status_changed (ControlProtocolInfo* cpi) {
			/* find the row */
			TreeModel::Children rows = _store->children();

			for (TreeModel::Children::iterator x = rows.begin(); x != rows.end(); ++x) {
				string n = ((*x)[_model.name]);

				if ((*x)[_model.protocol_info] == cpi) {
					_ignore_view_change++;
					(*x)[_model.enabled] = 0 != cpi->protocol;
					_ignore_view_change--;
					selection_changed (); // update sensitivity
					break;
				}
			}
		}

		void selection_changed ()
		{
			//enable the Edit button when a row is selected for editing
			TreeModel::Row row = *(_view.get_selection()->get_selected());
			if (row && row[_model.enabled]) {
				ControlProtocolInfo* cpi = row[_model.protocol_info];
				edit_button->set_sensitive (cpi && cpi->protocol && cpi->protocol->has_editor ());
			} else {
				edit_button->set_sensitive (false);
			}
		}

		void view_changed (TreeModel::Path const &, TreeModel::iterator const & i)
		{
			TreeModel::Row r = *i;

			if (_ignore_view_change) {
				return;
			}

			ControlProtocolInfo* cpi = r[_model.protocol_info];
			if (!cpi) {
				return;
			}

			bool const was_enabled = (cpi->protocol != 0);
			bool const is_enabled = r[_model.enabled];


			if (was_enabled != is_enabled) {

				if (!was_enabled) {
					ControlProtocolManager::instance().activate (*cpi);
				} else {
					ControlProtocolManager::instance().deactivate (*cpi);
				}
			}

			selection_changed ();
		}

		void edit_btn_clicked ()
		{
			std::string name;
			ControlProtocolInfo* cpi;
			TreeModel::Row row;

			row = *(_view.get_selection()->get_selected());
			if (!row[_model.enabled]) {
				return;
			}
			cpi = row[_model.protocol_info];
			if (!cpi || !cpi->protocol || !cpi->protocol->has_editor ()) {
				return;
			}
			Box* box = (Box*) cpi->protocol->get_gui ();
			if (!box) {
				return;
			}
			if (box->get_parent()) {
				static_cast<ArdourWindow*>(box->get_parent())->present();
				return;
			}
			WindowTitle title (Glib::get_application_name());
			title += row[_model.name];
			title += _("Configuration");
			/* once created, the window is managed by the surface itself (as ->get_parent())
			 * Surface's tear_down_gui() is called on session close, when de-activating
			 * or re-initializing a surface.
			 * tear_down_gui() hides an deletes the Window if it exists.
			 */
			ArdourWindow* win = new ArdourWindow (*((Gtk::Window*) _view.get_toplevel()), title.get_string());
			win->set_title (_("Control Protocol Settings"));
			win->add (*box);
			box->show ();
			win->present ();
		}

		void edit_clicked (GdkEventButton* ev)
		{
			if (ev->type != GDK_2BUTTON_PRESS) {
				return;
			}

			edit_btn_clicked();
		}

		class ControlSurfacesModelColumns : public TreeModelColumnRecord
	{
		public:

			ControlSurfacesModelColumns ()
			{
				add (name);
				add (enabled);
				add (protocol_info);
			}

			TreeModelColumn<string> name;
			TreeModelColumn<bool> enabled;
			TreeModelColumn<ControlProtocolInfo*> protocol_info;
	};

		Glib::RefPtr<ListStore> _store;
		ControlSurfacesModelColumns _model;
		TreeView _view;
		PBD::ScopedConnection protocol_status_connection;
		uint32_t _ignore_view_change;
		Gtk::Button* edit_button;
};

class VideoTimelineOptions : public OptionEditorMiniPage
{
	public:
		VideoTimelineOptions (RCConfiguration* c)
			: _rc_config (c)
			, _show_video_server_dialog_button (_("Show Video Server Startup Dialog"))
			, _video_advanced_setup_button (_("Advanced Setup (remote video server)"))
			, _xjadeo_browse_button (_("Browse..."))
		{
			Table* t = &table;
			int n = table.property_n_rows();

			t->attach (_show_video_server_dialog_button, 1, 4, n, n + 1);
			_show_video_server_dialog_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::show_video_server_dialog_toggled));
			Gtkmm2ext::UI::instance()->set_tip (_show_video_server_dialog_button,
					_("<b>When enabled</b> the video server is never launched automatically without confirmation"));
			++n;

			t->attach (_video_advanced_setup_button, 1, 4, n, n + 1, FILL);
			_video_advanced_setup_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::video_advanced_setup_toggled));
			Gtkmm2ext::UI::instance()->set_tip (_video_advanced_setup_button,
					_("<b>When enabled</b> you can specify a custom video-server URL and docroot. - Do not enable this option unless you know what you are doing."));
			++n;

			Label* l = manage (new Label (_("Video Server URL:")));
			l->set_alignment (0, 0.5);
			t->attach (*l, 1, 2, n, n + 1, FILL);
			t->attach (_video_server_url_entry, 2, 4, n, n + 1, FILL);
			Gtkmm2ext::UI::instance()->set_tip (_video_server_url_entry,
					_("Base URL of the video-server including http prefix. This is usually 'http://hostname.example.org:1554/' and defaults to 'http://localhost:1554/' when the video-server is running locally"));
			++n;

			l = manage (new Label (_("Video Folder:")));
			l->set_alignment (0, 0.5);
			t->attach (*l, 1, 2, n, n + 1, FILL);
			t->attach (_video_server_docroot_entry, 2, 4, n, n + 1);
			Gtkmm2ext::UI::instance()->set_tip (_video_server_docroot_entry,
					_("Local path to the video-server document-root. Only files below this directory will be accessible by the video-server. If the server run on a remote host, it should point to a network mounted folder of the server's docroot or be left empty if it is unavailable. It is used for the local video-monitor and file-browsing when opening/adding a video file."));
			++n;

			l = manage (new Label (""));
			t->attach (*l, 0, 4, n, n + 1, EXPAND | FILL);
			++n;

			l = manage (new Label (string_compose ("<b>%1</b>", _("Video Monitor"))));
			l->set_use_markup (true);
			l->set_alignment (0, 0.5);
			t->attach (*l, 0, 4, n, n + 1, EXPAND | FILL);
			++n;

			l = manage (new Label (string_compose (_("Custom Path to Video Monitor (%1) - leave empty for default:"),
#ifdef __APPLE__
							"Jadeo.app"
#elif defined PLATFORM_WINDOWS
							"xjadeo.exe"
#else
							"xjadeo"
#endif
							)));
			l->set_alignment (0, 0.5);
			t->attach (*l, 1, 4, n, n + 1, FILL);
			++n;

			t->attach (_custom_xjadeo_path, 2, 3, n, n + 1, EXPAND|FILL);
			Gtkmm2ext::UI::instance()->set_tip (_custom_xjadeo_path, _("Set a custom path to the Video Monitor Executable, changing this requires a restart."));
			t->attach (_xjadeo_browse_button, 3, 4, n, n + 1, FILL);

			_video_server_url_entry.signal_changed().connect (sigc::mem_fun(*this, &VideoTimelineOptions::server_url_changed));
			_video_server_url_entry.signal_activate().connect (sigc::mem_fun(*this, &VideoTimelineOptions::server_url_changed));
			_video_server_docroot_entry.signal_changed().connect (sigc::mem_fun(*this, &VideoTimelineOptions::server_docroot_changed));
			_video_server_docroot_entry.signal_activate().connect (sigc::mem_fun(*this, &VideoTimelineOptions::server_docroot_changed));
			_custom_xjadeo_path.signal_changed().connect (sigc::mem_fun (*this, &VideoTimelineOptions::custom_xjadeo_path_changed));
			_xjadeo_browse_button.signal_clicked ().connect (sigc::mem_fun (*this, &VideoTimelineOptions::xjadeo_browse_clicked));
		}

		void server_url_changed ()
		{
			_rc_config->set_video_server_url (_video_server_url_entry.get_text());
		}

		void server_docroot_changed ()
		{
			_rc_config->set_video_server_docroot (_video_server_docroot_entry.get_text());
		}

		void show_video_server_dialog_toggled ()
		{
			bool const x = _show_video_server_dialog_button.get_active ();
			_rc_config->set_show_video_server_dialog (x);
		}

		void video_advanced_setup_toggled ()
		{
			bool const x = _video_advanced_setup_button.get_active ();
			_rc_config->set_video_advanced_setup(x);
		}

		void custom_xjadeo_path_changed ()
		{
			_rc_config->set_xjadeo_binary (_custom_xjadeo_path.get_text());
		}

		void xjadeo_browse_clicked ()
		{
			Gtk::FileChooserDialog dialog(_("Set Video Monitor Executable"), Gtk::FILE_CHOOSER_ACTION_OPEN);
			dialog.set_filename (_rc_config->get_xjadeo_binary());
			dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
			dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
			if (dialog.run () == Gtk::RESPONSE_OK) {
				const std::string& filename = dialog.get_filename();
				if (!filename.empty() && (
#ifdef __APPLE__
							Glib::file_test (filename + "/Contents/MacOS/xjadeo", Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_EXECUTABLE) ||
#endif
							Glib::file_test (filename, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_EXECUTABLE)
							)) {
					_rc_config->set_xjadeo_binary (filename);
				}
			}
		}

		void parameter_changed (string const & p)
		{
			if (p == "video-server-url") {
				_video_server_url_entry.set_text (_rc_config->get_video_server_url());
			} else if (p == "video-server-docroot") {
				_video_server_docroot_entry.set_text (_rc_config->get_video_server_docroot());
			} else if (p == "show-video-server-dialog") {
				bool const x = _rc_config->get_show_video_server_dialog();
				_show_video_server_dialog_button.set_active (x);
			} else if (p == "video-advanced-setup") {
				bool const x = _rc_config->get_video_advanced_setup();
				_video_advanced_setup_button.set_active(x);
				_video_server_docroot_entry.set_sensitive(x);
				_video_server_url_entry.set_sensitive(x);
			} else if (p == "xjadeo-binary") {
				_custom_xjadeo_path.set_text (_rc_config->get_xjadeo_binary());
			}
		}

		void set_state_from_config ()
		{
			parameter_changed ("video-server-url");
			parameter_changed ("video-server-docroot");
			parameter_changed ("video-monitor-setup-dialog");
			parameter_changed ("show-video-server-dialog");
			parameter_changed ("video-advanced-setup");
			parameter_changed ("xjadeo-binary");
		}

	private:
		RCConfiguration* _rc_config;
		Entry _video_server_url_entry;
		Entry _video_server_docroot_entry;
		Entry _custom_xjadeo_path;
		CheckButton _show_video_server_dialog_button;
		CheckButton _video_advanced_setup_button;
		Button _xjadeo_browse_button;
};

class ColumVisibilityOption : public Option
{
	public:
	ColumVisibilityOption (string id, string name, uint32_t n_col, sigc::slot<uint32_t> get, sigc::slot<bool, uint32_t> set)
		: Option (id, name)
		, _heading (name)
		, _n_col (n_col)
		, _get (get)
		, _set (set)
	{
		cb = (CheckButton**) malloc (sizeof (CheckButton*) * n_col);
		for (uint32_t i = 0; i < n_col; ++i) {
			CheckButton* col = manage (new CheckButton (string_compose (_("Column %1 (Actions %2 + %3)"), i + 1, i * 2 + 1, i * 2 + 2)));
			col->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &ColumVisibilityOption::column_toggled), i));
			_vbox.pack_start (*col);
			cb[i] = col;
		}
		parameter_changed (id);
	}

	~ColumVisibilityOption () {
		free (cb);
	}

	Gtk::Widget& tip_widget() { return _vbox; }

	void set_state_from_config ()
	{
		uint32_t c = _get();
		for (uint32_t i = 0; i < _n_col; ++i) {
			bool en = (c & (1<<i)) ? true : false;
			if (cb[i]->get_active () != en) {
				cb[i]->set_active (en);
			}
		}
	}

	void add_to_page (OptionEditorPage* p)
	{
		_heading.add_to_page (p);
		add_widget_to_page (p, &_vbox);
	}
	private:

	void column_toggled (int b) {
		uint32_t c = _get();
		uint32_t cc = c;
		if (cb[b]->get_active ()) {
			c |= (1<<b);
		} else {
			c &= ~(1<<b);
		}
		if (cc != c) {
			_set (c);
		}
	}

	VBox _vbox;
	OptionEditorHeading _heading;

	CheckButton** cb;
	uint32_t _n_col;
	sigc::slot<uint32_t> _get;
	sigc::slot<bool, uint32_t> _set;
};


/** A class which allows control of visibility of some editor components using
 *  a VisibilityGroup.  The caller should pass in a `dummy' VisibilityGroup
 *  which has the correct members, but with null widget pointers.  This
 *  class allows the user to set visibility of the members, the details
 *  of which are stored in a configuration variable which can be watched
 *  by parts of the editor that actually contain the widgets whose visibility
 *  is being controlled.
 */

class VisibilityOption : public Option
{
public:
	/** @param name User-visible name for this group.
	 *  @param g `Dummy' VisibilityGroup (as described above).
	 *  @param get Method to get the value of the appropriate configuration variable.
	 *  @param set Method to set the value of the appropriate configuration variable.
	 */
	VisibilityOption (string name, VisibilityGroup* g, sigc::slot<string> get, sigc::slot<bool, string> set)
		: Option (g->get_state_name(), name)
		, _heading (name)
		, _visibility_group (g)
		, _get (get)
		, _set (set)
	{
		/* Watch for changes made by the user to our members */
		_visibility_group->VisibilityChanged.connect_same_thread (
			_visibility_group_connection, sigc::bind (&VisibilityOption::changed, this)
			);
	}

	void set_state_from_config ()
	{
		/* Set our state from the current configuration */
		_visibility_group->set_state (_get ());
	}

	void add_to_page (OptionEditorPage* p)
	{
		_heading.add_to_page (p);
		add_widget_to_page (p, _visibility_group->list_view ());
	}

	Gtk::Widget& tip_widget() { return *_visibility_group->list_view (); }

private:
	void changed ()
	{
		/* The user has changed something, so reflect this change
		   in the RCConfiguration.
		*/
		_set (_visibility_group->get_state_value ());
	}

	OptionEditorHeading _heading;
	VisibilityGroup* _visibility_group;
	sigc::slot<std::string> _get;
	sigc::slot<bool, std::string> _set;
	PBD::ScopedConnection _visibility_group_connection;
};


class MidiPortOptions : public OptionEditorMiniPage, public sigc::trackable
{
	public:
		MidiPortOptions()
			: input_heading (_("MIDI Inputs"))
			, output_heading (_("MIDI Outputs"))
			, no_input_label (_("No MIDI Input (capture) devices found."), Gtk::ALIGN_START)
			, no_output_label (_("No MIDI Output (playback) devices found."), Gtk::ALIGN_START)
		{
			setup_midi_port_view (midi_output_view, false);
			setup_midi_port_view (midi_input_view, true);

			input_heading.add_to_page (this);

			input_scroller.add (midi_input_view);
			input_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
			input_scroller.set_size_request (-1, 180);
			input_scroller.show ();

			int n = table.property_n_rows();
			table.attach (input_scroller, 0, 3, n, n + 1, FILL | EXPAND);
			table.attach (no_input_label, 1, 3, n + 1, n + 2, FILL | EXPAND);

			output_heading.add_to_page (this);

			output_scroller.add (midi_output_view);
			output_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
			output_scroller.set_size_request (-1, 180);
			output_scroller.show ();

			n = table.property_n_rows();
			table.attach (output_scroller, 0, 3, n, n + 1, FILL | EXPAND);
			table.attach (no_output_label, 1, 3, n + 1, n + 2, FILL | EXPAND);

			midi_output_view.show ();
			midi_input_view.show ();

			table.signal_map().connect (sigc::mem_fun (*this, &MidiPortOptions::on_map));
			table.signal_unmap().connect (sigc::mem_fun (*this, &MidiPortOptions::on_unmap));
		}

		void parameter_changed (string const&) {}
		void set_state_from_config() {}

		void on_map () {
			refill ();

			AudioEngine::instance()->PortRegisteredOrUnregistered.connect (connections,
					invalidator (*this),
					boost::bind (&MidiPortOptions::refill, this),
					gui_context());
			AudioEngine::instance()->MidiPortInfoChanged.connect (connections,
					invalidator (*this),
					boost::bind (&MidiPortOptions::refill, this),
					gui_context());
			AudioEngine::instance()->MidiSelectionPortsChanged.connect (connections,
					invalidator (*this),
					boost::bind (&MidiPortOptions::refill, this),
					gui_context());
		}

		void on_unmap () {
			connections.drop_connections ();
		}

		void refill () {
			if (refill_midi_ports (true, midi_input_view)) {
				no_input_label.hide ();
				input_scroller.show ();
			} else {
				no_input_label.show ();
				input_scroller.hide ();
			}
			if (refill_midi_ports (false, midi_output_view)) {
				no_output_label.hide ();
				output_scroller.show ();
			} else {
				no_output_label.show ();
				output_scroller.hide ();
			}
		}

	private:
		PBD::ScopedConnectionList connections;

		/* MIDI port management */
		struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord {

			MidiPortColumns () {
				add (pretty_name);
				add (music_data);
				add (control_data);
				add (selection);
				add (fullname);
				add (shortname);
				add (filler);
			}

			Gtk::TreeModelColumn<std::string> pretty_name;
			Gtk::TreeModelColumn<bool> music_data;
			Gtk::TreeModelColumn<bool> control_data;
			Gtk::TreeModelColumn<bool> selection;
			Gtk::TreeModelColumn<std::string> fullname;
			Gtk::TreeModelColumn<std::string> shortname;
			Gtk::TreeModelColumn<std::string> filler;
		};

		MidiPortColumns midi_port_columns;
		Gtk::TreeView midi_input_view;
		Gtk::TreeView midi_output_view;

		Gtk::ScrolledWindow input_scroller;
		Gtk::ScrolledWindow output_scroller;
		OptionEditorHeading input_heading;
		OptionEditorHeading output_heading;

		Gtk::Label no_input_label;
		Gtk::Label no_output_label;

		void setup_midi_port_view (Gtk::TreeView&, bool with_selection);
		bool refill_midi_ports (bool for_input, Gtk::TreeView&);
		void pretty_name_edit (std::string const & path, std::string const & new_text, Gtk::TreeView*);
		void midi_music_column_toggled (std::string const & path, Gtk::TreeView*);
		void midi_control_column_toggled (std::string const & path, Gtk::TreeView*);
		void midi_selection_column_toggled (std::string const & path, Gtk::TreeView*);
};

void
MidiPortOptions::setup_midi_port_view (Gtk::TreeView& view, bool with_selection)
{
	int pretty_name_column;
	int music_column;
	int control_column;
	int selection_column = 0;
	TreeViewColumn* col;
	Gtk::Label* l;

	pretty_name_column = view.append_column_editable (_("Name (click twice to edit)"), midi_port_columns.pretty_name) - 1;

	col = manage (new TreeViewColumn ("", midi_port_columns.music_data));
	col->set_alignment (ALIGN_CENTER);
	l = manage (new Label (_("Music Data")));
	set_tooltip (*l, string_compose (_("If ticked, %1 will consider this port to be a source of music performance data."), PROGRAM_NAME));
	col->set_widget (*l);
	l->show ();
	music_column = view.append_column (*col) - 1;

	col = manage (new TreeViewColumn ("", midi_port_columns.control_data));
	col->set_alignment (ALIGN_CENTER);
	l = manage (new Label (_("Control Data")));
	set_tooltip (*l, string_compose (_("If ticked, %1 will consider this port to be a source of control data."), PROGRAM_NAME));
	col->set_widget (*l);
	l->show ();
	control_column = view.append_column (*col) - 1;

	if (with_selection) {
		col = manage (new TreeViewColumn (_("Follow Selection"), midi_port_columns.selection));
		selection_column = view.append_column (*col) - 1;
		l = manage (new Label (_("Follow Selection")));
		set_tooltip (*l, string_compose (_("If ticked, and \"MIDI input follows selection\" is enabled,\n%1 will automatically connect the first selected MIDI track to this port.\n"), PROGRAM_NAME));
		col->set_widget (*l);
		l->show ();
	}

	/* filler column so that the last real column doesn't expand */
	view.append_column ("", midi_port_columns.filler);

	CellRendererText* pretty_name_cell = dynamic_cast<CellRendererText*> (view.get_column_cell_renderer (pretty_name_column));
	pretty_name_cell->property_editable() = true;
	pretty_name_cell->signal_edited().connect (sigc::bind (sigc::mem_fun (*this, &MidiPortOptions::pretty_name_edit), &view));

	CellRendererToggle* toggle_cell;

	toggle_cell = dynamic_cast<CellRendererToggle*> (view.get_column_cell_renderer (music_column));
	toggle_cell->property_activatable() = true;
	toggle_cell->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiPortOptions::midi_music_column_toggled), &view));

	toggle_cell = dynamic_cast<CellRendererToggle*> (view.get_column_cell_renderer (control_column));
	toggle_cell->property_activatable() = true;
	toggle_cell->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiPortOptions::midi_control_column_toggled), &view));

	if (with_selection) {
		toggle_cell = dynamic_cast<CellRendererToggle*> (view.get_column_cell_renderer (selection_column));
		toggle_cell->property_activatable() = true;
		toggle_cell->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiPortOptions::midi_selection_column_toggled), &view));
	}

	view.get_selection()->set_mode (SELECTION_SINGLE);
	view.set_tooltip_column (5); /* port short name */
	view.get_column(0)->set_resizable (true);
	view.get_column(0)->set_expand (true);
}

bool
MidiPortOptions::refill_midi_ports (bool for_input, Gtk::TreeView& view)
{
	using namespace ARDOUR;

	std::vector<string> ports;
	AudioEngine::instance()->get_configurable_midi_ports (ports, for_input);

	if (ports.empty()) {
		view.hide ();
		return false;
	}

	view.unset_model ();

	Glib::RefPtr<ListStore> model = Gtk::ListStore::create (midi_port_columns);

	for (vector<string>::const_iterator s = ports.begin(); s != ports.end(); ++s) {

		TreeModel::Row row  = *(model->append());
		MidiPortFlags flags = AudioEngine::instance()->midi_port_metadata (*s);
		std::string   pn    = AudioEngine::instance()->get_pretty_name_by_name (*s);

		row[midi_port_columns.music_data]   = flags & MidiPortMusic;
		row[midi_port_columns.control_data] = flags & MidiPortControl;
		row[midi_port_columns.selection]    = flags & MidiPortSelection;
		row[midi_port_columns.fullname]     = *s;
		row[midi_port_columns.shortname]    = AudioEngine::instance()->short_port_name_from_port_name (*s);
		row[midi_port_columns.pretty_name]  = pn.empty () ? row[midi_port_columns.shortname] : pn;
	}

	view.set_model (model);
	view.show ();

	return true;
}

void
MidiPortOptions::midi_music_column_toggled (string const & path, TreeView* view)
{
	TreeIter iter = view->get_model()->get_iter (path);

	if (!iter) {
		return;
	}

	bool new_value = ! bool ((*iter)[midi_port_columns.music_data]);

	/* don't reset model - wait for MidiPortInfoChanged signal */

	if (new_value) {
		ARDOUR::AudioEngine::instance()->add_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortMusic);
	} else {
		ARDOUR::AudioEngine::instance()->remove_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortMusic);
	}
}

void
MidiPortOptions::midi_control_column_toggled (string const & path, TreeView* view)
{
	TreeIter iter = view->get_model()->get_iter (path);

	if (!iter) {
		return;
	}

	bool new_value = ! bool ((*iter)[midi_port_columns.control_data]);

	/* don't reset model - wait for MidiPortInfoChanged signal */

	if (new_value) {
		ARDOUR::AudioEngine::instance()->add_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortControl);
	} else {
		ARDOUR::AudioEngine::instance()->remove_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortControl);
	}
}

void
MidiPortOptions::midi_selection_column_toggled (string const & path, TreeView* view)
{
	TreeIter iter = view->get_model()->get_iter (path);

	if (!iter) {
		return;
	}

	bool new_value = ! bool ((*iter)[midi_port_columns.selection]);

	/* don't reset model - wait for MidiSelectionPortsChanged signal */

	if (new_value) {
		ARDOUR::AudioEngine::instance()->add_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortSelection);
	} else {
		ARDOUR::AudioEngine::instance()->remove_midi_port_flags ((*iter)[midi_port_columns.fullname], MidiPortSelection);
	}
}

void
MidiPortOptions::pretty_name_edit (std::string const & path, string const & new_text, Gtk::TreeView* view)
{
	TreeIter iter = view->get_model()->get_iter (path);

	if (!iter) {
		return;
	}

	AudioEngine::instance()->set_port_pretty_name ((*iter)[midi_port_columns.fullname], new_text);
}



RCOptionEditor::RCOptionEditor ()
	: OptionEditorContainer (Config)
	  /* pack self-as-vbox into tabbable */
	, Tabbable (*this, _("Preferences"), X_("preferences"), /* detached by default */ false)
	, _rc_config (Config)
	, _mixer_strip_visibility ("mixer-element-visibility")
	, _cairo_image_surface (0)
{
	UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RCOptionEditor::parameter_changed));
	BoolOption* bo;

	/* GENERAL *****************************************************************/

	add_option (_("General"), new OptionEditorHeading (_("Audio/MIDI Setup")));

	add_option (_("General"),
			new RcActionButton (_("Show Audio/MIDI Setup Window"),
				sigc::mem_fun (*this, &RCOptionEditor::show_audio_setup)));

	bo = new BoolOption (
		     "try-autostart-engine",
		     _("Try to auto-launch audio/midi engine"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_try_autostart_engine),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_try_autostart_engine)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("When opening an existing session, if the most recent audio engine is available and can open the session's sample rate, the audio engine dialog may be skipped."));
	add_option (_("General"), bo );

#if 0
	/* "preferred-time-domain"
	   unlike other preferences, this variable would not actually 'change' anything; the session's domain is stored in session-config
	   so we don't show the rc-config value here.  instead, it is invisibly stored in the rc-config, with the user's most recent selection */
#endif

	add_option (_("General"), new OptionEditorHeading (S_("Options|Editor Undo")));

	add_option (_("General"), new UndoOptions (_rc_config));

	add_option (_("General"),
	     new BoolOption (
		     "verify-remove-last-capture",
		     _("Verify removal of last capture"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_verify_remove_last_capture),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_verify_remove_last_capture)
		     ));

	add_option (_("General"), new OptionEditorHeading (_("Session Management")));

	add_option (_("General"),
	     new BoolOption (
		     "periodic-safety-backups",
		     _("Make periodic backups of the session file"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_periodic_safety_backups),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_periodic_safety_backups)
		     ));

	add_option (_("General"), new DirectoryOption (
			    X_("default-session-parent-dir"),
			    _("Default folder for new sessions:"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_default_session_parent_dir),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_default_session_parent_dir)
			    ));

	add_option (_("General"),
	     new SpinOption<uint32_t> (
		     "max-recent-sessions",
		     _("Maximum number of recent sessions"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_max_recent_sessions),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_max_recent_sessions),
		     0, 1000, 1, 20
		     ));

	add_option (_("General"), new OptionEditorHeading (_("Import")));

	add_option (_("General"),
	     new BoolOption (
		     "only-copy-imported-files",
		     _("Drag and drop import always copies files to session"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_only_copy_imported_files),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_only_copy_imported_files)
		     ));


	add_option (_("General"),
			new DirectoryOption (
				X_("freesound-dir"),
				_("Cache Folder for downloaded Freesound clips:"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_freesound_dir),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_freesound_dir)
				));

	add_option (_("General"), new OptionEditorHeading (_("Export")));

	add_option (_("General"),
	     new BoolOption (
		     "save-export-analysis-image",
		     _("Save loudness analysis as image file after export"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_save_export_analysis_image),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_save_export_analysis_image)
		     ));

	add_option (_("General"),
	     new BoolOption (
		     "save-export-mixer-screenshot",
		     _("Save Mixer screenshot after export"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_save_export_mixer_screenshot),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_save_export_mixer_screenshot)
		     ));

#if defined PHONE_HOME && !defined MIXBUS
	add_option (_("General"), new OptionEditorHeading (_("New Version Check")));
	bo = new BoolOption (
		     "check-announcements",
		     _("Check for announcements at application start"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_check_announcements),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_check_announcements)
		     );
	bo ->set_note (string_compose (_("An anonymous request is performed to query announcements by contacting\n%1"), Config->get_pingback_url ()));
	add_option (_("General"), bo);

#endif

	/* APPEARANCE ***************************************************************/

	if (!ARDOUR::Profile->get_mixbus()) {
		add_option (_("Appearance"), new OptionEditorHeading (_("GUI Lock")));
		/* Lock GUI timeout */

		HSliderOption *slts = new HSliderOption("lock-gui-after-seconds",
				_("Lock timeout (seconds)"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_lock_gui_after_seconds),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_lock_gui_after_seconds),
				0, 1000, 1, 10
				);
		slts->scale().set_digits (0);
		Gtkmm2ext::UI::instance()->set_tip (
				slts->tip_widget(),
				_("Lock GUI after this many idle seconds (zero to never lock)"));
		add_option (_("Appearance"), slts);

		ComboOption<ScreenSaverMode>* scsvr = new ComboOption<ScreenSaverMode> (
				"screen-saver-mode",
				_("System Screensaver Mode"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_screen_saver_mode),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_screen_saver_mode)
				);

		scsvr->add (InhibitNever, _("Never Inhibit"));
		scsvr->add (InhibitWhileRecording, _("Inhibit while Recording"));
		scsvr->add (InhibitAlways, string_compose (_("Inhibit while %1 is running"), PROGRAM_NAME));

		add_option (_("Appearance"), scsvr);


	} // !mixbus

	add_option (_("Appearance"), new OptionEditorHeading (_("Theme")));

	add_option (_("Appearance"), new BoolOption (
		            "use-route-color-widely",
		            _("Color faders with track/bus colors"),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_route_color_widely),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_route_color_widely)
		            ));

	add_option (_("Appearance"), new BoolOption (
				"flat-buttons",
				_("Draw \"flat\" buttons"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_flat_buttons),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_flat_buttons)
				));

	add_option (_("Appearance"), new BoolOption (
				"boxy-buttons",
				_("Draw \"boxy\" buttons"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_boxy_buttons),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_boxy_buttons)
				));

	add_option (_("Appearance"), new BoolOption (
				"meter-style-led",
				_("LED meter style"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_style_led),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_style_led)
				));

	if (!Profile->get_mixbus()) {
		vector<string> icon_sets = ::get_icon_sets ();
		if (icon_sets.size() > 1) {
			ComboOption<std::string>* io = new ComboOption<std::string> (
					"icon-set", _("Icon Set"),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_icon_set),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_icon_set)
					);
			for (vector<string>::const_iterator i = icon_sets.begin (); i != icon_sets.end (); ++i) {
				io->add (*i, *i);
			}
			add_option (_("Appearance"), io);
		}
	}

	add_option (_("Appearance"), new OptionEditorHeading (_("Graphical User Interface")));

	add_option (_("Appearance"),
	     new BoolOption (
		     "widget-prelight",
		     _("Highlight widgets on mouseover"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_widget_prelight),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_widget_prelight)
		     ));

	add_option (_("Appearance"),
	     new BoolOption (
		     "use-tooltips",
		     _("Show tooltips if mouse hovers over a control"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_tooltips),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_tooltips)
		     ));

	bo = new BoolOption (
			"super-rapid-clock-update",
			_("Update clocks at TC Frame rate"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_super_rapid_clock_update),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_super_rapid_clock_update)
			);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> clock displays are updated every Timecode Frame (fps).\n\n"
				"<b>When disabled</b> clock displays are updated only every 100ms."
			 ));
	add_option (_("Appearance"), bo);

	add_option (_("Appearance"),
			new BoolOption (
				"blink-rec-arm",
				_("Blink Rec-Arm buttons"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_blink_rec_arm),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_blink_rec_arm)
				));

	add_option (_("Appearance"),
			new BoolOption (
				"blink-alert-indicators",
				_("Blink Alert Indicators"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_blink_alert_indicators),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_blink_alert_indicators)
				));

	add_option (_("Appearance/Recorder"), new OptionEditorHeading (_("Input Meter Layout")));

	ComboOption<InputMeterLayout>* iml = new ComboOption<InputMeterLayout> (
		"input-meter-layout",
		_("Input Meter Layout"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_input_meter_layout),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_input_meter_layout)
		);

	iml->add (LayoutAutomatic,  _("Automatic"));
	iml->add (LayoutHorizontal, _("Horizontal"));
	iml->add (LayoutVertical,   _("Vertical"));

	add_option (S_("Appearance/Recorder"), iml);


	add_option (_("Appearance/Editor"), new OptionEditorHeading (_("General")));
	add_option (_("Appearance/Editor"),
	     new BoolOption (
		     "show-name-highlight",
		     _("Use name highlight bars in region displays (requires a restart)"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_name_highlight),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_name_highlight)
		     ));

	add_option (_("Appearance/Editor"),
			new BoolOption (
			"color-regions-using-track-color",
			_("Region color follows track color"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_color_regions_using_track_color),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_color_regions_using_track_color)
			));

	add_option (_("Appearance/Editor"),
			new BoolOption (
			"show-region-names",
			_("Show Region Names"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_region_name),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_region_name)
			));

	add_option (_("Appearance/Editor"),
			new BoolOption (
			"show-selection-marker",
			_("Show Selection Marker"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_selection_marker),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_selection_marker)
			));


	HSliderOption *gui_hs;

	if (!Profile->get_mixbus()) {
		gui_hs = new HSliderOption(
				"waveform-gradient-depth",
				_("Waveforms color gradient depth"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_waveform_gradient_depth),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_waveform_gradient_depth),
				0, 1.0, 0.05
				);
		gui_hs->scale().set_update_policy (Gtk::UPDATE_DELAYED);
		add_option (_("Appearance/Editor"), gui_hs);
	}

	gui_hs = new HSliderOption(
			"timeline-item-gradient-depth",
			_("Timeline item gradient depth"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_timeline_item_gradient_depth),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_timeline_item_gradient_depth),
			0, 1.0, 0.05
			);
	gui_hs->scale().set_update_policy (Gtk::UPDATE_DELAYED);
	add_option (_("Appearance/Editor"), gui_hs);

	ComboOption<int>* emode = new ComboOption<int> (
			"time-axis-name-ellipsize-mode",
			_("Track name ellipsize mode"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_time_axis_name_ellipsize_mode),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_time_axis_name_ellipsize_mode)
		);
	emode->add (-1, _("Ellipsize start of name"));
	emode->add (0, _("Ellipsize middle of name"));
	emode->add (1, _("Ellipsize end of name"));

	Gtkmm2ext::UI::instance()->set_tip (emode->tip_widget(), _("Choose which part of long track names are hidden in the editor's track headers"));
	add_option (_("Appearance/Editor"), emode);

	ComboOption<uint32_t>* gap = new ComboOption<uint32_t> (
		     "vertical-region-gap",
		     _("Add a visual gap below Audio Regions"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_vertical_region_gap),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_vertical_region_gap)
		     );
	gap->add (0, _("None"));
	gap->add (2, _("Small"));
	gap->add (4, _("Large"));
	add_option (_("Appearance/Editor"), gap);

	add_option (_("Appearance/Editor"), new OptionEditorHeading (_("Editor Meters")));

	add_option (_("Appearance/Editor"),
	     new BoolOption (
		     "show-track-meters",
		     _("Show meters in track headers"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_track_meters),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_track_meters)
		     ));

	add_option (_("Appearance/Editor"),
	     new BoolOption (
		     "editor-stereo-only-meters",
		     _("Limit track header meters to stereo"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_editor_stereo_only_meters),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_editor_stereo_only_meters)
		     ));

	add_option (_("Appearance/Editor"), new OptionEditorHeading (_("MIDI Editing")));

	add_option (_("Appearance/Editor"),
		    new BoolOption (
			    "display-first-midi-bank-as-zero",
			    _("Display first MIDI bank/program as 0"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_first_midi_bank_is_zero),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_first_midi_bank_is_zero)
			    ));

	add_option (_("Appearance/Editor"),
	     new BoolOption (
		     "never-display-periodic-midi",
		     _("Don't display periodic (MTC, MMC) SysEx messages in MIDI Regions"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_never_display_periodic_midi),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_never_display_periodic_midi)
		     ));


	add_option (_("Appearance/Editor"),
	            new BoolOption (
		            "use-note-bars-for-velocity",
		            _("Show velocity horizontally inside notes"),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_note_bars_for_velocity),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_note_bars_for_velocity)
		            ));

	add_option (_("Appearance/Editor"),
	            new BoolOption (
		            "use-note-color-for-velocity",
		            _("Use colors to show note velocity"),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_note_color_for_velocity),
		            sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_note_color_for_velocity)
		            ));

	ComboOption<Editing::NoteNameDisplay>* nnd = new ComboOption<Editing::NoteNameDisplay> (
		"note-name-display",
		_("Display note names in MIDI track headers"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_note_name_display),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_note_name_display)
		);
	nnd->add (Editing::Always, _("Always"));
	nnd->add (Editing::WithMIDNAM, _("When Available"));
	nnd->add (Editing::Never, _("Never"));

	add_option (_("Appearance/Editor"), nnd);

	add_option (_("Appearance/Editor"), new OptionEditorBlank ());

	add_option (_("Appearance/Waveform"), new OptionEditorHeading (_("Editor Waveforms")));

	if (!Profile->get_mixbus()) {
		add_option (_("Appearance/Waveform"),
				new BoolOption (
					"show-waveforms",
					_("Show waveforms in regions"),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_waveforms),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_waveforms)
					));
	}  // !mixbus

	add_option (_("Appearance/Waveform"),
	     new BoolOption (
		     "show-waveforms-while-recording",
		     _("Show waveforms while recording"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_waveforms_while_recording),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_waveforms_while_recording)
		     ));

	add_option (_("Appearance/Waveform"),
			new BoolOption (
			"show-waveform-clipping",
			_("Show waveform clipping"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_waveform_clipping),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_waveform_clipping)
			));

	add_option (_("Appearance/Waveform"), new ClipLevelOptions ());

	ComboOption<WaveformScale>* wfs = new ComboOption<WaveformScale> (
		"waveform-scale",
		_("Waveform scale"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_waveform_scale),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_waveform_scale)
		);

	wfs->add (Linear, _("linear"));
	wfs->add (Logarithmic, _("logarithmic"));

	add_option (_("Appearance/Waveform"), wfs);

	ComboOption<WaveformShape>* wfsh = new ComboOption<WaveformShape> (
		"waveform-shape",
		_("Waveform shape"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_waveform_shape),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_waveform_shape)
		);

	wfsh->add (Traditional, _("traditional"));
	wfsh->add (Rectified, _("rectified"));

	add_option (_("Appearance/Waveform"), wfsh);
	add_option (_("Appearance/Waveform"), new OptionEditorBlank ());

	/* The names of these controls must be the same as those given in MixerStrip
	   for the actual widgets being controlled.
	*/
	_mixer_strip_visibility.add (0, X_("Input"), _("Input"));
	_mixer_strip_visibility.add (0, X_("PhaseInvert"), _("Phase Invert"));
	_mixer_strip_visibility.add (0, X_("RecMon"), _("Record & Monitor"));
	_mixer_strip_visibility.add (0, X_("SoloIsoLock"), _("Solo Iso / Lock"));
	_mixer_strip_visibility.add (0, X_("Output"), _("Output"));
	_mixer_strip_visibility.add (0, X_("Comments"), _("Comments"));
	_mixer_strip_visibility.add (0, X_("VCA"), _("VCA Assigns"));
	_mixer_strip_visibility.add (0, X_("TriggerGrid"), _("Trigger Grid"));
	_mixer_strip_visibility.add (0, X_("TriggerMaster"), _("Trigger Masters"));

#ifndef MIXBUS
	add_option (_("Appearance/Mixer"),
		new VisibilityOption (
			_("Mixer Strip"),
			&_mixer_strip_visibility,
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_mixer_strip_visibility),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_mixer_strip_visibility)
			)
		);
#else
	add_option (_("Appearance/Mixer"), new OptionEditorHeading (_("Mixer Strip")));
#endif

#ifndef MIXBUS32C
	add_option (_("Appearance/Mixer"),
	     new BoolOption (
		     "default-narrow_ms",
		     _("Use narrow strips in the mixer for new strips by default"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_default_narrow_ms),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_default_narrow_ms)
		     ));
#endif

	ComboOption<uint32_t>* mic = new ComboOption<uint32_t> (
		     "max-inline-controls",
		     _("Limit inline-mixer-strip controls per plugin"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_max_inline_controls),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_max_inline_controls)
		     );
	mic->add (0, _("Unlimited"));
	mic->add (16,  _("16 parameters"));
	mic->add (32,  _("32 parameters"));
	mic->add (64,  _("64 parameters"));
	mic->add (128, _("128 parameters"));
	add_option (_("Appearance/Mixer"), mic);

	add_option (_("Appearance/Mixer"), new OptionEditorBlank ());

	add_option (_("Appearance/Toolbar"), new OptionEditorHeading (_("Main Transport Toolbar Items")));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-toolbar-recpunch",
		     _("Display Record/Punch Options"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_toolbar_recpunch),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_toolbar_recpunch)
		     ));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-toolbar-latency",
		     _("Display Latency Compensation Info"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_toolbar_latency),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_toolbar_latency)
		     ));

	if (!ARDOUR::Profile->get_small_screen()) {
		add_option (_("Appearance/Toolbar"),
				new BoolOption (
					"show-secondary-clock",
					_("Display Secondary Clock"),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_secondary_clock),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_secondary_clock)
					));
	}

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-toolbar-selclock",
		     _("Display Selection Clock"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_toolbar_selclock),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_toolbar_selclock)
		     ));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-toolbar-monitor-info",
		     _("Display Monitor Section Info"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_toolbar_monitor_info),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_toolbar_monitor_info)
		     ));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-toolbar-cuectrl",
		     _("Display Cue Rec/Play Controls"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_toolbar_cuectrl),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_toolbar_cuectrl)
		     ));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-mini-timeline",
		     _("Display Navigation Timeline"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_mini_timeline),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_mini_timeline)
		     ));

	add_option (_("Appearance/Toolbar"),
	     new BoolOption (
		     "show-editor-meter",
		     _("Display Master Level Meter"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_editor_meter),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_editor_meter)
		     ));

	add_option (_("Appearance/Toolbar"),
			new ColumVisibilityOption (
				"action-table-columns", _("Display Action-Buttons"), MAX_LUA_ACTION_BUTTONS / 2,
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_action_table_columns),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_action_table_columns)
				)
			);
	add_option (_("Appearance/Toolbar"), new OptionEditorBlank ());

	/* size and scale */

#if (!defined __APPLE__ || defined MIXBUS)
	add_option (_("Appearance/Size and Scale"), new OptionEditorHeading (_("User Interface Size and Scale")));
#endif

#ifndef __APPLE__
	/* font scaling does nothing with GDK/Quartz */
	add_option (_("Appearance/Size and Scale"), new FontScalingOptions ());
#endif

	add_option (_("Appearance/Colors"), new OptionEditorHeading (_("Colors")));
	add_option (_("Appearance/Colors"), new ColorThemeManager);
	add_option (_("Appearance/Colors"), new OptionEditorBlank ());

	bo = new BoolOption (
		     "use-palette-for-new-route",
		     _("Use color-palette to assign color for new Tracks"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_palette_for_new_track),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_palette_for_new_track)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("<b>When enabled</b> new Tracks are assigned a color from the stripable-color-palette in round-robin fashion.\n"
				  "<b>When disabled</b> all new Tracks will use the FIRST color from the stripable-color-palette."
					));
	add_option (_("Appearance/Colors"), bo);

	bo = new BoolOption (
		     "use-palette-for-new-route",
		     _("Use color-palette to assign color for new Busses"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_palette_for_new_bus),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_palette_for_new_bus)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("<b>When enabled</b> new Buses are assigned a color from the stripable-color-palette in round-robin fashion.\n"
				  "<b>When disabled</b> all new Buses will use the FIRST color from the stripable-color-palette."
					));
	add_option (_("Appearance/Colors"), bo);


	bo = new BoolOption (
		     "use-palette-for-new-vca",
		     _("Use color-palette to assign color for new VCA"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_palette_for_new_vca),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_palette_for_new_vca)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("<b>When enabled</b> newly created VCAs are assigned a color from the stripable-color-palette in round-robin fashion.\n"
				  "<b>When disabled</b> all new VCAs will have a neutral color from the theme."
					));
	add_option (_("Appearance/Colors"), bo);

	/* Quirks */

	OptionEditorHeading* quirks_head = new OptionEditorHeading (_("Various Workarounds for Windowing Systems"));

	quirks_head->set_note (string_compose (_("Rules for closing, minimizing, maximizing, and stay-on-top can vary \
with each version of your OS, and the preferences that you've set in your OS.\n\n\
You can adjust the options, below, to change how application windows and dialogs behave.\n\n\
These settings will only take effect after %1 is restarted.\n\
	"), PROGRAM_NAME));

	add_option (_("Appearance/Quirks"), quirks_head);

	bo = new BoolOption (
		     "use-wm-visibility",
		     _("Use visibility information provided by your Window Manager/Desktop"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_wm_visibility),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_wm_visibility)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("If you have trouble toggling between hidden Editor and Mixer windows, try changing this setting."));
	add_option (_("Appearance/Quirks"), bo);

#ifndef __APPLE__

#ifndef PLATFORM_WINDOWS
	bo = new BoolOption (
			"hide-splash-screen",
			_("Show/Hide splash screen instead of setting z-axis stack order"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_hide_splash_screen),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_hide_splash_screen)
			);
	add_option (_("Appearance/Quirks"), bo);
#endif

	bo = new BoolOption (
			"all-floating-windows-are-dialogs",
			_("All floating windows are dialogs"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_all_floating_windows_are_dialogs),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_all_floating_windows_are_dialogs)
			);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
			_("Mark all floating windows to be type \"Dialog\" rather than using \"Utility\" for some.\nThis may help with some window managers."));
	add_option (_("Appearance/Quirks"), bo);

	bo = new BoolOption (
			"transients-follow-front",
			_("Transient windows follow front window."),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_transients_follow_front),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_transients_follow_front)
			);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("Make transient windows follow the front window when toggling between the editor and mixer."));
	add_option (_("Appearance/Quirks"), bo);
#endif

	if (!Profile->get_mixbus()) {
		bo = new BoolOption (
				"floating-monitor-section",
				_("Float detached monitor-section window"),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_floating_monitor_section),
				sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_floating_monitor_section)
				);
		Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
					_("When detaching the monitoring section, mark it as \"Utility\" window to stay in front."));
		add_option (_("Appearance/Quirks"), bo);
	}

#if !(defined PLATFORM_WINDOWS || defined __APPLE__)
	bo = new BoolOption (
			"allow-to-resize-engine-dialog",
			_("Allow to resize Engine Dialog"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_allow_to_resize_engine_dialog),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_allow_to_resize_engine_dialog)
			);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget (),
				_("On some XWayland systems the engine-dialog is blank when shown a second time (from the main menu). Allowing to resize the window works around this oddity."));
	add_option (_("Appearance/Quirks"), bo);
#endif

	add_option (_("Appearance/Quirks"), new OptionEditorBlank ());
#if (!defined USE_CAIRO_IMAGE_SURFACE || defined CAIRO_SUPPORTS_FORCE_BUGGY_GRADIENTS_ENVIRONMENT_VARIABLE || defined __APPLE__)
	add_option (_("Appearance"), new OptionEditorHeading (_("Graphics Acceleration")));
#endif

#ifdef __APPLE__
	ComboOption<AppleNSGLViewMode>* glmode = new ComboOption<AppleNSGLViewMode> (
		"nsgl-view-mode",
		_("Render Canvas on openGL texture (requires restart)"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_nsgl_view_mode),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_nsgl_view_mode)
		);
	glmode->add (NSGLHiRes, _("Yes, with Retina scaling"));
	glmode->add (NSGLLoRes, _("Yes, low resolution"));
	glmode->add (NSGLDisable, _("No"));

	Gtkmm2ext::UI::instance()->set_tip (glmode->tip_widget(), string_compose (
				_("Render editor canvas, on a openGL texture which may improve graphics performance.\nThis requires restarting %1 before having an effect"), PROGRAM_NAME));
	add_option (_("Appearance"), glmode);
#endif

#ifndef USE_CAIRO_IMAGE_SURFACE
	_cairo_image_surface = new BoolOption (
		"cairo-image-surface",
		_("Use intermediate image-surface to render canvas (requires restart)"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_cairo_image_surface),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_cairo_image_surface)
		);

	Gtkmm2ext::UI::instance()->set_tip (_cairo_image_surface->tip_widget(), string_compose (
				_("Render large parts of the application user-interface in software, instead of using 2D-graphics acceleration.\nThis requires restarting %1 before having an effect"), PROGRAM_NAME));
	add_option (_("Appearance"), _cairo_image_surface);
#endif

#ifdef CAIRO_SUPPORTS_FORCE_BUGGY_GRADIENTS_ENVIRONMENT_VARIABLE
	BoolOption* bgo = new BoolOption (
		"buggy-gradients",
		_("Possibly improve slow graphical performance (requires restart)"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_buggy_gradients),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_buggy_gradients)
		);

	Gtkmm2ext::UI::instance()->set_tip (bgo->tip_widget(), string_compose (_("Disables hardware gradient rendering on buggy video drivers (\"buggy gradients patch\").\nThis requires restarting %1 before having an effect"), PROGRAM_NAME));
	add_option (_("Appearance"), bgo);
#endif

#if ENABLE_NLS

	add_option (_("Appearance/Translation"), new OptionEditorHeading (_("Internationalization")));

	bo = new BoolOption (
			"enable-translation",
			_("Use translations"),
			sigc::ptr_fun (ARDOUR::translations_are_enabled),
			sigc::ptr_fun (ARDOUR::set_translations_enabled)
			);

	bo->set_note (string_compose (_("These settings will only take effect after %1 is restarted (if available for your language preferences)."), PROGRAM_NAME));

	add_option (_("Appearance/Translation"), bo);

	parameter_changed ("enable-translation");
#endif // ENABLE_NLS

	add_option (_("Appearance/Regions"), new OptionEditorHeading (_("Region Information")));

	add_option (_("Appearance/Regions"),
			new BoolOption (
		     "show-region-xrun-markers",
		     _("Show xrun markers in regions"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_region_xrun_markers),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_region_xrun_markers)
		     ));

	add_option (_("Appearance/Regions"),
			new BoolOption (
		     "show-region-cue-markers",
		     _("Show cue markers in regions"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_region_cue_markers),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_region_cue_markers)
		     ));

	add_option (_("Appearance/Regions"),
	     new BoolComboOption (
		     "show-region-gain-envelopes",
		     _("Show gain envelopes in audio regions"),
		     _("in all modes"),
		     _("only in Draw and Internal Edit modes"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_region_gain),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_region_gain)
		     ));



	/* EDITOR *******************************************************************/


	add_option (_("Editor"), new OptionEditorHeading (_("Scroll and Zoom Behaviors")));

	if (!Profile->get_mixbus()) {

		add_option (_("Editor"),
				new BoolOption (
					"use-mouse-position-as-zoom-focus-on-scroll",
					_("Zoom to mouse position when zooming with scroll wheel"),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_mouse_position_as_zoom_focus_on_scroll),
					sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_mouse_position_as_zoom_focus_on_scroll)
					));
	}  // !mixbus

	add_option (_("Editor"),
		    new BoolOption (
			    "use-time-rulers-to-zoom-with-vertical-drag",
			    _("Zoom with vertical drag in rulers"),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_time_rulers_to_zoom_with_vertical_drag),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_time_rulers_to_zoom_with_vertical_drag)
			    ));

	add_option (_("Editor"),
		    new BoolOption (
			    "use-double-click-to-zoom-to-selection",
			    _("Double click zooms to selection"),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_use_double_click_to_zoom_to_selection),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_use_double_click_to_zoom_to_selection)
			    ));

	add_option (_("Editor"),
		    new BoolOption (
			    "update-editor-during-summary-drag",
			    _("Update editor window during drags of the summary"),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_update_editor_during_summary_drag),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_update_editor_during_summary_drag)
			    ));

	add_option (_("Editor"),
	    new BoolOption (
		    "autoscroll-editor",
		    _("Auto-scroll editor window when dragging near its edges"),
		    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_autoscroll_editor),
		    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_autoscroll_editor)
		    ));

	ComboOption<float>* dps = new ComboOption<float> (
		     "draggable-playhead-speed",
		     _("Auto-scroll speed when dragging playhead"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_draggable_playhead_speed),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_draggable_playhead_speed)
		     );
	dps->add (0.05, _("5%"));
	dps->add (0.1, _("10%"));
	dps->add (0.25, _("25%"));
	dps->add (0.5, _("50%"));
	dps->add (1.0, _("100%"));
	add_option (_("Editor"), dps);

	// XXX Long label, pushes other ComboBoxes to the right
	ComboOption<float>* eet = new ComboOption<float> (
		     "extra-ui-extents-time",
		     _("Limit zoom & summary view beyond session extents to"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_extra_ui_extents_time),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_extra_ui_extents_time)
		     );
	eet->add (1, _("1 minute"));
	eet->add (2, _("2 minutes"));
	eet->add (20, _("20 minutes"));
	eet->add (60, _("1 hour"));
	eet->add (60*2, _("2 hours"));
	eet->add (60*24, _("24 hours"));
	add_option (_("Editor"), eet);

	add_option (_("Editor"), new OptionEditorHeading (_("Editor Behavior")));

	add_option (_("Editor"),
	     new BoolOption (
		     "automation-follows-regions",
		     _("Move relevant automation when audio regions are moved"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_follows_regions),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_follows_regions)
		     ));

	bo = new BoolOption (
		     "new-automation-points-on-lane",
		     _("Ignore Y-axis when adding new automation-points"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_new_automation_points_on_lane),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_new_automation_points_on_lane)
		     );
	add_option (_("Editor"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> new points drawn in any automation lane will be placed on the existing line, regardless of mouse y-axis position."));

	bo = new BoolOption (
		     "automation-edit-cancels-auto-hide",
		     _("Automation edit cancels auto hide"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_automation_edit_cancels_auto_hide),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_automation_edit_cancels_auto_hide)
		     );
	add_option (_("Editor"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> automatically displayed automation lanes remain visible if events are added to the lane.\n"
			  "<b>When disabled</b>, spilled automation lanes are unconditionally hidden when a different control is touched.\n"
			  "This setting only has effect if 'Show Automation Lane on Touch' is used.")
			);

	ComboOption<FadeShape>* fadeshape = new ComboOption<FadeShape> (
			"default-fade-shape",
			_("Default fade shape"),
			sigc::mem_fun (*_rc_config,
				&RCConfiguration::get_default_fade_shape),
			sigc::mem_fun (*_rc_config,
				&RCConfiguration::set_default_fade_shape)
			);

	fadeshape->add (FadeLinear,
			_("Linear (for highly correlated material)"));
	fadeshape->add (FadeConstantPower, _("Constant power"));
	fadeshape->add (FadeSymmetric, _("Symmetric"));
	fadeshape->add (FadeSlow, _("Slow"));
	fadeshape->add (FadeFast, _("Fast"));

	add_option (_("Editor"), fadeshape);

#if 1 // XXX wide ComboBox
	ComboOption<RegionEquivalence> *eqv = new ComboOption<RegionEquivalence> (
		     "region-equivalency",
		     _("Regions in edit groups are edited together"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_region_equivalence),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_region_equivalence)
		     );

	eqv->add (Overlap,   _("whenever they overlap in time"));
	eqv->add (Enclosed,  _("if either encloses the other"));
	eqv->add (Exact,     _("if they have identical length, position and origin"));
	eqv->add (LayerTime, _("if they have identical length, position and layer"));

	add_option (_("Editor"), eqv);
#endif

	ComboOption<LayerModel>* lm = new ComboOption<LayerModel> (
		"layer-model",
		_("Layering model"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_layer_model),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_layer_model)
		);

	lm->add (LaterHigher, _("later is higher"));
	lm->add (Manual, _("manual layering"));
	add_option (_("Editor"), lm);

	add_option (_("Editor"), new OptionEditorHeading (_("Split/Separate")));

	ComboOption<RangeSelectionAfterSplit> *rras = new ComboOption<RangeSelectionAfterSplit> (
		    "range-selection-after-separate",
		    _("After a Separate operation, in Range mode"),
		    sigc::mem_fun (*_rc_config, &RCConfiguration::get_range_selection_after_split),
		    sigc::mem_fun (*_rc_config, &RCConfiguration::set_range_selection_after_split));

	rras->add(ClearSel,    _("Clear the Range Selection"));
	rras->add(PreserveSel, _("Preserve the Range Selection"));
	rras->add(ForceSel,    _("Select the regions under the range."));
	add_option (_("Editor"), rras);

#if 1 // XXX very wide ComboBox
	ComboOption<RegionSelectionAfterSplit> *rsas = new ComboOption<RegionSelectionAfterSplit> (
		    "region-selection-after-split",
		    _("After a Split operation, in Object mode"),
		    sigc::mem_fun (*_rc_config, &RCConfiguration::get_region_selection_after_split),
		    sigc::mem_fun (*_rc_config, &RCConfiguration::set_region_selection_after_split));

	// TODO: decide which of these modes are really useful
	rsas->add (None,                     _("Clear the Region Selection"));
	rsas->add (NewlyCreatedLeft,         _("Select the newly-created regions BEFORE the split point"));
	rsas->add (NewlyCreatedRight,        _("Select only the newly-created regions AFTER the split point"));
	rsas->add (NewlyCreatedBoth,         _("Select the newly-created regions"));
#if 0
	rsas->add(Existing,                  _("unmodified regions in the existing selection"));
	rsas->add(ExistingNewlyCreatedLeft,  _("existing selection and newly-created regions before the split"));
	rsas->add(ExistingNewlyCreatedRight, _("existing selection and newly-created regions after the split"));
#endif
	rsas->add(ExistingNewlyCreatedBoth,  _("Preserve existing selection, and select newly-created regions"));

	add_option (_("Editor"), rsas);
#endif

	add_option (_("Editor/Snap"), new OptionEditorHeading (_("General Snap options:")));

	add_option (_("Editor/Snap"),
		    new SpinOption<uint32_t> (
			    "snap-threshold",
			    _("Snap Threshold (pixels)"),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_threshold),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_threshold),
			    10, 200,
			    1, 10
			    ));

	add_option (_("Editor/Snap"),
		    new SpinOption<uint32_t> (
			    "ruler-granularity",
			    _("Approximate Grid/Ruler granularity (pixels)"),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_ruler_granularity),
			    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_ruler_granularity),
			    1, 100,
			    1, 10
			    ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "show-snapped-cursor",
		     _("Show \"snapped cursor\""),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_snapped_cursor),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_snapped_cursor)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "rubberbanding-snaps-to-grid",
		     _("Snap rubberband selection to grid"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_rubberbanding_snaps_to_grid),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_rubberbanding_snaps_to_grid)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "grid-follows-internal",
		     _("Grid switches to alternate selection for Internal Edit tools"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_grid_follows_internal),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_grid_follows_internal)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "show-grid-rulers",
		     _("Grid mode selection may change ruler visibility"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_grids_ruler),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_grids_ruler)
		     ));

	// TODO toggle sensitivity of rulers-follow-grid when show-grid-rulers changes
	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "rulers-follow-grid",
		     _("Rulers automatically change to follow the Grid mode selection"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_rulers_follow_grid),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_rulers_follow_grid)
		     ));

	add_option (_("Editor/Snap"), new OptionEditorHeading (_("Snap Target Mode:")));

	ComboOption<SnapTarget> *stm = new ComboOption<SnapTarget> (
		    "snap-target",
		    _("When the Grid is enabled, snap to"),
		    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_target),
		    sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_target));

	stm->add(SnapTargetGrid,  _("Grid"));
	stm->add(SnapTargetOther, _("Snap Targets"));
	stm->add(SnapTargetBoth,  _("Both the Grid and Snap Targets"));
	add_option (_("Editor/Snap"), stm);

	add_option (_("Editor/Snap"), new OptionEditorHeading (_("Snap Targets:")));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "snap-to-marks",
		     _("Markers"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_to_marks),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_to_marks)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "snap-to-playhead",
		     _("Playhead"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_to_playhead),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_to_playhead)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "snap-to-region-sync",
		     _("Region Sync Points"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_to_region_sync),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_to_region_sync)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "snap-to-region-start",
		     _("Region Starts"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_to_region_start),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_to_region_start)
		     ));

	add_option (_("Editor/Snap"),
	     new BoolOption (
		     "snap-to-region-end",
		     _("Region Ends"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_snap_to_region_end),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_snap_to_region_end)
		     ));

	add_option (_("Editor/Modifiers"), new OptionEditorHeading (_("Keyboard Modifiers")));
	add_option (_("Editor/Modifiers"), new KeyboardOptions);
	add_option (_("Editor/Modifiers"), new OptionEditorBlank ());


	/* MIDI *********************************************************************/

	add_option (_("MIDI"), new OptionEditorHeading (_("Session")));

	bo = new BoolOption (
		"allow-non-quarter-pulse",
		_("Allow non quarter-note pulse"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_allow_non_quarter_pulse),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_allow_non_quarter_pulse)
		);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    string_compose (_("<b>When enabled</b> %1 will allow tempo to be expressed in divisions per minute\n"
							      "<b>When disabled</b> %1 will only allow tempo to be expressed in quarter notes per minute"),
							    PROGRAM_NAME));
	add_option (_("MIDI"), bo);

	add_option (_("MIDI"),
	     new SpinOption<int32_t> (
		     "initial-program-change",
		     _("Initial program change"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_initial_program_change),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_initial_program_change),
		     -1, 65536, 1, 10
		     ));

	add_option (_("MIDI"), new OptionEditorHeading (_("Audition")));

	add_option (_("MIDI"),
	     new BoolOption (
		     "sound-midi-notes",
		     _("Sound MIDI notes as they are selected in the editor"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_sound_midi_notes),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_sound_midi_notes)
		     ));

	add_option (_("MIDI"), new OptionEditorHeading (_("Virtual Keyboard")));

	ComboOption<std::string>* vkeybdlayout = new ComboOption<std::string> (
		"vkeybd-layout",
		_("Virtual Keyboard Layout"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_vkeybd_layout),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_vkeybd_layout)
		);

	vkeybdlayout->add ("None",   _("Mouse-only (no keyboard)"));
	vkeybdlayout->add ("QWERTY", _("QWERTY"));
	vkeybdlayout->add ("QWERTZ", _("QWERTZ"));
	vkeybdlayout->add ("AZERTY", _("AZERTY"));
	vkeybdlayout->add ("DVORAK", _("DVORAK"));
	vkeybdlayout->add ("QWERTY Single", _("QWERTY Single"));
	vkeybdlayout->add ("QWERTZ Single", _("QWERTZ Single"));

	add_option (_("MIDI"), vkeybdlayout);

	add_option (_("MIDI"), new OptionEditorHeading (_("Default Visible Note Range")));

	const std::string legal_midi_name_chars = S_("legal characters for MIDI note names|ABCDEFG#1234567890");

	mrl_option = new EntryOption ("lower-midi-note", _("Default lower visible MIDI note"),
	                                           sigc::mem_fun (*this, &RCOptionEditor::get_default_lower_midi_note),
	                                           sigc::mem_fun (*this, &RCOptionEditor::set_default_lower_midi_note));
	mrl_option->set_valid_chars (legal_midi_name_chars);

	mru_option = new EntryOption ("lower-midi-note", _("Default upper visible MIDI note"),
	                                           sigc::mem_fun (*this, &RCOptionEditor::get_default_upper_midi_note),
	                                           sigc::mem_fun (*this, &RCOptionEditor::set_default_upper_midi_note));
	mru_option->set_valid_chars (legal_midi_name_chars);

	SpinOption<int>* mnh_option = new SpinOption<int> ("max-note-height", _("Maximum note height"),
	                                                   sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_max_note_height),
	                                                   sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_max_note_height),
	                                                   10, 40, 1, 5);
	add_option (_("MIDI"), mrl_option);
	add_option (_("MIDI"), mru_option);
	add_option (_("MIDI"), mnh_option);

	/* MIDI PORTs */
	add_option (_("MIDI"), new OptionEditorHeading (_("MIDI Port Options")));

	add_option (_("MIDI"),
		    new BoolOption (
			    "midi-input-follows-selection",
			    _("MIDI input follows MIDI track selection"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_input_follows_selection),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_input_follows_selection)
			    ));

	add_option (_("MIDI/MIDI Port Config"), new OptionEditorBlank ());
	add_option (_("MIDI/MIDI Port Config"), new MidiPortOptions ());

	add_option (_("MIDI"), new OptionEditorBlank ());

	/* TRANSPORT & SYNC *********************************************************/

	add_option (_("Transport"), new OptionEditorHeading (_("General")));

	bo = new BoolOption (
		     "name-new-markers",
		     _("Prompt for new marker names"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_name_new_markers),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_name_new_markers)
		);
	add_option (_("Transport"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(), _("<b>When enabled</b>, popup a dialog when a new marker is created to allow its name to be set as it is created."
								"\n\nYou can always rename markers by right-clicking on them."));

	bo = new BoolOption (
		     "stop-at-session-end",
		     _("Stop at the end of the session"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_at_session_end),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_at_session_end)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    string_compose (_("<b>When enabled</b> if %1 is <b>not recording</b>, and <b>all trigger-slots are unused</b> "
							      "the transport is stopped when it reaches the current session end marker\n\n"
							      "<b>When disabled</b> %1 will continue to roll past the session end marker at all times"),
							    PROGRAM_NAME));
	add_option (_("Transport"), bo);


	bo = new BoolOption (
		     "latched-record-enable",
		     _("Keep record-enable engaged on stop"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_latched_record_enable),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_latched_record_enable)
		     );
	add_option (_("Transport"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> master record will remain engaged when the transport transitions to stop.\n<b>When disabled</b> master record will be disabled when the transport transitions to stop."));

	bo = new BoolOption (
		     "reset-default-speed-on-stop",
		     _("Reset default speed on stop"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_reset_default_speed_on_stop),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_reset_default_speed_on_stop)
		     );
	add_option (_("Transport"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b>, stopping the transport will reset the default speed to normal.\n<b>When disabled</b> any current default speed will remain in effect across transport stops."));

	bo = new BoolOption (
		     "disable-disarm-during-roll",
		     _("Disable per-track record disarm while rolling"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_disable_disarm_during_roll),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_disable_disarm_during_roll)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(), _("<b>When enabled</b> this will prevent you from accidentally stopping specific tracks recording during a take."));
	add_option (_("Transport"), bo);

	bo = new BoolOption (
		     "quieten_at_speed",
		     _("12dB gain reduction during fast-forward and fast-rewind"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_quieten_at_speed),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_quieten_at_speed)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> this will reduce the unpleasant increase in perceived volume "
				"that occurs when fast-forwarding or rewinding through some kinds of audio"));
	add_option (_("Transport"), bo);


	bo = new BoolOption (
		     "rewind-ffwd-like-tape-decks",
		     _("Rewind/Fast-forward buttons change direction immediately"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_rewind_ffwd_like_tape_decks),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_rewind_ffwd_like_tape_decks)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
	                                    _("<b>When enabled</b> rewind/ffwd controls will immediately change playback direction when appropriate.\n\n"
	                                      "<b>When disabled</b> rewind/ffwd controls will gradually speed up/slow down playback"));
	add_option (_("Transport"), bo);


	bo = new BoolOption (
		"auto-return-after-rewind-ffwd",
		_("Allow auto-return after rewind/ffwd operations"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_return_after_rewind_ffwd),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_return_after_rewind_ffwd)
		);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
	                                    _("<b>When enabled</b> if auto-return is enabled, the playhead will auto-return after rewind/ffwd operations\n\n"
	                                      "<b>When disabled</b> the playhead will never auto-return after rewind/ffwd operations")
		);
	add_option (_("Transport"), bo);


	ComboOption<float>* psc = new ComboOption<float> (
		     "preroll-seconds",
		     _("Preroll"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_preroll_seconds),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_preroll_seconds)
		     );
	Gtkmm2ext::UI::instance()->set_tip (psc->tip_widget(),
					    (_("The amount of preroll to apply when <b>Play with Preroll</b> or <b>Record with Preroll</b>is initiated.\n\n"
					       "If <b>Follow Edits</b> is enabled, the preroll is applied to the playhead position when a region is selected or trimmed.")));
	psc->add (-4.0, _("4 Bars"));
	psc->add (-2.0, _("2 Bars"));
	psc->add (-1.0, _("1 Bar"));
	psc->add (0.0, _("0 (no pre-roll)"));
	psc->add (0.1, _("0.1 second"));
	psc->add (0.25, _("0.25 second"));
	psc->add (0.5, _("0.5 second"));
	psc->add (1.0, _("1.0 second"));
	psc->add (2.0, _("2.0 seconds"));
	add_option (_("Transport"), psc);


	add_option (_("Transport"), new OptionEditorHeading (_("Looping")));

	bo = new BoolOption (
		     "loop-is-mode",
		     _("Play loop is a transport mode"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_loop_is_mode),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_loop_is_mode)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    (_("<b>When enabled</b> the loop button does not start playback but forces playback to always play the loop\n\n"
					       "<b>When disabled</b> the loop button starts playing the loop, but stop then cancels loop playback")));


	add_option (_("Transport"), bo);


	ComboOption<LoopFadeChoice>* lca = new ComboOption<LoopFadeChoice> (
		     "loop-fade-choice",
		     _("Loop Fades"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_loop_fade_choice),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_loop_fade_choice)
		     );
	lca->add (NoLoopFade, _("No fades at loop boundaries"));
	lca->add (EndLoopFade, _("Fade out at loop end"));
	lca->add (BothLoopFade, _("Fade in at loop start & Fade out at loop end"));
	lca->add (XFadeLoop, _("Cross-fade loop end and start"));
	add_option (_("Transport"), lca);
	Gtkmm2ext::UI::instance()->set_tip (lca->tip_widget(), _("Options for fades/crossfades at loop boundaries"));

	add_option (_("Transport"), new OptionEditorHeading (_("Dropout (xrun) Handling")));
	bo = new BoolOption (
		     "stop-recording-on-xrun",
		     _("Stop recording when an xrun occurs"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_recording_on_xrun),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_recording_on_xrun)
		     );
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    string_compose (_("<b>When enabled</b> %1 will stop recording if an over- or underrun is detected by the audio engine"),
							    PROGRAM_NAME));
	add_option (_("Transport"), bo);

	bo = new BoolOption (
		     "create-xrun-marker",
		     _("Create markers where xruns occur"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_create_xrun_marker),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_create_xrun_marker)
		     );
	add_option (_("Transport"), bo);

	bo = new BoolOption (
		     "recording-resets-xrun-count",
		     _("Reset xrun counter when starting to record"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_recording_resets_xrun_count),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_recording_resets_xrun_count)
		     );
	add_option (_("Transport"), bo);

	add_option (_("Transport/Chase"), new OptionEditorHeading (_("MIDI Machine Control (MMC)")));

	add_option (_("Transport/Chase"),
		    new BoolOption (
			    "mmc-control",
			    _("Respond to MMC commands"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_control),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_control)
			    ));

	add_option (_("Transport/Chase"),
	     new SpinOption<uint8_t> (
		     "mmc-receive-device-id",
		     _("Inbound MMC device ID"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_receive_device_id),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_receive_device_id),
		     0, 127, 1, 10
		     ));

	add_option (_("Transport/Chase"), new OptionEditorHeading (_("Transport Masters")));

	add_option (_("Transport/Chase"),
			new RcActionButton (_("Show Transport Masters Window"),
				sigc::mem_fun (*this, &RCOptionEditor::show_transport_masters)));

	_sync_framerate = new BoolOption (
		     "timecode-sync-frame-rate",
		     _("Match session video frame rate to external timecode"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_sync_frame_rate),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_sync_frame_rate)
		     );
	Gtkmm2ext::UI::instance()->set_tip
		(_sync_framerate->tip_widget(),
		 string_compose (_("This option controls the value of the video frame rate <i>while chasing</i> an external timecode source.\n\n"
				   "<b>When enabled</b> the session video frame rate will be changed to match that of the selected external timecode source.\n\n"
				   "<b>When disabled</b> the session video frame rate will not be changed to match that of the selected external timecode source."
				   "Instead the frame rate indication in the main clock will flash red and %1 will convert between the external "
				   "timecode standard and the session standard."), PROGRAM_NAME));
	add_option (_("Transport/Chase"), _sync_framerate);

	auto mcr = new SpinOption<double> (
		"midi-clock-resolution",
		_("BPM Resolution for incoming MIDI Clock"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_clock_resolution),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_clock_resolution),
		0., 1., 0.01, 0.1, _("quarters"), 1, 2
		);
	Gtkmm2ext::UI::instance()->set_tip
		(mcr->tip_widget(),
		 _("This option can be used to quantize incoming MIDI clock to whole (or fractions of a) quarter note.\n\n"
		   "Setting it to zero prevents any quantization, which can result in a rather jittery response to incoming MIDI Clock.\n\n"
		   "Setting it to 1.0 quantizes to whole (integer) BPM values, and is the default.\n\n"
		   "If you are using a MIDI clock source that quantizes to some fraction of a quarter note then adjust this setting to reflect that."));

	add_option (_("Transport/Chase"), new OptionEditorHeading (_("MIDI Clock")));
	add_option (_("Transport/Chase"), mcr);

	add_option (_("Transport/Generate"), new OptionEditorHeading (_("Linear Timecode (LTC) Generator")));

	add_option (_("Transport/Generate"),
		    new BoolOption (
			    "send-ltc",
			    _("Enable LTC generator"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_ltc),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_ltc)
			    ));

	_ltc_send_continuously = new BoolOption (
			    "ltc-send-continuously",
			    _("Send LTC while stopped"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_ltc_send_continuously),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_ltc_send_continuously)
			    );
	Gtkmm2ext::UI::instance()->set_tip
		(_ltc_send_continuously->tip_widget(),
		 string_compose (_("<b>When enabled</b> %1 will continue to send LTC information even when the transport (playhead) is not moving"), PROGRAM_NAME));
	add_option (_("Transport/Generate"), _ltc_send_continuously);

	_ltc_volume_slider = new HSliderOption("ltcvol", _("LTC generator level [dBFS]"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_ltc_output_volume),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_ltc_output_volume),
					-50, 0, .5, 5,
					.05, true);

	Gtkmm2ext::UI::instance()->set_tip
		(_ltc_volume_slider->tip_widget(),
		 _("Specify the Peak Volume of the generated LTC signal in dBFS. A good value is  0dBu ^= -18dBFS in an EBU calibrated system"));

	add_option (_("Transport/Generate"), _ltc_volume_slider);

	add_option (_("Transport/Generate"), new LTCPortSelectOption (_rc_config, this));

	add_option (_("Transport/Generate"), new OptionEditorHeading (_("MIDI Time Code (MTC) Generator")));

	add_option (_("Transport/Generate"),
		    new BoolOption (
			    "send-mtc",
			    _("Enable MTC Generator"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mtc),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mtc)
			    ));


	SpinOption<int>* soi = new SpinOption<int> (
			    "mtc-qf-speed-tolerance",
			    _("Max MTC varispeed (%)"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_mtc_qf_speed_tolerance),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_mtc_qf_speed_tolerance),
			    0, 20, 1, 5
			    );
	Gtkmm2ext::UI::instance()->set_tip (soi->tip_widget(), _("Percentage either side of normal transport speed to transmit MTC."));
	add_option (_("Transport/Generate"), soi);

	add_option (_("Transport/Generate"), new OptionEditorHeading (_("MIDI Machine Control (MMC)")));

	add_option (_("Transport/Generate"),
		    new BoolOption (
			    "send-mmc",
			    _("Send MMC commands"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mmc),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mmc)
			    ));

	add_option (_("Transport/Generate"),
	     new SpinOption<uint8_t> (
		     "mmc-send-device-id",
		     _("Outbound MMC device ID"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_send_device_id),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_send_device_id),
		     0, 127, 1, 10
		     ));

	add_option (_("Transport/Generate"), new OptionEditorHeading (_("MIDI Beat Clock (Mclk) Generator")));

	add_option (_("Transport/Generate"),
		    new BoolOption (
			    "send-midi-clock",
			    _("Enable Mclk generator"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_midi_clock),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_midi_clock)
			    ));

	add_option (_("Transport"), new OptionEditorHeading (_("Plugins")));

	bo = new BoolOption (
		"plugins-stop-with-transport",
		_("Silence plugins when the transport is stopped"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugins_stop_with_transport),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugins_stop_with_transport)
		);
	add_option (_("Transport"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> plugins will be reset at transport stop. When disabled plugins will be left unchanged at transport stop.\n\nThis mostly affects plugins with a \"tail\" like Reverbs."));

	/* PLUGINS ******************************************************************/

#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT || defined AUDIOUNIT_SUPPORT || defined VST3_SUPPORT)
	add_option (_("Plugins"), new OptionEditorHeading (_("Scan/Discover")));
	add_option (_("Plugins"),
			new RcActionButton (_("Scan for Plugins"),
				sigc::mem_fun (*this, &RCOptionEditor::plugin_scan_refresh)));

	add_option (_("Plugins"), new PluginScanTimeOutSliderOption (_rc_config));
#endif

	add_option (_("Plugins"), new OptionEditorHeading (_("General")));

#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT || defined AUDIOUNIT_SUPPORT || defined VST3_SUPPORT)

	bo = new BoolOption (
			"discover-plugins-on-start",
			_("Scan for [new] Plugins on Application Start"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_discover_plugins_on_start),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_discover_plugins_on_start)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> new plugins are searched, tested and added to the cache index on application start. When disabled new plugins will only be available after triggering a 'Scan' manually"));

	bo = new BoolOption (
			"show-plugin-scan-window",
			_("Always Display Plugin Scan Progress"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_plugin_scan_window),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_plugin_scan_window)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> a popup window showing plugin scan progress is displayed for indexing (cache load) and discovery (detect new plugins)"));

	bo = new BoolOption (
			"verbose-plugin-scan",
			_("Verbose Plugin Scan"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_verbose_plugin_scan),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_verbose_plugin_scan)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> additional information for every plugin is shown to the Plugin Manager Log."));
#endif

	bo = new BoolOption (
			"show-manager-if-plugins-are-missing",
			_("Open Plugin Manager window when missing plugins are found"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_manager_if_plugins_are_missing),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_manager_if_plugins_are_missing)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> the Plugin Manager is display at session load if the session contains any plugins that are missing, or plugins have been updated and require a rescan."));

	bo = new BoolOption (
		"new-plugins-active",
			_("Make new plugins active"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_new_plugins_active),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_new_plugins_active)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> plugins will be activated when they are added to tracks/busses.\n<b>When disabled</b> plugins will be left inactive when they are added to tracks/busses"));

	bo = new BoolOption (
		"setup-sidechain",
			_("Setup Sidechain ports when loading plugin with aux inputs"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_setup_sidechain),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_setup_sidechain)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> sidechain ports are created for plugins at instantiation time if a plugin has sidechain inputs. Note that the ports themselves will have to be manually connected, so while the plugin pins are connected they are initially fed with silence.\n<b>When disabled</b> sidechain input pins will remain unconnected."));

	add_option (_("Plugins/GUI"), new OptionEditorHeading (_("Plugin GUI")));
	add_option (_("Plugins/GUI"),
	     new BoolOption (
		     "open-gui-after-adding-plugin",
		     _("Automatically open the plugin GUI when adding a new plugin"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_open_gui_after_adding_plugin),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_open_gui_after_adding_plugin)
		     ));

	bo = new BoolOption (
		"one-plugin-window-only",
		_("Show only one plugin window at a time"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_one_plugin_window_only),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_one_plugin_window_only)
		);
	add_option (_("Plugins/GUI"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
					    _("<b>When enabled</b> at most one plugin GUI window can be on-screen at a time. <b>When disabled</b>, the number of visible plugin GUI windows is unlimited"));

	ComboOption<PluginGUIBehavior>* puimode = new ComboOption<PluginGUIBehavior> (
			"plugin-gui-behavior",
			_("Closing a Plugin GUI Window"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_plugin_gui_behavior),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_plugin_gui_behavior)
			);
		puimode->add (PluginGUIHide, _("only hides the window"));
		puimode->add (PluginGUIDestroyAny, _("destroys the GUI instance, releasing resources"));
		puimode->add (PluginGUIDestroyVST, _("only destroys VST2/3 UIs, hides others"));

		add_option (_("Plugins/GUI"), puimode);
	Gtkmm2ext::UI::instance()->set_tip (puimode->tip_widget(), _("Closing a plugin window, usually only hides it. This makes is fast to open the same plugin UI again at a later time.\n\nMost plugin UIs are inactive and do not consume any CPU resources while they are not mapped on the screen.\n\nHowever some plugins do consume significant CPU and GPU resources even when they are not currently displayed. This option allows to work around the issue."));

#ifdef LV2_EXTENDED
	add_option (_("Plugins/GUI"), new OptionEditorHeading (_("Mixer Strip Inline Display")));
	add_option (_("Plugins/GUI"),
	     new BoolOption (
		     "show-inline-display-by-default",
		     _("Show Plugin Inline Display on Mixer Strip by default"),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_show_inline_display_by_default),
		     sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_show_inline_display_by_default)
		     ));

	_plugin_prefer_inline = new BoolOption (
			"prefer-inline-over-gui",
			_("Don't automatically open the plugin GUI when the plugin has an inline display mode"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_prefer_inline_over_gui),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_prefer_inline_over_gui)
			);
	add_option (_("Plugins/GUI"), _plugin_prefer_inline);
#endif


#if (defined WINDOWS_VST_SUPPORT || defined MACVST_SUPPORT || defined LXVST_SUPPORT || defined VST3_SUPPORT)
	add_option (_("Plugins/VST"), new OptionEditorHeading (_("VST")));
#if 0
	add_option (_("Plugins/VST"),
			new RcActionButton (_("Scan for Plugins"),
				sigc::mem_fun (*this, &RCOptionEditor::plugin_scan_refresh)));
#endif

#if (defined AUDIOUNIT_SUPPORT && defined MACVST_SUPPORT)
	bo = new BoolOption (
			"use-macvst",
			_("Enable Mac VST2 support (requires restart or re-scan)"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_macvst),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_macvst)
			);
	add_option (_("Plugins/VST"), bo);
#endif

#ifndef MIXBUS

#ifdef WINDOWS_VST_SUPPORT
	bo = new BoolOption (
			"use-windows-vst",
			_("Enable Windows VST2 support (requires restart or re-scan)"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_windows_vst),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_windows_vst)
			);
	add_option (_("Plugins/VST"), bo);
#endif

#ifdef LXVST_SUPPORT
	bo = new BoolOption (
			"use-lxvst",
			_("Enable Linux VST2 support (requires restart or re-scan)"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_lxvst),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_lxvst)
			);
	add_option (_("Plugins/VST"), bo);
#endif

#ifdef VST3_SUPPORT
	bo = new BoolOption (
			"use-vst3",
			_("Enable VST3 support (requires restart or re-scan)"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_vst3),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_vst3)
			);
	add_option (_("Plugins/VST"), bo);
#endif

#endif // !Mixbus

#if (defined WINDOWS_VST_SUPPORT || defined MACVST_SUPPORT || defined LXVST_SUPPORT)
	add_option (_("Plugins/VST"), new OptionEditorHeading (_("VST 2.x")));

	add_option (_("Plugins/VST"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_vst2_cache),
				_("VST 2 Cache:")));

	add_option (_("Plugins/VST"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_vst2_blacklist),
				_("VST 2 Ignorelist:")));
#endif

#ifdef LXVST_SUPPORT
	add_option (_("Plugins/VST"),
			new RcActionButton (_("Edit"),
				sigc::bind (sigc::mem_fun (*this, &RCOptionEditor::edit_vst_path),
					_("Set Linux VST2 Search Path"),
					PluginManager::instance().get_default_lxvst_path (),
					sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugin_path_lxvst),
					sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugin_path_lxvst)
					),
				_("Linux VST2 Path:")));

	add_option (_("Plugins/VST"),
			new RcConfigDisplay (
				"plugin-path-lxvst",
				_("Path:"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugin_path_lxvst),
				0));
#endif

#ifdef WINDOWS_VST_SUPPORT
	add_option (_("Plugins/VST"),
			new RcActionButton (_("Edit"),
				sigc::bind (sigc::mem_fun (*this, &RCOptionEditor::edit_vst_path),
					_("Set Windows VST2 Search Path"),
					PluginManager::instance().get_default_windows_vst_path (),
					sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugin_path_vst),
					sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugin_path_vst)
					),
				_("Windows VST2 Path:")));

	add_option (_("Plugins/VST"),
			new RcConfigDisplay (
				"plugin-path-vst",
				_("Path:"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugin_path_vst),
				';'));
#endif

#ifdef VST3_SUPPORT
	add_option (_("Plugins/VST"), new OptionEditorHeading (_("VST 3")));
	add_option (_("Plugins/VST"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_vst3_cache),
				_("VST 3 Cache:")));

	add_option (_("Plugins/VST"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_vst3_blacklist),
				_("VST 3 Ignorelist:")));

	RcActionButton* vst3_path =
			new RcActionButton (_("Edit"),
				sigc::bind (sigc::mem_fun (*this, &RCOptionEditor::edit_vst_path),
					_("Set Additional VST3 Search Path"),
					"", /* default is blank */
					sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugin_path_vst3),
					sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugin_path_vst3)
					),
				_("Additional VST3 Path:"));

	vst3_path->set_note (_("Customizing VST3 paths is discouraged. Note that default VST3 paths as per "
	                       "<a href=\"https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Locations+Format/Plugin+Locations.html\">specification</a> "
	                       "are always searched, and need not be explicitly set."));
	add_option (_("Plugins/VST"), vst3_path);

	// -> Appearance/Mixer ?
	add_option (_("Plugins/VST"),
	     new BoolOption (
		     "show-vst3-micro-edit-inline",
		     _("Automatically show 'Micro Edit' tagged controls on the mixer-strip"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_vst3_micro_edit_inline),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_vst3_micro_edit_inline)
		     ));

#if (defined WINDOWS_VST_SUPPORT || defined MACVST_SUPPORT || defined LXVST_SUPPORT)
	add_option (_("Plugins/VST"), new OptionEditorHeading (_("VST2/VST3")));
	add_option (_("Plugins/VST"),
	     new BoolOption (
		     "conceal-vst2-if-vst3-exists",
		     _("Conceal VST2 Plugin if matching VST3 exists"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_conceal_vst2_if_vst3_exists),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_conceal_vst2_if_vst3_exists)
		     ));
#endif
#endif // VST3
#endif // Any VST (2 or 3)

#ifdef AUDIOUNIT_SUPPORT

	add_option (_("Plugins/Audio Unit"), new OptionEditorHeading (_("Audio Unit")));
#if 0
	add_option (_("Plugins/Audio Unit"),
			new RcActionButton (_("Scan for Plugins"),
				sigc::mem_fun (*this, &RCOptionEditor::plugin_scan_refresh)));
#endif

	bo = new BoolOption (
			"use-audio-units",
			_("Enable Audio Unit support (requires restart or re-scan)"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_audio_units),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_audio_units)
			);
	add_option (_("Plugins/Audio Unit"), bo);

	add_option (_("Plugins/Audio Unit"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_au_cache),
				_("AU Cache:")));

	add_option (_("Plugins/Audio Unit"),
			new RcActionButton (_("Clear"),
				sigc::mem_fun (*this, &RCOptionEditor::clear_au_blacklist),
				_("AU Ignorelist:")));
#endif

	add_option (_("Plugins"), new OptionEditorHeading (_("LV1/LV2")));
	add_option (_("Plugins"),
	     new BoolOption (
		     "conceal-lv1-if-lv2-exists",
		     _("Conceal LADSPA (LV1) Plugins if matching LV2 exists"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_conceal_lv1_if_lv2_exists),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_conceal_lv1_if_lv2_exists)
		     ));
	add_option (_("Plugins"), new OptionEditorHeading (_("Instrument")));

	bo = new BoolOption (
			"ask-replace-instrument",
			_("Ask to replace existing instrument plugin"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_ask_replace_instrument),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_ask_replace_instrument)
			);
	add_option (_("Plugins"), bo);

	bo = new BoolOption (
			"ask-setup_instrument",
			_("Interactively configure instrument plugins on insert"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_ask_setup_instrument),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_ask_setup_instrument)
			);
	add_option (_("Plugins"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> show a dialog to select instrument channel configuration before adding a multichannel plugin."));

	add_option (_("Plugins"), new OptionEditorHeading (_("Statistics")));

	add_option (_("Plugins"),
			new RcActionButton (_("Reset Statistics"),
				sigc::mem_fun (*this, &RCOptionEditor::plugin_reset_stats)));

	add_option (_("Plugins"),
	     new SpinOption<int32_t> (
		     "max-plugin-chart",
		     _("Plugin chart (use-count) length"),
				 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_max_plugin_chart),
				 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_max_plugin_chart),
		     10, 25, 1, 5
		     ));

	add_option (_("Plugins"),
	     new SpinOption<int32_t> (
		     "max-plugin-recent",
		     _("Plugin recent list length"),
				 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_max_plugin_recent),
				 sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_max_plugin_recent),
		     10, 50, 1, 5
		     ));

	add_option (_("Plugins"), new OptionEditorBlank ());

	/* MONITORING, SOLO) ********************************************************/

	add_option (_("Monitoring"), new OptionEditorHeading (_("Monitoring")));

	ComboOption<MonitorModel>* mm = new ComboOption<MonitorModel> (
		"monitoring-model",
		_("Record monitoring handled by"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_monitoring_model),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_monitoring_model)
		);

	if (AudioEngine::instance()->port_engine().can_monitor_input()) {
		mm->add (HardwareMonitoring, _("Audio Driver"));
	}

	string prog (PROGRAM_NAME);
	boost::algorithm::to_lower (prog);
	mm->add (SoftwareMonitoring, string_compose (_("%1"), prog));
	mm->add (ExternalMonitoring, _("Audio Hardware"));

	add_option (_("Monitoring"), mm);

	bo = new BoolOption (
		     "auto-input-does-talkback",
		     _("Auto Input does 'talkback'"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_input_does_talkback),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_input_does_talkback)
		     );
	add_option (_("Monitoring"), bo);
	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			string_compose (_("<b>When enabled</b>, and Transport->Auto-Input is enabled, %1 will always monitor audio inputs when transport is stopped, even if tracks aren't armed."),
					PROGRAM_NAME));

	add_option (_("Monitoring"), new OptionEditorHeading (_("Solo")));

	_solo_control_is_listen_control = new BoolOption (
		"solo-control-is-listen-control",
		_("Solo controls are Listen controls"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_control_is_listen_control),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_control_is_listen_control)
		);

	add_option (_("Monitoring"), _solo_control_is_listen_control);

	add_option (_("Monitoring"),
	     new BoolOption (
		     "exclusive-solo",
		     _("Exclusive solo"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_exclusive_solo),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_exclusive_solo)
		     ));

	add_option (_("Monitoring"),
	     new BoolOption (
		     "show-solo-mutes",
		     _("Show solo muting"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_solo_mutes),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_solo_mutes)
		     ));

	add_option (_("Monitoring"),
	     new BoolOption (
		     "solo-mute-override",
		     _("Soloing overrides muting"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_mute_override),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_mute_override)
		     ));

	add_option (_("Monitoring"),
	     new FaderOption (
		     "solo-mute-gain",
		     _("Solo-in-place mute cut (dB)"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_mute_gain),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_mute_gain)
		     ));

	_listen_position = new ComboOption<ListenPosition> (
		"listen-position",
		_("Listen Position"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_listen_position),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_listen_position)
		);

	_listen_position->add (AfterFaderListen, _("after-fader (AFL)"));
	_listen_position->add (PreFaderListen, _("pre-fader (PFL)"));

	add_option (_("Monitoring"), _listen_position);

	ComboOption<PFLPosition>* pp = new ComboOption<PFLPosition> (
		"pfl-position",
		_("PFL signals come from"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_pfl_position),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_pfl_position)
		);

	pp->add (PFLFromBeforeProcessors, _("before pre-fader processors"));
	pp->add (PFLFromAfterProcessors, _("pre-fader but after pre-fader processors"));

	add_option (_("Monitoring"), pp);

	ComboOption<AFLPosition>* pa = new ComboOption<AFLPosition> (
		"afl-position",
		_("AFL signals come from"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_afl_position),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_afl_position)
		);

	pa->add (AFLFromBeforeProcessors, _("immediately post-fader"));
	pa->add (AFLFromAfterProcessors, _("after post-fader processors (before pan)"));

	add_option (_("Monitoring"), pa);

	/* SIGNAL FLOW **************************************************************/

	add_option (_("Signal Flow"), new OptionEditorHeading (_("Master")));
	add_option (_("Signal Flow"),
			new BoolOption (
				"use-master-volume",
				_("Enable master-bus output gain control"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_master_volume),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_master_volume)
				));

	ComboOption<uint32_t>* zitaq = new ComboOption<uint32_t> (
			"port-resampler-quality",
			_("I/O Resampler (vari-speed) quality"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_port_resampler_quality),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_port_resampler_quality)
			);

		zitaq->add (0, _("Off (no vari-speed)"));
		zitaq->add (9, _("Low (16 samples latency)"));
		zitaq->add (17, _("Moderate (32 samples latency), default"));
		zitaq->add (33, _("Medium (64 samples latency)"));
		zitaq->add (49, _("High (96 samples latency)"));
		zitaq->add (65, _("Very High (128 samples latency)"));
		zitaq->add (93, _("Extreme (184 samples latency)"));

		uint32_t prq_val = _rc_config->get_port_resampler_quality ();
		if (!(prq_val == 0 || prq_val == 9 || prq_val == 17 || prq_val == 33 || prq_val == 65 || prq_val == 93)) {
			if (prq_val < 8) {
				_rc_config->set_port_resampler_quality (8);
				prq_val = 8;
			}
			if (prq_val > 96) {
				_rc_config->set_port_resampler_quality (96);
				prq_val = 96;
			}
			zitaq->add (prq_val, string_compose (_("Custom (%1 samples latency)"), prq_val - 1));
		}

		zitaq->set_note (_("This setting will only take effect when the Audio Engine is restarted."));
		set_tooltip (zitaq->tip_widget(), _("To facilitate vari-speed playback/recording, audio is resampled to change pitch and speed. This introduces latency depending on the quality. For consistency this latency is also present when not vari-speeding (even if no resampling happens).\n\nIt is possible to disable this feature, which will also disable vari-speed. - Except if the audio-engine runs at a different sample-rate than the session, the quality is set to be at least 'Very High' (128 samples round-trip latency)"));

	add_option (_("Signal Flow"), zitaq);

	add_option (_("Signal Flow"), new OptionEditorHeading (_("Default Track / Bus Muting Options")));

	add_option (_("Signal Flow"),
	     new BoolOption (
		     "mute-affects-pre-fader",
		     _("Mute affects pre-fader sends"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_pre_fader),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_pre_fader)
		     ));

	add_option (_("Signal Flow"),
	     new BoolOption (
		     "mute-affects-post-fader",
		     _("Mute affects post-fader sends"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_post_fader),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_post_fader)
		     ));

	add_option (_("Signal Flow"),
	     new BoolOption (
		     "mute-affects-control-outs",
		     _("Mute affects control outputs"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_control_outs),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_control_outs)
		     ));

	add_option (_("Signal Flow"),
	     new BoolOption (
		     "mute-affects-main-outs",
		     _("Mute affects main outputs"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_main_outs),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_main_outs)
		     ));


	add_option (_("Signal Flow"), new OptionEditorHeading (_("Send Routing")));
	add_option (_("Signal Flow"),
			new BoolOption (
				"link-send-and-route-panner",
				_("Link panners of Aux and External Sends with main panner by default"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_link_send_and_route_panner),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_link_send_and_route_panner)
				));

	add_option (_("Signal Flow"), new OptionEditorHeading (_("Audio Regions")));

	add_option (_("Signal Flow"),
	     new BoolOption (
		     "replicate-missing-region-channels",
		     _("Replicate missing region channels"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_replicate_missing_region_channels),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_replicate_missing_region_channels)
		     ));

	if (!Profile->get_mixbus()) {

		add_option (_("Signal Flow"), new OptionEditorHeading (_("Track and Bus Connections")));

		bo = new BoolOption (
					"auto-connect-standard-busses",
					_("Auto-connect main output (master or monitor) bus to physical ports"),
					sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_connect_standard_busses),
					sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_connect_standard_busses)
					);
		add_option (_("Signal Flow"), bo);
		Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			_("<b>When enabled</b> the main output bus is auto-connected to the first N physical ports. "
				"If the session has a monitor-section, the monitor-bus output is connected to the hardware playback ports, "
				"otherwise the master-bus output is directly used for playback."));

		ComboOption<AutoConnectOption>* iac = new ComboOption<AutoConnectOption> (
				"input-auto-connect",
				_("Connect track inputs"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_input_auto_connect),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_input_auto_connect)
				);

		iac->add (AutoConnectPhysical, _("automatically to physical inputs"));
		iac->add (ManualConnect, _("manually"));

		add_option (_("Signal Flow"), iac);

		ComboOption<AutoConnectOption>* oac = new ComboOption<AutoConnectOption> (
				"output-auto-connect",
				_("Connect track and bus outputs"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_output_auto_connect),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_output_auto_connect)
				);

		oac->add (AutoConnectPhysical, _("automatically to physical outputs"));
		oac->add (AutoConnectMaster, _("automatically to master bus"));
		oac->add (ManualConnect, _("manually"));

		add_option (_("Signal Flow"), oac);

		bo = new BoolOption (
				"strict-io",
				_("Use 'Strict-I/O' for new tracks or busses"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_strict_io),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_strict_io)
				);

		add_option (_("Signal Flow"), bo);
		Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
				_("With strict-i/o enabled, Effect Processors will not modify the number of channels on a track. The number of output channels will always match the number of input channels."));

	}  // !mixbus

	/* Click */

	add_option (_("Metronome"), new OptionEditorHeading (_("Metronome")));
	add_option (_("Metronome"), new ClickOptions (_rc_config));

	add_option (_("Metronome"), new OptionEditorHeading (_("Options")));

	bo = new BoolOption (
			"click-record-only",
			_("Enable metronome only while recording"),
			sigc::mem_fun (*_rc_config, &RCConfiguration::get_click_record_only),
			sigc::mem_fun (*_rc_config, &RCConfiguration::set_click_record_only)
			);

	Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
			string_compose (_("<b>When enabled</b> the metronome will remain silent if %1 is <b>not recording</b>."), PROGRAM_NAME));
	add_option (_("Metronome"), bo);
	add_option (_("Metronome"), new OptionEditorBlank ());

	/* CONTROL SURFACES *********************************************************/

	add_option (_("Control Surfaces"), new OptionEditorHeading (_("Control Surfaces")));
	add_option (_("Control Surfaces"), new ControlSurfacesOptions ());


	/* METERS *******************************************************************/

	if (Profile->get_mixbus()) {
		add_option (S_("Preferences|Metering"), new OptionEditorHeading (_("Meterbridge meters")));
	} else {
		add_option (S_("Preferences|Metering"), new OptionEditorHeading (_("Metering")));
	}

	ComboOption<float>* mht = new ComboOption<float> (
		"meter-hold",
		_("Peak hold time"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_hold),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_hold)
		);

	mht->add (MeterHoldOff, _("off"));
	mht->add (MeterHoldShort, _("short"));
	mht->add (MeterHoldMedium, _("medium"));
	mht->add (MeterHoldLong, _("long"));

	add_option (S_("Preferences|Metering"), mht);

	ComboOption<float>* mfo = new ComboOption<float> (
		"meter-falloff",
		_("DPM fall-off"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_falloff),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_falloff)
		);

	mfo->add (METER_FALLOFF_OFF,      _("off"));
	mfo->add (METER_FALLOFF_SLOWEST,  _("slowest [6.6dB/sec]"));
	mfo->add (METER_FALLOFF_SLOW,     _("slow [8.6dB/sec] (BBC PPM, EBU PPM)"));
	mfo->add (METER_FALLOFF_SLOWISH,  _("moderate [12.0dB/sec] (DIN)"));
	mfo->add (METER_FALLOFF_MODERATE, _("medium [13.3dB/sec] (EBU Digi PPM, IRT Digi PPM)"));
	mfo->add (METER_FALLOFF_MEDIUM,   _("fast [20dB/sec]"));
	mfo->add (METER_FALLOFF_FAST,     _("very fast [32dB/sec]"));

	add_option (S_("Preferences|Metering"), mfo);

	ComboOption<MeterLineUp>* mlu = new ComboOption<MeterLineUp> (
		"meter-line-up-level",
		_("Meter line-up level; 0dBu"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_line_up_level),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_line_up_level)
		);

	mlu->add (MeteringLineUp24, _("-24dBFS (SMPTE US: 4dBu = -20dBFS)"));
	mlu->add (MeteringLineUp20, _("-20dBFS (SMPTE RP.0155)"));
	mlu->add (MeteringLineUp18, _("-18dBFS (EBU, BBC)"));
	mlu->add (MeteringLineUp15, _("-15dBFS (DIN)"));

	Gtkmm2ext::UI::instance()->set_tip (mlu->tip_widget(), _("Configure meter-marks and color-knee point for dBFS scale DPM, set reference level for IEC1/Nordic, IEC2 PPM and VU meter."));

	add_option (S_("Preferences|Metering"), mlu);

	ComboOption<MeterLineUp>* mld = new ComboOption<MeterLineUp> (
		"meter-line-up-din",
		_("IEC1/DIN Meter line-up level; 0dBu"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_line_up_din),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_line_up_din)
		);

	mld->add (MeteringLineUp24, _("-24dBFS (SMPTE US: 4dBu = -20dBFS)"));
	mld->add (MeteringLineUp20, _("-20dBFS (SMPTE RP.0155)"));
	mld->add (MeteringLineUp18, _("-18dBFS (EBU, BBC)"));
	mld->add (MeteringLineUp15, _("-15dBFS (DIN)"));

	Gtkmm2ext::UI::instance()->set_tip (mld->tip_widget(), _("Reference level for IEC1/DIN meter."));

	add_option (S_("Preferences|Metering"), mld);

	ComboOption<VUMeterStandard>* mvu = new ComboOption<VUMeterStandard> (
		"meter-vu-standard",
		_("VU Meter standard"),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_vu_standard),
		sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_vu_standard)
		);

	mvu->add (MeteringVUfrench,   _("0VU = -2dBu (France)"));
	mvu->add (MeteringVUamerican, _("0VU = 0dBu (North America, Australia)"));
	mvu->add (MeteringVUstandard, _("0VU = +4dBu (standard)"));
	mvu->add (MeteringVUeight,    _("0VU = +8dBu"));

	add_option (S_("Preferences|Metering"), mvu);

	HSliderOption *mpks = new HSliderOption("meter-peak",
			_("Peak indicator threshold [dBFS]"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_meter_peak),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_meter_peak),
			-10, 0, .1, .1
			);

	Gtkmm2ext::UI::instance()->set_tip (
			mpks->tip_widget(),
			_("Specify the audio signal level in dBFS at and above which the meter-peak indicator will flash red."));

	add_option (S_("Preferences|Metering"), mpks);

	OptionEditorHeading* default_meter_head = new OptionEditorHeading (_("Default Meter Types"));
	default_meter_head->set_note (_("These settings apply to newly created tracks and busses. For the Master bus, this will be when a new session is created."));

	add_option (S_("Preferences|Metering"), default_meter_head);

	ComboOption<MeterType>* mtm = new ComboOption<MeterType> (
		"meter-type-master",
		_("Default Meter Type for Master Bus"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_type_master),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_type_master)
		);
	mtm->add (MeterPeak,    ArdourMeter::meter_type_string(MeterPeak));
	mtm->add (MeterPeak0dB, ArdourMeter::meter_type_string(MeterPeak0dB));
	mtm->add (MeterK20,     ArdourMeter::meter_type_string(MeterK20));
	mtm->add (MeterK14,     ArdourMeter::meter_type_string(MeterK14));
	mtm->add (MeterK12,     ArdourMeter::meter_type_string(MeterK12));
	mtm->add (MeterIEC1DIN, ArdourMeter::meter_type_string(MeterIEC1DIN));
	mtm->add (MeterIEC1NOR, ArdourMeter::meter_type_string(MeterIEC1NOR));
	mtm->add (MeterIEC2BBC, ArdourMeter::meter_type_string(MeterIEC2BBC));
	mtm->add (MeterIEC2EBU, ArdourMeter::meter_type_string(MeterIEC2EBU));

	add_option (S_("Preferences|Metering"), mtm);

	ComboOption<MeterType>* mtb = new ComboOption<MeterType> (
		"meter-type-bus",
		_("Default meter type for busses"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_type_bus),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_type_bus)
		);
	mtb->add (MeterPeak,    ArdourMeter::meter_type_string(MeterPeak));
	mtb->add (MeterPeak0dB, ArdourMeter::meter_type_string(MeterPeak0dB));
	mtb->add (MeterK20,     ArdourMeter::meter_type_string(MeterK20));
	mtb->add (MeterK14,     ArdourMeter::meter_type_string(MeterK14));
	mtb->add (MeterK12,     ArdourMeter::meter_type_string(MeterK12));
	mtb->add (MeterIEC1DIN, ArdourMeter::meter_type_string(MeterIEC1DIN));
	mtb->add (MeterIEC1NOR, ArdourMeter::meter_type_string(MeterIEC1NOR));
	mtb->add (MeterIEC2BBC, ArdourMeter::meter_type_string(MeterIEC2BBC));
	mtb->add (MeterIEC2EBU, ArdourMeter::meter_type_string(MeterIEC2EBU));

	add_option (S_("Preferences|Metering"), mtb);

	ComboOption<MeterType>* mtt = new ComboOption<MeterType> (
		"meter-type-track",
		_("Default meter type for tracks"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_type_track),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_type_track)
		);
	mtt->add (MeterPeak,    ArdourMeter::meter_type_string(MeterPeak));
	mtt->add (MeterPeak0dB, ArdourMeter::meter_type_string(MeterPeak0dB));
	mtt->add (MeterK20,     ArdourMeter::meter_type_string(MeterK20));
	mtt->add (MeterK14,     ArdourMeter::meter_type_string(MeterK14));
	mtt->add (MeterK12,     ArdourMeter::meter_type_string(MeterK12));
	mtt->add (MeterIEC1DIN, ArdourMeter::meter_type_string(MeterIEC1DIN));
	mtt->add (MeterIEC1NOR, ArdourMeter::meter_type_string(MeterIEC1NOR));
	mtt->add (MeterIEC2BBC, ArdourMeter::meter_type_string(MeterIEC2BBC));
	mtt->add (MeterIEC2EBU, ArdourMeter::meter_type_string(MeterIEC2EBU));

	add_option (S_("Preferences|Metering"), mtt);

	add_option (S_("Preferences|Metering"), new OptionEditorHeading (_("Region Analysis")));  //needs translation

	add_option (S_("Preferences|Metering"),
	     new BoolOption (
		     "auto-analyse-audio",
		     _("Enable automatic analysis of audio"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_analyse_audio),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_analyse_audio)
		     ));


	/* PERFORMANCE **************************************************************/

	uint32_t hwcpus = hardware_concurrency ();

	if (hwcpus > 1) {
		add_option (_("Performance"), new OptionEditorHeading (_("DSP CPU Utilization")));

		ComboOption<int32_t>* procs = new ComboOption<int32_t> (
				"processor-usage",
				_("Signal processing uses"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_processor_usage),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_processor_usage)
				);

		procs->add (-1, _("all but one processor"));
		procs->add (0, _("all available processors"));

		for (uint32_t i = 1; i <= hwcpus; ++i) {
			procs->add (i, string_compose (P_("%1 processor", "%1 processors", i), i));
		}

		procs->set_note (string_compose (_("This setting will only take effect when %1 is restarted."), PROGRAM_NAME));

		add_option (_("Performance"), procs);
	}

#if !(defined PLATFORM_WINDOWS || defined __APPLE__)
	if (Glib::file_test ("/dev/cpu_dma_latency", Glib::FILE_TEST_EXISTS)) {

		ComboOption<int32_t>* cpudma = new ComboOption<int32_t> (
				"cpu-dma-latency",
				_("Power Management, CPU DMA latency"),
				sigc::mem_fun (*_rc_config, &RCConfiguration::get_cpu_dma_latency),
				sigc::mem_fun (*_rc_config, &RCConfiguration::set_cpu_dma_latency)
				);

		set<int> lvalues;
		vector<string> latency_files;
		Searchpath sp ("/sys/devices/system/cpu/cpu0/cpuidle/");
		find_files_matching_regex (latency_files, sp, "latency$", true);
		for (vector<string>::const_iterator i = latency_files.begin(); i != latency_files.end (); ++i) {
			try {
				std::string l = Glib::file_get_contents (*i);
				int lv = atoi (l.c_str());
				if (lv > 0) {
					lvalues.insert (lv);
				}
			} catch (...) { }
		}

		if (lvalues.empty ()) {
			lvalues.insert (5);
			lvalues.insert (55);
			lvalues.insert (100);
		}

		int32_t cpudma_val = _rc_config->get_cpu_dma_latency ();
		if (cpudma_val > 0) {
			lvalues.insert (cpudma_val);
		}

		cpudma->add (-1, _("Unset"));
		cpudma->add (0, _("Lowest (prevent CPU sleep states)"));

		for (set<int>::const_iterator i = lvalues.begin(); i != lvalues.end (); ++i) {
			cpudma->add (*i, string_compose (_("%1 usec"), *i));
		}

		set_tooltip (cpudma->tip_widget(), _("This setting sets the maximum tolerable CPU DMA latency. This prevents the CPU from entering power-save states which can be beneficial for reliable low latency."));

		if (access ("/dev/cpu_dma_latency", W_OK)) {
			cpudma->set_note (_("This setting requires write access to `/dev/cpu_dma_latency'."));
		}

		add_option (_("Performance"), cpudma);
	}

#endif


	add_option (_("Performance"), new OptionEditorHeading (_("CPU/FPU Denormals")));

	add_option (_("Performance"),
	     new BoolOption (
		     "denormal-protection",
		     _("Use DC bias to protect against denormals"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_protection),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_protection)
		     ));

	ComboOption<DenormalModel>* dm = new ComboOption<DenormalModel> (
		"denormal-model",
		_("Processor handling"),
		sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_model),
		sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_model)
		);

	int dmsize = 1;
	dm->add (DenormalNone, _("no processor handling"));

	FPU* fpu = FPU::instance();

	if (fpu->has_flush_to_zero()) {
		++dmsize;
		dm->add (DenormalFTZ, _("use FlushToZero"));
	} else if (_rc_config->get_denormal_model() == DenormalFTZ) {
		_rc_config->set_denormal_model(DenormalNone);
	}

	if (fpu->has_denormals_are_zero()) {
		++dmsize;
		dm->add (DenormalDAZ, _("use DenormalsAreZero"));
	} else if (_rc_config->get_denormal_model() == DenormalDAZ) {
		_rc_config->set_denormal_model(DenormalNone);
	}

	if (fpu->has_flush_to_zero() && fpu->has_denormals_are_zero()) {
		++dmsize;
		dm->add (DenormalFTZDAZ, _("use FlushToZero and DenormalsAreZero"));
	} else if (_rc_config->get_denormal_model() == DenormalFTZDAZ) {
		_rc_config->set_denormal_model(DenormalNone);
	}

	if (dmsize == 1) {
		dm->set_sensitive(false);
	}

	dm->set_note (_("Changes may not be effective until audio-engine restart."));

	add_option (_("Performance"), dm);


	add_option (_("Performance"), new OptionEditorHeading (_("Disk I/O Buffering"))); //ToDo: this changed, needs translation.  disambiguated from soundcard i/o buffering

	add_option (_("Performance"), new BufferingOptions (_rc_config));

	/* Image cache size */
	add_option (_("Performance"), new OptionEditorHeading (_("Memory Usage")));

	HSliderOption *sics = new HSliderOption ("waveform-cache-size",
			_("Waveform image cache size (megabytes)"),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_waveform_cache_size),
			sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_waveform_cache_size),
			1, 1024, 10 /* 1 MB to 1GB in steps of 10MB */
			);
	sics->scale().set_digits (0);
	Gtkmm2ext::UI::instance()->set_tip (
			sics->tip_widget(),
		 _("Increasing the cache size uses more memory to store waveform images, which can improve graphical performance."));
	add_option (_("Performance"), sics);

	add_option (_("Performance"), new OptionEditorHeading (_("Automation")));

	add_option (_("Performance"),
	     new SpinOption<double> (
		     "automation-thinning-factor",
		     _("Thinning factor (larger value => less data)"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_thinning_factor),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_thinning_factor),
		     0, 1000, 1, 20
		     ));

	add_option (_("Performance"),
	     new SpinOption<double> (
		     "automation-interval-msecs",
		     _("Automation sampling interval (milliseconds)"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_interval_msecs),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_interval_msecs),
		     1, 1000, 1, 20
		     ));

	add_option (_("Performance"), new OptionEditorHeading (_("Automatables")));

	ComboOption<uint32_t>* lna = new ComboOption<uint32_t> (
		     "limit-n-automatables",
		     _("Limit automatable parameters per plugin"),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::get_limit_n_automatables),
		     sigc::mem_fun (*_rc_config, &RCConfiguration::set_limit_n_automatables)
		     );
	lna->add (0, _("Unlimited"));
	lna->add (64,  _("64 parameters"));
	lna->add (128, _("128 parameters"));
	lna->add (256, _("256 parameters"));
	lna->add (512, _("512 parameters"));
	lna->add (999, _("999 parameters"));
	add_option (_("Performance"), lna);
	Gtkmm2ext::UI::instance()->set_tip (lna->tip_widget(),
					    _("Some Plugins expose an unreasonable amount of control-inputs. This option limits the number of parameters that can are listed as automatable without restricting the number of total controls.\n\nThis reduces lag in the GUI and shortens excessively long drop-down lists for plugins with a large number of control ports.\n\nNote: This only affects newly added plugins and is applied to plugin on session-reload. Already automated parameters are retained."));

	/* VIDEO Timeline */
	add_option (_("Video"), new OptionEditorHeading (_("Video Server")));
	add_option (_("Video"), new VideoTimelineOptions (_rc_config));

	/* trigger launcing */

	add_option (_("Triggering"), new OptionEditorHeading (_("Triggering")));

	TriggerPortSelectOption* dtip  = new TriggerPortSelectOption (_rc_config, this);

	set_tooltip (dtip->tip_widget(), _("If set, this identifies the input MIDI port that will be automatically connected to trigger boxes.\n\n"
	                                   "It is intended to be connected to a NxN pad device (such as the Ableton Push 2 or Novation Launchpad)\n"
	                                   "or a regular MIDI device capable of sending sequential note numbers (like a typical keyboard)"));
	add_option (_("Triggering"), dtip);

	add_option (_("Triggering"), new OptionEditorHeading (_("Clip Library")));

	add_option (_("Triggering"), new DirectoryOption (
			    X_("clip-library-dir"),
			    _("User writable Clip Library:"),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::get_clip_library_dir),
			    sigc::mem_fun (*_rc_config, &RCConfiguration::set_clip_library_dir)
			    ));

	add_option (_("Triggering"),
			new RcActionButton (_("Reset Clip Library Dir"),
				sigc::mem_fun (*this, &RCOptionEditor::reset_clip_library_dir)));

	/* END OF SECTIONS/OPTIONS etc */

	Widget::show_all ();

	//trigger some parameter-changed messages which affect widget-visibility or -sensitivity
	parameter_changed ("send-ltc");
	parameter_changed ("sync-source");
	parameter_changed ("open-gui-after-adding-plugin");

#ifdef __APPLE__
	parameter_changed ("use-opengl-view");
#endif

	XMLNode* node = ARDOUR_UI::instance()->preferences_settings();
	if (node) {
		/* gcc4 complains about ambiguity with Gtk::Widget::set_state
		   (Gtk::StateType) here !!!
		*/
		Tabbable::set_state (*node, Stateful::loading_state_version);
	}

	set_current_page (_("General"));

	/* Place the search entry */

	treeview_packer.pack_end (search_packer, false, false);

	/* Connect metadata */

	for (auto p : pages()) {
		for (auto oc : p.second->components) {
			Option* o = dynamic_cast<Option*> (oc);
			if (o) {
				Configuration::Metadata const * m = Configuration::get_metadata (o->id());
				if (m) {
					oc->set_metadata (*m);
				}
			}
		}
	}
}

bool
RCOptionEditor::on_key_release_event (GdkEventKey* event)
{
	if (Keyboard::modifier_state_equals (event->state, Keyboard::close_window_modifier)) {
		if (event->keyval == (guint) Keyboard::close_window_key) {
			WindowProxy::hide ();
			return true;
		}
	}

	return false;
}

void
RCOptionEditor::set_session (Session *s)
{
	SessionHandlePtr::set_session (s);
	_transport_masters_widget.set_session (s);
}

void
RCOptionEditor::parameter_changed (string const & p)
{
	OptionEditor::parameter_changed (p);

	if (p == "use-monitor-bus") {
		bool const s = Config->get_use_monitor_bus ();
		if (!s) {
			/* we can't use this if we don't have a monitor bus */
			Config->set_solo_control_is_listen_control (false); // XXX
		}
		_solo_control_is_listen_control->set_sensitive (s);
		_listen_position->set_sensitive (s);
	} else if (p == "sync-source") {
		std::shared_ptr<TransportMaster> tm (TransportMasterManager::instance().current());
		if (std::dynamic_pointer_cast<TimecodeTransportMaster> (tm)) {
			_sync_framerate->set_sensitive (true);
		} else {
			_sync_framerate->set_sensitive (false);
		}
	} else if (p == "send-ltc") {
		bool const s = Config->get_send_ltc ();
		_ltc_send_continuously->set_sensitive (s);
		_ltc_volume_slider->set_sensitive (s);
	} else if (p == "open-gui-after-adding-plugin" || p == "show-inline-display-by-default") {
#ifdef LV2_EXTENDED
		_plugin_prefer_inline->set_sensitive (UIConfiguration::instance().get_open_gui_after_adding_plugin() && UIConfiguration::instance().get_show_inline_display_by_default());
#endif
	} else if (p == "conceal-lv1-if-lv2-exists") {
		plugin_scan_refresh ();
	} else if (p == "conceal-vst2-if-vst3-exists") {
		plugin_scan_refresh ();
	}
}

void RCOptionEditor::reset_clip_library_dir () {
	_rc_config->set_clip_library_dir ("@default@");
	clip_library_dir (false);
}

void RCOptionEditor::show_audio_setup () {
	Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action ("Window", "toggle-audio-midi-setup");
	tact->set_active();
}

void RCOptionEditor::show_transport_masters () {
	Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action ("Window", "toggle-transport-masters");
	tact->set_active();
}

void RCOptionEditor::plugin_scan_refresh () {
	/* first argument says discover new plugins, second means be verbose */
	PluginScanDialog psd (false, true, current_toplevel ());
	psd.start ();
	ARDOUR_UI::instance()->show_plugin_manager ();
}

void RCOptionEditor::plugin_reset_stats () {
	PluginManager::instance().reset_stats();
}

void RCOptionEditor::clear_vst2_cache () {
	PluginManager::instance().clear_vst_cache();
}

void RCOptionEditor::clear_vst2_blacklist () {
	PluginManager::instance().clear_vst_blacklist();
}

void RCOptionEditor::clear_vst3_cache () {
	PluginManager::instance().clear_vst3_cache();
}

void RCOptionEditor::clear_vst3_blacklist () {
	PluginManager::instance().clear_vst3_blacklist();
}

void RCOptionEditor::clear_au_cache () {
	PluginManager::instance().clear_au_cache();
}

void RCOptionEditor::clear_au_blacklist () {
	PluginManager::instance().clear_au_blacklist();
}

void
RCOptionEditor::edit_vst_path (std::string const& title, std::string const& dflt, sigc::slot<string> get, sigc::slot<bool, string> set)
{
	/* see also PluginManagerUI::edit_vst_path */
	PathsDialog pd (*current_toplevel(), title, get (), dflt);
	if (pd.run () != Gtk::RESPONSE_ACCEPT) {
		return;
	}
	pd.hide();
	set (pd.get_serialized_paths());
	MessageDialog msg (_("Re-scan Plugins now?"), false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
	msg.set_default_response (Gtk::RESPONSE_YES);
	if (msg.run() != Gtk::RESPONSE_YES) {
		return;
	}
	msg.hide ();
	plugin_scan_refresh ();
}


Gtk::Window*
RCOptionEditor::use_own_window (bool and_fill_it)
{
	bool new_window = !own_window ();

	Gtk::Window* win = Tabbable::use_own_window (and_fill_it);

	if (win && new_window) {
		win->set_name ("PreferencesWindow");
		ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Preferences"), this);
		win->resize (1, 1);
		win->set_resizable (false);
	}

	return win;
}

XMLNode&
RCOptionEditor::get_state () const
{
	XMLNode* node = new XMLNode (X_("Preferences"));
	node->add_child_nocopy (Tabbable::get_state());
	return *node;
}

std::string
RCOptionEditor::get_default_lower_midi_note ()
{
	return ParameterDescriptor::midi_note_name (UIConfiguration::instance().get_default_lower_midi_note());
}

bool
RCOptionEditor::set_default_lower_midi_note (std::string str)
{
	int note = ParameterDescriptor::midi_note_num (str);

	if (note < 0) {
		mru_option->set_state_from_config ();
		return false;
	}

	return UIConfiguration::instance().set_default_lower_midi_note (note);
}

std::string
RCOptionEditor::get_default_upper_midi_note ()
{
	return ParameterDescriptor::midi_note_name (UIConfiguration::instance().get_default_upper_midi_note());
}

bool
RCOptionEditor::set_default_upper_midi_note (std::string str)
{
	int note = ParameterDescriptor::midi_note_num (str);

	if (note < 0) {
		mru_option->set_state_from_config ();
		return false;
	}

	return UIConfiguration::instance().set_default_upper_midi_note (note);
}
