+
+void
+Mixer_UI::monitor_section_attached ()
+{
+ Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
+ Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
+ act->set_sensitive (true);
+ tact->set_active ();
+}
+
+void
+Mixer_UI::monitor_section_detached ()
+{
+ Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
+ act->set_sensitive (false);
+}
+
+void
+Mixer_UI::store_current_favorite_order ()
+{
+ typedef Gtk::TreeModel::Children type_children;
+ type_children children = favorite_plugins_model->children();
+ favorite_order.clear();
+ for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ Gtk::TreeModel::Row row = *iter;
+ ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
+ favorite_order.push_back (ppp->_pip);
+ std::string name = row[favorite_plugins_columns.name];
+ favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
+ }
+}
+
+void
+Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
+{
+ Gtk::TreeModel::Row row = *iter;
+ ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
+ assert (ppp);
+ favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
+}
+
+void
+Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
+{
+ PluginManager& manager (PluginManager::instance());
+ for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
+ if (manager.get_status (*i) != PluginManager::Favorite) {
+ continue;
+ }
+ result.push_back (*i);
+ }
+}
+
+struct PluginCustomSorter {
+public:
+ bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
+ PluginInfoList::const_iterator aiter = _user.begin();
+ PluginInfoList::const_iterator biter = _user.begin();
+ while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
+ while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
+
+ if (aiter != _user.end() && biter != _user.end()) {
+ return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
+ }
+ if (aiter != _user.end()) {
+ return true;
+ }
+ if (biter != _user.end()) {
+ return false;
+ }
+ return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
+ }
+ PluginCustomSorter(PluginInfoList user) : _user (user) {}
+private:
+ PluginInfoList _user;
+};
+
+void
+Mixer_UI::refill_favorite_plugins ()
+{
+ PluginInfoList plugs;
+ PluginManager& mgr (PluginManager::instance());
+
+#ifdef LV2_SUPPORT
+ refiller (plugs, mgr.lv2_plugin_info ());
+#endif
+#ifdef WINDOWS_VST_SUPPORT
+ refiller (plugs, mgr.windows_vst_plugin_info ());
+#endif
+#ifdef LXVST_SUPPORT
+ refiller (plugs, mgr.lxvst_plugin_info ());
+#endif
+#ifdef AUDIOUNIT_SUPPORT
+ refiller (plugs, mgr.au_plugin_info ());
+#endif
+ refiller (plugs, mgr.ladspa_plugin_info ());
+ refiller (plugs, mgr.lua_plugin_info ());
+
+ store_current_favorite_order ();
+
+ PluginCustomSorter cmp (favorite_order);
+ plugs.sort (cmp);
+
+ favorite_order = plugs;
+
+ sync_treeview_from_favorite_order ();
+}
+
+void
+Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
+{
+ TreeIter iter;
+ if (!(iter = favorite_plugins_model->get_iter (path))) {
+ return;
+ }
+ ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
+ if (!ppp) {
+ return;
+ }
+ PluginInfoPtr pip = ppp->_pip;
+ if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
+ if (favorite_ui_state[pip->unique_id]) {
+ favorite_plugins_display.expand_row (path, true);
+ }
+ }
+}
+
+void
+Mixer_UI::sync_treeview_from_favorite_order ()
+{
+ favorite_plugins_model->clear ();
+ for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
+ PluginInfoPtr pip = (*i);
+
+ TreeModel::Row newrow = *(favorite_plugins_model->append());
+ newrow[favorite_plugins_columns.name] = (*i)->name;
+ newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
+ if (!_session) {
+ continue;
+ }
+
+ vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
+ for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
+ Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
+ child_row[favorite_plugins_columns.name] = (*j).label;
+ child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
+ }
+ if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
+ if (favorite_ui_state[pip->unique_id]) {
+ favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
+ }
+ }
+ }
+}
+
+void
+Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
+{
+ using namespace Gtk::Menu_Helpers;
+
+ Gtk::Menu* m = manage (new Menu);
+ MenuList& items = m->items ();
+
+ if (_selection.axes.empty()) {
+ items.push_back (MenuElem (_("No Track/Bus is selected.")));
+ } else {
+ items.push_back (MenuElem (_("Add at the top"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
+ items.push_back (MenuElem (_("Add Pre-Fader"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
+ items.push_back (MenuElem (_("Add Post-Fader"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
+ items.push_back (MenuElem (_("Add at the end"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
+ }
+
+ items.push_back (SeparatorElem());
+
+ items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
+
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp && ppp->_preset.valid && ppp->_preset.user) {
+ // we cannot currently delete AU presets
+ if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
+ items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
+ }
+ }
+
+ m->popup (ev->button, ev->time);
+}
+
+bool
+Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
+{
+ if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
+ TreeModel::Path path;
+ TreeViewColumn* column;
+ int cellx, celly;
+ if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
+ if (selection) {
+ selection->unselect_all();
+ selection->select(path);
+ }
+ }
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp) {
+ popup_note_context_menu (ev);
+ }
+ }
+ return false;
+}
+
+
+PluginPresetPtr
+Mixer_UI::selected_plugin ()
+{
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
+ if (!selection) {
+ return PluginPresetPtr();
+ }
+ Gtk::TreeModel::iterator iter = selection->get_selected();
+ if (!iter) {
+ return PluginPresetPtr();
+ }
+ return (*iter)[favorite_plugins_columns.plugin];
+}
+
+void
+Mixer_UI::add_selected_processor (ProcessorPosition pos)
+{
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp) {
+ add_favorite_processor (ppp, pos);
+ }
+}
+
+void
+Mixer_UI::delete_selected_preset ()
+{
+ if (!_session) {
+ return;
+ }
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
+ return;
+ }
+ PluginPtr plugin = ppp->_pip->load (*_session);
+ plugin->get_presets();
+ plugin->remove_preset (ppp->_preset.label);
+}
+
+void
+Mixer_UI::remove_selected_from_favorites ()
+{
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (!ppp) {
+ return;
+ }
+ PluginManager::PluginStatusType status = PluginManager::Normal;
+ PluginManager& manager (PluginManager::instance());
+
+ manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
+ manager.save_statuses ();
+}
+
+void
+Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
+{
+ TreeIter iter;
+ if (!(iter = favorite_plugins_model->get_iter (path))) {
+ return;
+ }
+ ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
+ add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
+}
+
+void
+Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
+{
+ if (!_session || _selection.axes.empty()) {
+ return;
+ }
+
+ PluginInfoPtr pip = ppp->_pip;
+ for (AxisViewSelection::iterator i = _selection.axes.begin(); i != _selection.axes.end(); ++i) {
+ boost::shared_ptr<ARDOUR::Route> rt = boost::dynamic_pointer_cast<ARDOUR::Route> ((*i)->stripable());
+
+ if (!rt) {
+ continue;
+ }
+
+ PluginPtr p = pip->load (*_session);
+
+ if (!p) {
+ continue;
+ }
+
+ if (ppp->_preset.valid) {
+ p->load_preset (ppp->_preset);
+ }
+
+ Route::ProcessorStreams err;
+ boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
+
+ switch (pos) {
+ case AddTop:
+ rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
+ break;
+ case AddPreFader:
+ rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
+ break;
+ case AddPostFader:
+ {
+ int idx = 0;
+ int pos = 0;
+ for (;;++idx) {
+ boost::shared_ptr<Processor> np = rt->nth_processor (idx);
+ if (!np) {
+ break;
+ }
+ if (!np->display_to_user()) {
+ continue;
+ }
+ if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
+ boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
+ break;
+ }
+ ++pos;
+ }
+ rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
+ }
+ break;
+ case AddBottom:
+ rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
+ break;
+ }
+ }
+}
+
+bool
+PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
+{
+ if (data.get_target() != "GTK_TREE_MODEL_ROW") {
+ return false;
+ }
+
+ // only allow to re-order top-level items
+ TreePath src;
+ if (TreePath::get_from_selection_data (data, src)) {
+ if (src.up() && src.up()) {
+ return false;
+ }
+ }
+
+ // don't allow to drop as child-rows.
+ Gtk::TreeModel::Path _dest = dest; // un const
+ const bool is_child = _dest.up (); // explicit bool for clang
+ if (!is_child || _dest.empty ()) {
+ return true;
+ }
+ return false;
+}
+
+void
+Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
+{
+ if (data.get_target() != "PluginPresetPtr") {
+ return;
+ }
+ if (data.get_length() != sizeof (PluginPresetPtr)) {
+ return;
+ }
+ const void *d = data.get_data();
+ const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
+
+ PluginManager::PluginStatusType status = PluginManager::Favorite;
+ PluginManager& manager (PluginManager::instance());
+
+ manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
+ manager.save_statuses ();
+}
+
+void
+Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
+{
+ /* call protected MixerActor:: method */
+ vca_assign (vca);
+}
+
+void
+Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
+{
+ /* call protected MixerActor:: method */
+ vca_unassign (vca);
+}
+
+void
+Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
+{
+ boost::shared_ptr<VCA> v = spilled_vca.lock();
+ if (v != vca) {
+ spilled_vca = vca;
+ show_vca_change (vca); /* EMIT SIGNAL */
+ if (vca) {
+ _group_tabs->hide ();
+ } else {
+ _group_tabs->show ();
+ }
+ redisplay_track_list ();
+ }
+}
+
+bool
+Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
+{
+ return vca == spilled_vca.lock();
+}
+
+void
+Mixer_UI::register_actions ()
+{
+ Glib::RefPtr<ActionGroup> group = myactions.create_action_group (X_("Mixer"));
+
+ myactions.register_action (group, "solo", _("Toggle Solo on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::solo_action));
+ myactions.register_action (group, "mute", _("Toggle Mute on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::mute_action));
+ myactions.register_action (group, "recenable", _("Toggle Rec-enable on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::rec_enable_action));
+ myactions.register_action (group, "increment-gain", _("Decrease Gain on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::step_gain_up_action));
+ myactions.register_action (group, "decrement-gain", _("Increase Gain on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::step_gain_down_action));
+ myactions.register_action (group, "unity-gain", _("Set Gain to 0dB on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::unity_gain_action));
+
+
+ myactions.register_action (group, "copy-processors", _("Copy Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::copy_processors));
+ myactions.register_action (group, "cut-processors", _("Cut Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::cut_processors));
+ myactions.register_action (group, "paste-processors", _("Paste Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::paste_processors));
+ myactions.register_action (group, "delete-processors", _("Delete Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::delete_processors));
+ myactions.register_action (group, "select-all-processors", _("Select All (visible) Processors"), sigc::mem_fun (*this, &Mixer_UI::select_all_processors));
+ myactions.register_action (group, "toggle-processors", _("Toggle Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::toggle_processors));
+ myactions.register_action (group, "ab-plugins", _("Toggle Selected Plugins"), sigc::mem_fun (*this, &Mixer_UI::ab_plugins));
+ myactions.register_action (group, "select-none", _("Deselect all strips and processors"), sigc::mem_fun (*this, &Mixer_UI::select_none));
+
+ myactions.register_action (group, "scroll-left", _("Scroll Mixer Window to the left"), sigc::mem_fun (*this, &Mixer_UI::scroll_left));
+ myactions.register_action (group, "scroll-right", _("Scroll Mixer Window to the right"), sigc::mem_fun (*this, &Mixer_UI::scroll_right));
+
+ myactions.register_action (group, "toggle-midi-input-active", _("Toggle MIDI Input Active for Mixer-Selected Tracks/Busses"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::toggle_midi_input_active), false));
+}
+
+void
+Mixer_UI::load_bindings ()
+{
+ bindings = Bindings::get_bindings (X_("Mixer"), myactions);
+}
+
+template<class T> void
+Mixer_UI::control_action (boost::shared_ptr<T> (Stripable::*get_control)() const)
+{
+ boost::shared_ptr<ControlList> cl (new ControlList);
+ boost::shared_ptr<AutomationControl> ac;
+ bool val = false;
+ bool have_val = false;
+
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ boost::shared_ptr<Stripable> s = r->stripable();
+ if (s) {
+ ac = (s.get()->*get_control)();
+ if (ac) {
+ cl->push_back (ac);
+ if (!have_val) {
+ val = !ac->get_value();
+ have_val = true;
+ }
+ }
+ }
+ }
+
+ _session->set_controls (cl, val, Controllable::UseGroup);
+}
+
+void
+Mixer_UI::solo_action ()
+{
+ control_action (&Stripable::solo_control);
+}
+
+void
+Mixer_UI::mute_action ()
+{
+ control_action (&Stripable::mute_control);
+}
+
+void
+Mixer_UI::rec_enable_action ()
+{
+ control_action (&Stripable::rec_enable_control);
+}
+
+void
+Mixer_UI::step_gain_up_action ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->step_gain_up ();
+ }
+ }
+}
+
+void
+Mixer_UI::step_gain_down_action ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->step_gain_down ();
+ }
+ }
+}
+
+void
+Mixer_UI::unity_gain_action ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ boost::shared_ptr<Stripable> s = r->stripable();
+ if (s) {
+ boost::shared_ptr<AutomationControl> ac = s->gain_control();
+ if (ac) {
+ ac->set_value (1.0, Controllable::UseGroup);
+ }
+ }
+ }
+}
+
+void
+Mixer_UI::copy_processors ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->copy_processors ();
+ }
+ }
+}
+void
+Mixer_UI::cut_processors ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->cut_processors ();
+ }
+ }
+}
+void
+Mixer_UI::paste_processors ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->paste_processors ();
+ }
+ }
+}
+void
+Mixer_UI::select_all_processors ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->select_all_processors ();
+ }
+ }
+}
+void
+Mixer_UI::toggle_processors ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->toggle_processors ();
+ }
+ }
+}
+void
+Mixer_UI::ab_plugins ()
+{
+ set_axis_targets_for_operation ();
+
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->ab_plugins ();
+ }
+ }
+}
+
+void
+Mixer_UI::vca_assign (boost::shared_ptr<VCA> vca)
+{
+ set_axis_targets_for_operation ();
+#if 0
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->vca_assign (vca);
+ }
+ }
+#endif
+}
+
+void
+Mixer_UI::vca_unassign (boost::shared_ptr<VCA> vca)
+{
+ set_axis_targets_for_operation ();
+#if 0
+ BOOST_FOREACH(AxisView* r, _axis_targets) {
+ MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
+ if (ms) {
+ ms->vca_unassign (vca);
+ }
+ }
+#endif
+}