+ 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::Label input_label;
+ Gtk::Label output_label;
+ int refill_id;
+
+ 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;
+ 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_known_midi_ports (ports);
+
+ if (ports.empty()) {
+ view.hide ();
+ return false;
+ }
+
+ Glib::RefPtr<ListStore> model = Gtk::ListStore::create (midi_port_columns);
+
+ for (vector<string>::const_iterator s = ports.begin(); s != ports.end(); ++s) {
+
+ if (AudioEngine::instance()->port_is_mine (*s)) {
+ continue;
+ }
+
+ PortManager::MidiPortInformation mpi (AudioEngine::instance()->midi_port_information (*s));
+
+ if (!mpi.exists) {
+ continue;
+ }
+
+ if (for_input != mpi.input) {
+ continue;
+ }
+
+ TreeModel::Row row = *(model->append());
+
+ row[midi_port_columns.pretty_name] = mpi.pretty_name;
+ row[midi_port_columns.music_data] = mpi.properties & MidiPortMusic;
+ row[midi_port_columns.control_data] = mpi.properties & MidiPortControl;
+ row[midi_port_columns.selection] = mpi.properties & MidiPortSelection;
+ row[midi_port_columns.fullname] = *s;
+ row[midi_port_columns.shortname] = AudioEngine::instance()->short_port_name_from_port_name (*s);
+ }
+
+ view.set_model (model);
+
+ 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, string_compose (_("%1 Preferences"), PROGRAM_NAME))
+ , Tabbable (*this, _("Preferences")
+#ifdef MIXBUS
+ , false // detached by default (first start, no instant.xml)
+#endif
+ ) /* pack self-as-vbox into tabbable */
+ , _rc_config (Config)
+ , _mixer_strip_visibility ("mixer-element-visibility")
+{
+ UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RCOptionEditor::parameter_changed));
+
+ /* MISC */
+
+ uint32_t hwcpus = hardware_concurrency ();
+ BoolOption* bo;
+
+ if (hwcpus > 1) {
+ add_option (_("General"), 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 (_("General"), procs);
+ }
+
+ /* Image cache size */
+ add_option (_("General"), 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 (_("General"), sics);
+
+ add_option (_("General"), new OptionEditorHeading (_("Engine")));
+
+ add_option (_("General"),
+ 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)