2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include <sigc++/bind.h>
28 #include <gtkmm/accelmap.h>
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/unwind.h"
34 #include <glibmm/threads.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/keyboard.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/tearoff.h>
40 #include <gtkmm2ext/window_title.h>
41 #include <gtkmm2ext/doi.h>
43 #include "ardour/amp.h"
44 #include "ardour/debug.h"
45 #include "ardour/audio_track.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/plugin_manager.h"
48 #include "ardour/route_group.h"
49 #include "ardour/session.h"
50 #include "ardour/vca.h"
51 #include "ardour/vca_manager.h"
55 #include "mixer_strip.h"
56 #include "monitor_section.h"
57 #include "plugin_selector.h"
58 #include "public_editor.h"
59 #include "ardour_ui.h"
62 #include "route_sorter.h"
64 #include "gui_thread.h"
65 #include "mixer_group_tabs.h"
67 #include "ui_config.h"
68 #include "vca_master_strip.h"
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
83 Mixer_UI* Mixer_UI::_instance = 0;
89 _instance = new Mixer_UI;
96 : Tabbable (_content, _("Mixer"))
97 , no_track_list_redisplay (false)
98 , in_group_row_change (false)
100 , _monitor_section (0)
101 , _plugin_selector (0)
102 , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
103 , ignore_reorder (false)
104 , _in_group_rebuild_or_clear (false)
105 , _route_deletion_in_progress (false)
106 , _following_editor_selection (false)
108 , _show_mixer_list (true)
110 Stripable::PresentationInfoChange.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_presentation_info, this), gui_context());
112 /* bindings was already set in MixerActor constructor */
114 _content.set_data ("ardour-bindings", bindings);
116 scroller.set_can_default (true);
117 // set_default (scroller);
119 scroller_base.set_flags (Gtk::CAN_FOCUS);
120 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
121 scroller_base.set_name ("MixerWindow");
122 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
124 /* set up drag-n-drop */
125 vector<TargetEntry> target_table;
126 target_table.push_back (TargetEntry ("PluginFavoritePtr"));
127 scroller_base.drag_dest_set (target_table);
128 scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
130 // add as last item of strip packer
131 strip_packer.pack_end (scroller_base, true, true);
133 _group_tabs = new MixerGroupTabs (this);
134 VBox* b = manage (new VBox);
135 b->pack_start (*_group_tabs, PACK_SHRINK);
136 b->pack_start (strip_packer);
140 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
142 setup_track_display ();
144 group_model = ListStore::create (group_columns);
145 group_display.set_model (group_model);
146 group_display.append_column (_("Group"), group_columns.text);
147 group_display.append_column (_("Show"), group_columns.visible);
148 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
149 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
150 group_display.get_column (0)->set_expand(true);
151 group_display.get_column (1)->set_expand(false);
152 group_display.set_name ("EditGroupList");
153 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
154 group_display.set_reorderable (true);
155 group_display.set_headers_visible (true);
156 group_display.set_rules_hint (true);
157 group_display.set_can_focus(false);
159 /* name is directly editable */
161 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
162 name_cell->property_editable() = true;
163 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
165 /* use checkbox for the active column */
167 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
168 active_cell->property_activatable() = true;
169 active_cell->property_radio() = false;
171 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
172 /* We use this to notice drag-and-drop reorders of the group list */
173 group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
174 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
176 group_display_scroller.add (group_display);
177 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
179 HBox* route_group_display_button_box = manage (new HBox());
181 Button* route_group_add_button = manage (new Button ());
182 Button* route_group_remove_button = manage (new Button ());
186 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
188 route_group_add_button->add (*w);
190 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
192 route_group_remove_button->add (*w);
194 route_group_display_button_box->set_homogeneous (true);
196 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
197 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
199 route_group_display_button_box->add (*route_group_add_button);
200 route_group_display_button_box->add (*route_group_remove_button);
202 group_display_vbox.pack_start (group_display_scroller, true, true);
203 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
205 group_display_frame.set_name ("BaseFrame");
206 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
207 group_display_frame.add (group_display_vbox);
210 list<TargetEntry> target_list;
211 target_list.push_back (TargetEntry ("PluginPresetPtr"));
213 favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
214 favorite_plugins_display.set_model (favorite_plugins_model);
215 favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
216 favorite_plugins_display.set_name ("EditGroupList");
217 favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
218 favorite_plugins_display.set_reorderable (false);
219 favorite_plugins_display.set_headers_visible (true);
220 favorite_plugins_display.set_rules_hint (true);
221 favorite_plugins_display.set_can_focus (false);
222 favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
223 favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
224 favorite_plugins_display.add_drop_targets (target_list);
225 favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
226 favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
227 favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
228 favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
229 favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
230 favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
232 favorite_plugins_scroller.add (favorite_plugins_display);
233 favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
235 favorite_plugins_frame.set_name ("BaseFrame");
236 favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
237 favorite_plugins_frame.add (favorite_plugins_scroller);
239 rhs_pane1.pack1 (favorite_plugins_frame, false, true);
240 rhs_pane1.pack2 (track_display_frame);
241 rhs_pane2.pack1 (rhs_pane1);
242 rhs_pane2.pack2 (group_display_frame);
244 list_vpacker.pack_start (rhs_pane2, true, true);
246 vca_scroller.add (vca_packer);
247 vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
249 inner_pane.pack1 (scroller);
250 inner_pane.pack2 (vca_scroller);
252 global_hpacker.pack_start (inner_pane, true, true);
253 global_hpacker.pack_start (out_packer, false, false);
255 list_hpane.pack1(list_vpacker, false, true);
256 list_hpane.pack2(global_hpacker, true, false);
258 rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
259 static_cast<Gtk::Paned*> (&rhs_pane1)));
260 rhs_pane2.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
261 static_cast<Gtk::Paned*> (&rhs_pane2)));
262 list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
263 static_cast<Gtk::Paned*> (&list_hpane)));
264 inner_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
265 static_cast<Gtk::Paned*> (&inner_pane)));
267 _content.pack_start (list_hpane, true, true);
271 route_group_display_button_box->show();
272 route_group_add_button->show();
273 route_group_remove_button->show();
276 _content.set_name ("MixerWindow");
278 global_hpacker.show();
280 scroller_base.show();
281 scroller_hpacker.show();
282 mixer_scroller_vpacker.show();
284 group_display_button_label.show();
285 group_display_button.show();
286 group_display_scroller.show();
287 favorite_plugins_scroller.show();
288 group_display_vbox.show();
289 group_display_frame.show();
290 favorite_plugins_frame.show();
299 group_display.show();
300 favorite_plugins_display.show();
302 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
304 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
305 _plugin_selector = new PluginSelector (PluginManager::instance ());
307 #error implement deferred Plugin-Favorite list
309 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
310 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
311 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
314 Mixer_UI::~Mixer_UI ()
316 if (_monitor_section) {
317 monitor_section_detached ();
318 delete _monitor_section;
320 delete _plugin_selector;
324 Mixer_UI::track_editor_selection ()
326 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
330 Mixer_UI::use_own_window (bool and_fill_it)
332 bool new_window = !own_window();
334 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
337 if (win && new_window) {
338 win->set_name ("MixerWindow");
339 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
340 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
341 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
342 win->set_data ("ardour-bindings", bindings);
350 Mixer_UI::show_window ()
352 Tabbable::show_window ();
354 /* show/hide group tabs as required */
355 parameter_changed ("show-group-tabs");
357 /* now reset each strips width so the right widgets are shown */
360 TreeModel::Children rows = track_model->children();
361 TreeModel::Children::iterator ri;
363 for (ri = rows.begin(); ri != rows.end(); ++ri) {
364 ms = (*ri)[track_columns.strip];
368 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
369 /* Fix visibility of mixer strip stuff */
370 ms->parameter_changed (X_("mixer-element-visibility"));
373 /* force focus into main area */
374 scroller_base.grab_focus ();
378 Mixer_UI::add_masters (VCAList& vcas)
380 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
382 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
384 TreeModel::Row row = *(track_model->append());
385 row[track_columns.text] = (*v)->name();
386 row[track_columns.visible] = true;
387 row[track_columns.vca] = vms;
389 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
392 redisplay_track_list ();
396 Mixer_UI::remove_master (VCAMasterStrip* vms)
398 if (_session && _session->deletion_in_progress()) {
399 /* its all being taken care of */
403 TreeModel::Children rows = track_model->children();
404 TreeModel::Children::iterator ri;
406 for (ri = rows.begin(); ri != rows.end(); ++ri) {
407 if ((*ri)[track_columns.vca] == vms) {
408 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
409 track_model->erase (ri);
416 Mixer_UI::add_strips (RouteList& routes)
418 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
419 uint32_t nroutes = 0;
421 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
422 boost::shared_ptr<Route> r = (*it)[track_columns.route];
430 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
437 _selection.clear_routes ();
443 no_track_list_redisplay = true;
444 track_display.set_model (Glib::RefPtr<ListStore>());
446 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
447 boost::shared_ptr<Route> route = (*x);
449 if (route->is_auditioner()) {
453 if (route->is_monitor()) {
455 if (!_monitor_section) {
456 _monitor_section = new MonitorSection (_session);
458 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
460 _monitor_section->tearoff().set_state (*mnode);
464 out_packer.pack_end (_monitor_section->tearoff(), false, false);
465 _monitor_section->set_session (_session);
466 _monitor_section->tearoff().show_all ();
468 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
469 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
471 monitor_section_attached ();
473 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
475 /* no regular strip shown for control out */
480 strip = new MixerStrip (*this, _session, route);
481 strips.push_back (strip);
483 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
485 if (strip->width_owner() != strip) {
486 strip->set_width_enum (_strip_width, this);
491 TreeModel::Row row = *(track_model->insert(insert_iter));
492 row[track_columns.text] = route->name();
493 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
494 row[track_columns.route] = route;
495 row[track_columns.strip] = strip;
496 row[track_columns.vca] = 0;
499 _selection.add (strip);
502 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
504 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
505 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
508 } catch (const std::exception& e) {
509 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
512 no_track_list_redisplay = false;
513 track_display.set_model (track_model);
515 sync_presentation_info_from_treeview ();
516 redisplay_track_list ();
520 Mixer_UI::deselect_all_strip_processors ()
522 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
523 (*i)->deselect_all_processors();
528 Mixer_UI::select_strip (MixerStrip& ms, bool add)
531 _selection.add (&ms);
533 _selection.set (&ms);
538 Mixer_UI::select_none ()
540 _selection.clear_routes();
541 deselect_all_strip_processors();
545 Mixer_UI::delete_processors ()
547 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
548 (*i)->delete_processors();
554 Mixer_UI::remove_strip (MixerStrip* strip)
556 if (_session && _session->deletion_in_progress()) {
557 /* its all being taken care of */
561 TreeModel::Children rows = track_model->children();
562 TreeModel::Children::iterator ri;
563 list<MixerStrip *>::iterator i;
565 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
569 for (ri = rows.begin(); ri != rows.end(); ++ri) {
570 if ((*ri)[track_columns.strip] == strip) {
571 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
572 track_model->erase (ri);
579 Mixer_UI::sync_presentation_info_from_treeview ()
581 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
585 TreeModel::Children rows = track_model->children();
591 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync order keys from model\n");
593 TreeModel::Children::iterator ri;
597 for (ri = rows.begin(); ri != rows.end(); ++ri) {
598 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
599 bool visible = (*ri)[track_columns.visible];
606 if (route->presentation_info().special()) {
611 route->presentation_info().set_flag (PresentationInfo::Hidden);
613 route->presentation_info().unset_flag (PresentationInfo::Hidden);
616 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("route %1 old order %2 new order %3\n", route->name(), route->presentation_info().group_order(), order));
618 if (order != route->presentation_info().group_order()) {
619 route->set_presentation_group_order_explicit (order);
627 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
628 _session->notify_presentation_info_change ();
633 Mixer_UI::sync_treeview_from_presentation_info ()
635 if (!_session || _session->deletion_in_progress()) {
639 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from order keys.\n");
641 /* we could get here after either a change in the Mixer or Editor sort
642 * order, but either way, the mixer order keys reflect the intended
643 * order for the GUI, so reorder the treeview model to match it.
646 vector<int> neworder;
647 TreeModel::Children rows = track_model->children();
648 uint32_t old_order = 0;
649 bool changed = false;
656 uint32_t vca_cnt = 0;
657 uint32_t max_route_order_key = 0;
659 /* count number of Routes in track_model (there may be some odd reason
660 why this is not the same as the number in the session, but here we
661 care about the track model.
664 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
665 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
667 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
671 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
672 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
674 /* VCAs need to sort after all routes. We don't display
675 * them in the same place (March 2016), but we don't
676 * want them intermixed in the track_model
678 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
680 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
684 SortByNewDisplayOrder cmp;
686 sort (sorted.begin(), sorted.end(), cmp);
687 neworder.assign (sorted.size(), 0);
691 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
693 neworder[n] = sr->old_display_order;
695 if (sr->old_display_order != n) {
699 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("MIXER change order from %1 to %2\n",
700 sr->old_display_order, n));
704 Unwinder<bool> uw (ignore_reorder, true);
705 track_model->reorder (neworder);
708 redisplay_track_list ();
712 Mixer_UI::follow_editor_selection ()
714 if (_following_editor_selection) {
718 _following_editor_selection = true;
719 _selection.block_routes_changed (true);
721 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
723 _selection.clear_routes ();
725 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
726 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
728 MixerStrip* ms = strip_by_route (rtav->route());
735 _following_editor_selection = false;
736 _selection.block_routes_changed (false);
741 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
743 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
744 if ((*i)->route() == r) {
753 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
755 if (ev->button == 1) {
756 if (_selection.selected (strip)) {
757 /* primary-click: toggle selection state of strip */
758 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
759 _selection.remove (strip);
760 } else if (_selection.routes.size() > 1) {
761 /* de-select others */
762 _selection.set (strip);
765 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
766 _selection.add (strip);
767 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
769 if (!_selection.selected(strip)) {
771 /* extend selection */
773 vector<MixerStrip*> tmp;
774 bool accumulate = false;
775 bool found_another = false;
777 tmp.push_back (strip);
779 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
781 /* hit clicked strip, start accumulating till we hit the first
790 } else if (_selection.selected (*i)) {
791 /* hit selected strip. if currently accumulating others,
792 we're done. if not accumulating others, start doing so.
794 found_another = true;
809 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
813 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
817 _selection.set (strip);
826 Mixer_UI::set_session (Session* sess)
828 SessionHandlePtr::set_session (sess);
830 if (_plugin_selector) {
831 _plugin_selector->set_session (_session);
834 _group_tabs->set_session (sess);
840 refill_favorite_plugins();
842 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
843 set_state (*node, 0);
847 initial_track_display ();
849 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
850 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
851 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
852 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
853 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
854 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
855 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
857 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
859 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
861 route_groups_changed ();
870 Mixer_UI::session_going_away ()
872 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
874 _in_group_rebuild_or_clear = true;
875 group_model->clear ();
876 _in_group_rebuild_or_clear = false;
879 track_model->clear ();
881 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
885 if (_monitor_section) {
886 _monitor_section->tearoff().hide_visible ();
889 monitor_section_detached ();
895 SessionHandlePtr::session_going_away ();
902 Mixer_UI::track_visibility_changed (std::string const & path)
904 if (_session && _session->deletion_in_progress()) {
910 if ((iter = track_model->get_iter (path))) {
911 MixerStrip* strip = (*iter)[track_columns.strip];
913 bool visible = (*iter)[track_columns.visible];
915 if (strip->set_marked_for_display (!visible)) {
916 update_track_visibility ();
923 Mixer_UI::update_track_visibility ()
925 TreeModel::Children rows = track_model->children();
926 TreeModel::Children::iterator i;
929 Unwinder<bool> uw (no_track_list_redisplay, true);
931 for (i = rows.begin(); i != rows.end(); ++i) {
932 MixerStrip *strip = (*i)[track_columns.strip];
934 (*i)[track_columns.visible] = strip->marked_for_display ();
938 /* force presentation catch up with visibility changes
941 sync_presentation_info_from_treeview ();
944 redisplay_track_list ();
948 Mixer_UI::show_strip (MixerStrip* ms)
950 TreeModel::Children rows = track_model->children();
951 TreeModel::Children::iterator i;
953 for (i = rows.begin(); i != rows.end(); ++i) {
955 MixerStrip* strip = (*i)[track_columns.strip];
957 (*i)[track_columns.visible] = true;
958 redisplay_track_list ();
965 Mixer_UI::hide_strip (MixerStrip* ms)
967 TreeModel::Children rows = track_model->children();
968 TreeModel::Children::iterator i;
970 for (i = rows.begin(); i != rows.end(); ++i) {
972 MixerStrip* strip = (*i)[track_columns.strip];
974 (*i)[track_columns.visible] = false;
975 redisplay_track_list ();
982 Mixer_UI::start_updating ()
984 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
989 Mixer_UI::stop_updating ()
991 fast_screen_update_connection.disconnect();
996 Mixer_UI::fast_update_strips ()
998 if (_content.is_mapped () && _session) {
999 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1000 (*i)->fast_update ();
1006 Mixer_UI::set_all_strips_visibility (bool yn)
1008 TreeModel::Children rows = track_model->children();
1009 TreeModel::Children::iterator i;
1012 Unwinder<bool> uw (no_track_list_redisplay, true);
1014 for (i = rows.begin(); i != rows.end(); ++i) {
1016 TreeModel::Row row = (*i);
1017 MixerStrip* strip = row[track_columns.strip];
1023 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1027 (*i)[track_columns.visible] = yn;
1031 redisplay_track_list ();
1036 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1038 TreeModel::Children rows = track_model->children();
1039 TreeModel::Children::iterator i;
1042 Unwinder<bool> uw (no_track_list_redisplay, true);
1044 for (i = rows.begin(); i != rows.end(); ++i) {
1045 TreeModel::Row row = (*i);
1046 MixerStrip* strip = row[track_columns.strip];
1052 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1056 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1057 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1061 (*i)[track_columns.visible] = yn;
1065 if (at) { /* track */
1066 (*i)[track_columns.visible] = yn;
1071 if (!at && !mt) { /* bus */
1072 (*i)[track_columns.visible] = yn;
1077 if (mt) { /* midi-track */
1078 (*i)[track_columns.visible] = yn;
1085 redisplay_track_list ();
1089 Mixer_UI::hide_all_routes ()
1091 set_all_strips_visibility (false);
1095 Mixer_UI::show_all_routes ()
1097 set_all_strips_visibility (true);
1101 Mixer_UI::show_all_audiobus ()
1103 set_all_audio_midi_visibility (2, true);
1106 Mixer_UI::hide_all_audiobus ()
1108 set_all_audio_midi_visibility (2, false);
1112 Mixer_UI::show_all_audiotracks()
1114 set_all_audio_midi_visibility (1, true);
1117 Mixer_UI::hide_all_audiotracks ()
1119 set_all_audio_midi_visibility (1, false);
1123 Mixer_UI::show_all_miditracks()
1125 set_all_audio_midi_visibility (3, true);
1128 Mixer_UI::hide_all_miditracks ()
1130 set_all_audio_midi_visibility (3, false);
1135 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1137 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1138 sync_presentation_info_from_treeview ();
1142 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1144 /* this happens as the second step of a DnD within the treeview as well
1145 as when a row/route is actually deleted.
1147 if it was a deletion then we have to force a redisplay because
1148 order keys may not have changed.
1151 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1152 sync_presentation_info_from_treeview ();
1154 if (_route_deletion_in_progress) {
1155 redisplay_track_list ();
1160 Mixer_UI::redisplay_track_list ()
1162 TreeModel::Children rows = track_model->children();
1163 TreeModel::Children::iterator i;
1164 uint32_t n_masters = 0;
1166 if (no_track_list_redisplay) {
1170 container_clear (vca_packer);
1172 for (i = rows.begin(); i != rows.end(); ++i) {
1174 VCAMasterStrip* vms = (*i)[track_columns.vca];
1177 vca_packer.pack_start (*vms, false, false);
1183 MixerStrip* strip = (*i)[track_columns.strip];
1186 /* we're in the middle of changing a row, don't worry */
1190 bool const visible = (*i)[track_columns.visible];
1193 strip->set_gui_property ("visible", true);
1195 if (strip->packed()) {
1197 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1198 out_packer.reorder_child (*strip, -1);
1201 strip_packer.reorder_child (*strip, -1); /* put at end */
1206 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1207 out_packer.pack_start (*strip, false, false);
1209 strip_packer.pack_start (*strip, false, false);
1211 strip->set_packed (true);
1216 strip->set_gui_property ("visible", false);
1218 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1219 /* do nothing, these cannot be hidden */
1221 if (strip->packed()) {
1222 strip_packer.remove (*strip);
1223 strip->set_packed (false);
1229 /* update visibility of VCA assign buttons */
1231 if (n_masters == 0) {
1232 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1233 vca_scroller.hide ();
1235 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1236 vca_scroller.show ();
1239 _group_tabs->set_dirty ();
1243 Mixer_UI::strip_width_changed ()
1245 _group_tabs->set_dirty ();
1248 TreeModel::Children rows = track_model->children();
1249 TreeModel::Children::iterator i;
1252 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1253 MixerStrip* strip = (*i)[track_columns.strip];
1259 bool visible = (*i)[track_columns.visible];
1262 strip->queue_draw();
1269 struct PresentationInfoRouteSorter
1271 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1272 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1277 Mixer_UI::initial_track_display ()
1279 boost::shared_ptr<RouteList> routes = _session->get_routes();
1280 RouteList copy (*routes);
1281 PresentationInfoRouteSorter sorter;
1286 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1287 Unwinder<bool> uw2 (ignore_reorder, true);
1289 track_model->clear ();
1290 VCAList vcas = _session->vca_manager().vcas();
1295 redisplay_track_list ();
1299 Mixer_UI::show_track_list_menu ()
1301 if (track_menu == 0) {
1302 build_track_menu ();
1305 track_menu->popup (1, gtk_get_current_event_time());
1309 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1311 if (Keyboard::is_context_menu_event (ev)) {
1312 show_track_list_menu ();
1320 Mixer_UI::build_track_menu ()
1322 using namespace Menu_Helpers;
1323 using namespace Gtk;
1325 track_menu = new Menu;
1326 track_menu->set_name ("ArdourContextMenu");
1327 MenuList& items = track_menu->items();
1329 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1330 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1331 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1332 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1333 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1334 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1335 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1336 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1341 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1343 if (!what_changed.contains (ARDOUR::Properties::name)) {
1347 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1349 TreeModel::Children rows = track_model->children();
1350 TreeModel::Children::iterator i;
1352 for (i = rows.begin(); i != rows.end(); ++i) {
1353 if ((*i)[track_columns.strip] == mx) {
1354 (*i)[track_columns.text] = mx->route()->name();
1359 error << _("track display list item for renamed strip not found!") << endmsg;
1363 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1365 TreeModel::Path path;
1366 TreeViewColumn* column;
1370 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1371 _group_tabs->get_menu(0)->popup (1, ev->time);
1375 TreeIter iter = group_model->get_iter (path);
1377 _group_tabs->get_menu(0)->popup (1, ev->time);
1381 RouteGroup* group = (*iter)[group_columns.group];
1383 if (Keyboard::is_context_menu_event (ev)) {
1384 _group_tabs->get_menu(group)->popup (1, ev->time);
1388 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1390 if (Keyboard::is_edit_event (ev)) {
1392 // edit_route_group (group);
1394 group_display.queue_draw();
1403 bool visible = (*iter)[group_columns.visible];
1404 (*iter)[group_columns.visible] = !visible;
1406 group_display.queue_draw();
1419 Mixer_UI::activate_all_route_groups ()
1421 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1425 Mixer_UI::disable_all_route_groups ()
1427 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1431 Mixer_UI::route_groups_changed ()
1433 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1435 _in_group_rebuild_or_clear = true;
1437 /* just rebuild the while thing */
1439 group_model->clear ();
1442 /* this is currently not used,
1443 * Mixer_UI::group_display_button_press() has a case for it,
1444 * and a commented edit_route_group() but that's n/a since 2011.
1446 * This code is left as reminder that
1447 * row[group_columns.group] = 0 has special meaning.
1451 row = *(group_model->append());
1452 row[group_columns.visible] = true;
1453 row[group_columns.text] = (_("-all-"));
1454 row[group_columns.group] = 0;
1458 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1460 _group_tabs->set_dirty ();
1461 _in_group_rebuild_or_clear = false;
1465 Mixer_UI::new_route_group ()
1469 _group_tabs->run_new_group_dialog (rl);
1473 Mixer_UI::remove_selected_route_group ()
1475 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1476 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1482 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1485 /* selection mode is single, so rows.begin() is it */
1487 if ((iter = group_model->get_iter (*i))) {
1489 RouteGroup* rg = (*iter)[group_columns.group];
1492 _session->remove_route_group (*rg);
1498 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1500 if (in_group_row_change) {
1504 /* force an update of any mixer strips that are using this group,
1505 otherwise mix group names don't change in mixer strips
1508 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1509 if ((*i)->route_group() == group) {
1510 (*i)->route_group_changed();
1514 TreeModel::iterator i;
1515 TreeModel::Children rows = group_model->children();
1516 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1518 in_group_row_change = true;
1520 for (i = rows.begin(); i != rows.end(); ++i) {
1521 if ((*i)[group_columns.group] == group) {
1522 (*i)[group_columns.visible] = !group->is_hidden ();
1523 (*i)[group_columns.text] = group->name ();
1528 in_group_row_change = false;
1530 if (change.contains (Properties::name)) {
1531 _group_tabs->set_dirty ();
1534 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1535 if ((*j)->route_group() == group) {
1536 if (group->is_hidden ()) {
1546 Mixer_UI::show_mixer_list (bool yn)
1549 list_vpacker.show ();
1551 //if user wants to show the pane, we should make sure that it is wide enough to be visible
1552 int width = list_hpane.get_position();
1554 list_hpane.set_position(40);
1557 list_vpacker.hide ();
1560 _show_mixer_list = yn;
1564 Mixer_UI::show_monitor_section (bool yn)
1566 if (!monitor_section()) {
1569 if (monitor_section()->tearoff().torn_off()) {
1574 monitor_section()->tearoff().show();
1576 monitor_section()->tearoff().hide();
1581 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1586 if ((iter = group_model->get_iter (path))) {
1588 if ((group = (*iter)[group_columns.group]) == 0) {
1592 if (new_text != group->name()) {
1593 group->set_name (new_text);
1599 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1603 if (in_group_row_change) {
1607 if ((group = (*iter)[group_columns.group]) == 0) {
1611 std::string name = (*iter)[group_columns.text];
1613 if (name != group->name()) {
1614 group->set_name (name);
1617 bool hidden = !(*iter)[group_columns.visible];
1619 if (hidden != group->is_hidden ()) {
1620 group->set_hidden (hidden, this);
1624 /** Called when a group model row is deleted, but also when the model is
1625 * reordered by a user drag-and-drop; the latter is what we are
1626 * interested in here.
1629 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1631 if (_in_group_rebuild_or_clear) {
1635 /* Re-write the session's route group list so that the new order is preserved */
1637 list<RouteGroup*> new_list;
1639 Gtk::TreeModel::Children children = group_model->children();
1640 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1641 RouteGroup* g = (*i)[group_columns.group];
1643 new_list.push_back (g);
1647 _session->reorder_route_groups (new_list);
1652 Mixer_UI::add_route_group (RouteGroup* group)
1654 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1657 in_group_row_change = true;
1659 TreeModel::Row row = *(group_model->append());
1660 row[group_columns.visible] = !group->is_hidden ();
1661 row[group_columns.group] = group;
1662 if (!group->name().empty()) {
1663 row[group_columns.text] = group->name();
1665 row[group_columns.text] = _("unnamed");
1669 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1672 TreeViewColumn* col = group_display.get_column (0);
1673 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1674 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1677 _group_tabs->set_dirty ();
1679 in_group_row_change = false;
1683 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1685 using namespace Menu_Helpers;
1687 if (Keyboard::is_context_menu_event (ev)) {
1688 ARDOUR_UI::instance()->add_route ();
1696 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1698 printf ("Mixer_UI::scroller_drag_data_received\n");
1699 if (data.get_target() != "PluginFavoritePtr") {
1700 context->drag_finish (false, false, time);
1704 const void * d = data.get_data();
1705 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1707 PluginPresetList nfos;
1709 tv->get_object_drag_data (nfos, &source);
1711 Route::ProcessorList pl;
1714 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1715 PluginPresetPtr ppp = (*i);
1716 PluginInfoPtr pip = ppp->_pip;
1717 if (!pip->is_instrument ()) {
1720 ARDOUR_UI::instance()->session_add_midi_track (NULL, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0);
1724 context->drag_finish (ok, false, time);
1728 Mixer_UI::set_strip_width (Width w, bool save)
1732 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1733 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1738 struct PluginStateSorter {
1740 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1741 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1742 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1743 if (aiter != _user.end() && biter != _user.end()) {
1744 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1746 if (aiter != _user.end()) {
1749 if (biter != _user.end()) {
1752 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1755 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1757 std::list<std::string> _user;
1761 Mixer_UI::set_state (const XMLNode& node, int version)
1763 XMLProperty const * prop;
1765 Tabbable::set_state (node, version);
1767 if ((prop = node.property ("narrow-strips"))) {
1768 if (string_is_affirmative (prop->value())) {
1769 set_strip_width (Narrow);
1771 set_strip_width (Wide);
1775 if ((prop = node.property ("show-mixer"))) {
1776 if (string_is_affirmative (prop->value())) {
1781 if ((prop = node.property ("maximised"))) {
1782 bool yn = string_is_affirmative (prop->value());
1783 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1785 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1786 bool fs = tact && tact->get_active();
1788 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1792 if ((prop = node.property ("show-mixer-list"))) {
1793 bool yn = string_is_affirmative (prop->value());
1794 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1796 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1798 /* do it twice to force the change */
1799 tact->set_active (!yn);
1800 tact->set_active (yn);
1804 XMLNode* plugin_order;
1805 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1806 store_current_favorite_order ();
1807 std::list<string> order;
1808 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1809 XMLNodeConstIterator i;
1810 for (i = kids.begin(); i != kids.end(); ++i) {
1811 if ((prop = (*i)->property ("unique-id"))) {
1812 std::string unique_id = prop->value();
1813 order.push_back (unique_id);
1814 if ((prop = (*i)->property ("expanded"))) {
1815 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1819 PluginStateSorter cmp (order);
1820 favorite_order.sort (cmp);
1821 sync_treeview_from_favorite_order ();
1827 Mixer_UI::get_state ()
1829 XMLNode* node = new XMLNode (X_("Mixer"));
1832 node->add_child_nocopy (Tabbable::get_state());
1834 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane1, true));
1835 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1836 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane2, true));
1837 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1838 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (list_hpane, false));
1839 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1840 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (inner_pane, false));
1841 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1843 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1844 node->add_property ("show-mixer", _visible ? "yes" : "no");
1845 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1846 node->add_property ("maximised", _maximised ? "yes" : "no");
1848 store_current_favorite_order ();
1849 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1851 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1852 XMLNode* p = new XMLNode ("PluginInfo");
1853 p->add_property ("sort", cnt);
1854 p->add_property ("unique-id", (*i)->unique_id);
1855 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1856 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1858 plugin_order->add_child_nocopy (*p);
1860 node->add_child_nocopy (*plugin_order);
1866 Mixer_UI::pane_allocation_handler (Allocation& allocation, Gtk::Paned* which)
1869 XMLProperty* prop = 0;
1870 XMLNode* geometry = ARDOUR_UI::instance()->mixer_settings();
1871 int height = default_height;
1872 static bool done[4] = { false, false, false, false };
1874 /* Gtk::Paned behaves very oddly and rather undesirably. Setting the
1875 * position is a crapshoot if you time it incorrectly with the overall
1876 * sizing of the Paned. For example, if you might retrieve the size with
1877 * ::get_position() and then later call ::set_position() on a Paned at
1878 * a time when its allocation is different than it was when you retrieved
1879 * the position. The position will be interpreted as the size of the
1880 * first (top or left) child widget. If packing/size allocation later
1881 * resizes the Paned to a (final) smaller size, the position will be
1882 * used in ways that mean that the Paned ends up NOT in the same state
1883 * that it was in when you originally saved the position.
1885 * Concrete example: Paned is 800 pixels wide, position is 400
1886 * (halfway). Save position as 400. During program restart, set
1887 * position to 400, before Paned has been allocated any space. Paned
1888 * ends up initially sized to 1200 pixels. Position is now 1/3 of the
1889 * way across/down. Subsequent resizes will leave the position 1/3 of
1890 * the way across, rather than leaving it as a fixed pixel
1891 * position. Eventually, the Paned ends up 800 pixels wide/high again,
1892 * but the position is now 267, not 400.
1896 * We deal with this by using two strategies:
1898 * 1) only set the position if the allocated size of the Paned is at
1899 * least as big as it needs to be for the position to make sense.
1901 * 2) in recent versions of Ardour, save the position as a fraction,
1902 * and restore it using that fraction.
1904 * So, we will only call ::set_position() AFTER the Paned is of a
1905 * sensible size, and then in addition, we will set the position in a
1906 * way that will be maintained as/when/if the Paned is resized.
1908 * Once we've called ::set_position() on a Paned, we don't do it
1912 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1918 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1921 pos = atof (prop->value());
1925 /* older versions of Ardour stored absolute position */
1926 if ((done[0] = (allocation.get_height() > pos))) {
1927 rhs_pane1.set_position (pos);
1930 if ((done[0] = (allocation.get_height() > 1.0/pos))) {
1931 paned_set_position_as_fraction (rhs_pane1, pos, true);
1936 if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
1942 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
1943 pos = 2 * height / 3;
1945 pos = atof (prop->value());
1949 /* older versions of Ardour stored absolute position */
1950 if ((done[1] = (allocation.get_height() > pos))) {
1951 rhs_pane2.set_position (pos);
1954 if ((done[1] = (allocation.get_height() > 1.0/pos))) {
1955 paned_set_position_as_fraction (rhs_pane2, pos, true);
1960 if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1966 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1967 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
1969 pos = max (0.1, atof (prop->value ()));
1973 if ((done[2] = (allocation.get_width() > pos))) {
1974 list_hpane.set_position (pos);
1977 if ((done[2] = (allocation.get_width() > 1.0/pos))) {
1978 paned_set_position_as_fraction (list_hpane, pos, false);
1983 if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
1989 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
1990 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
1992 pos = max (0.1, atof (prop->value ()));
1996 /* older versions of Ardour stored absolute position */
1997 if ((done[3] = (allocation.get_width() > pos))) {
1998 inner_pane.set_position (pos);
2001 if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2002 paned_set_position_as_fraction (inner_pane, pos, false);
2009 Mixer_UI::scroll_left ()
2011 if (!scroller.get_hscrollbar()) return;
2012 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2013 /* stupid GTK: can't rely on clamping across versions */
2014 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2018 Mixer_UI::scroll_right ()
2020 if (!scroller.get_hscrollbar()) return;
2021 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2022 /* stupid GTK: can't rely on clamping across versions */
2023 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2027 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2029 switch (ev->direction) {
2030 case GDK_SCROLL_LEFT:
2034 if (ev->state & Keyboard::TertiaryModifier) {
2040 case GDK_SCROLL_RIGHT:
2044 case GDK_SCROLL_DOWN:
2045 if (ev->state & Keyboard::TertiaryModifier) {
2057 Mixer_UI::parameter_changed (string const & p)
2059 if (p == "show-group-tabs") {
2060 bool const s = _session->config.get_show_group_tabs ();
2062 _group_tabs->show ();
2064 _group_tabs->hide ();
2066 } else if (p == "default-narrow_ms") {
2067 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2068 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2069 (*i)->set_width_enum (s ? Narrow : Wide, this);
2071 } else if (p == "use-monitor-bus") {
2072 if (_session && !_session->monitor_out()) {
2073 monitor_section_detached ();
2079 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2081 g->set_active (a, this);
2085 Mixer_UI::plugin_selector()
2087 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2088 if (!_plugin_selector)
2089 _plugin_selector = new PluginSelector (PluginManager::instance());
2092 return _plugin_selector;
2096 Mixer_UI::setup_track_display ()
2098 track_model = ListStore::create (track_columns);
2099 track_display.set_model (track_model);
2100 track_display.append_column (_("Strips"), track_columns.text);
2101 track_display.append_column (_("Show"), track_columns.visible);
2102 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2103 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2104 track_display.get_column (0)->set_expand(true);
2105 track_display.get_column (1)->set_expand(false);
2106 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2107 track_display.set_name (X_("EditGroupList"));
2108 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2109 track_display.set_reorderable (true);
2110 track_display.set_headers_visible (true);
2111 track_display.set_can_focus(false);
2113 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2114 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2116 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2117 track_list_visible_cell->property_activatable() = true;
2118 track_list_visible_cell->property_radio() = false;
2119 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2121 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2123 track_display_scroller.add (track_display);
2124 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2126 VBox* v = manage (new VBox);
2128 v->pack_start (track_display_scroller, true, true);
2130 Button* b = manage (new Button);
2132 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2136 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2138 v->pack_start (*b, false, false);
2140 track_display_frame.set_name("BaseFrame");
2141 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2142 track_display_frame.add (*v);
2144 track_display_scroller.show();
2145 track_display_frame.show();
2146 track_display.show();
2150 Mixer_UI::new_track_or_bus ()
2152 ARDOUR_UI::instance()->add_route ();
2156 Mixer_UI::update_title ()
2158 if (!own_window()) {
2165 if (_session->snap_name() != _session->name()) {
2166 n = _session->snap_name ();
2168 n = _session->name ();
2171 if (_session->dirty ()) {
2175 WindowTitle title (n);
2176 title += S_("Window|Mixer");
2177 title += Glib::get_application_name ();
2178 own_window()->set_title (title.get_string());
2182 WindowTitle title (S_("Window|Mixer"));
2183 title += Glib::get_application_name ();
2184 own_window()->set_title (title.get_string());
2189 Mixer_UI::strip_by_x (int x)
2191 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2194 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2195 x2 = x1 + (*i)->get_width();
2197 if (x >= x1 && x <= x2) {
2206 Mixer_UI::set_route_targets_for_operation ()
2208 _route_targets.clear ();
2210 if (!_selection.empty()) {
2211 _route_targets = _selection.routes;
2215 // removed "implicit" selections of strips, after discussion on IRC
2220 Mixer_UI::monitor_section_going_away ()
2222 if (_monitor_section) {
2223 monitor_section_detached ();
2224 out_packer.remove (_monitor_section->tearoff());
2225 _monitor_section->set_session (0);
2226 delete _monitor_section;
2227 _monitor_section = 0;
2232 Mixer_UI::toggle_midi_input_active (bool flip_others)
2234 boost::shared_ptr<RouteList> rl (new RouteList);
2237 set_route_targets_for_operation ();
2239 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2240 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2243 rl->push_back ((*r)->route());
2244 onoff = !mt->input_active();
2248 _session->set_exclusive_input_active (rl, onoff, flip_others);
2252 Mixer_UI::maximise_mixer_space ()
2254 if (!own_window()) {
2262 _window->fullscreen ();
2267 Mixer_UI::restore_mixer_space ()
2269 if (!own_window()) {
2277 own_window()->unfullscreen();
2282 Mixer_UI::monitor_section_attached ()
2284 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2285 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2286 act->set_sensitive (true);
2287 tact->set_active ();
2291 Mixer_UI::monitor_section_detached ()
2293 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2294 act->set_sensitive (false);
2298 Mixer_UI::store_current_favorite_order ()
2300 typedef Gtk::TreeModel::Children type_children;
2301 type_children children = favorite_plugins_model->children();
2302 favorite_order.clear();
2303 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2305 Gtk::TreeModel::Row row = *iter;
2306 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2307 favorite_order.push_back (ppp->_pip);
2308 std::string name = row[favorite_plugins_columns.name];
2309 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2314 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2316 Gtk::TreeModel::Row row = *iter;
2317 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2319 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2323 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2325 PluginManager& manager (PluginManager::instance());
2326 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2327 if (manager.get_status (*i) != PluginManager::Favorite) {
2330 result.push_back (*i);
2334 struct PluginCustomSorter {
2336 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2337 PluginInfoList::const_iterator aiter = _user.begin();
2338 PluginInfoList::const_iterator biter = _user.begin();
2339 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2340 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2342 if (aiter != _user.end() && biter != _user.end()) {
2343 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2345 if (aiter != _user.end()) {
2348 if (biter != _user.end()) {
2351 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2353 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2355 PluginInfoList _user;
2359 Mixer_UI::refill_favorite_plugins ()
2361 PluginInfoList plugs;
2362 PluginManager& mgr (PluginManager::instance());
2365 refiller (plugs, mgr.lv2_plugin_info ());
2367 #ifdef WINDOWS_VST_SUPPORT
2368 refiller (plugs, mgr.windows_vst_plugin_info ());
2370 #ifdef LXVST_SUPPORT
2371 refiller (plugs, mgr.lxvst_plugin_info ());
2373 #ifdef AUDIOUNIT_SUPPORT
2374 refiller (plugs, mgr.au_plugin_info ());
2376 refiller (plugs, mgr.ladspa_plugin_info ());
2377 refiller (plugs, mgr.lua_plugin_info ());
2379 store_current_favorite_order ();
2381 PluginCustomSorter cmp (favorite_order);
2384 favorite_order = plugs;
2386 sync_treeview_from_favorite_order ();
2390 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2393 if (!(iter = favorite_plugins_model->get_iter (path))) {
2396 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2400 PluginInfoPtr pip = ppp->_pip;
2401 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2402 if (favorite_ui_state[pip->unique_id]) {
2403 favorite_plugins_display.expand_row (path, true);
2409 Mixer_UI::sync_treeview_from_favorite_order ()
2411 favorite_plugins_model->clear ();
2412 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2413 PluginInfoPtr pip = (*i);
2415 TreeModel::Row newrow = *(favorite_plugins_model->append());
2416 newrow[favorite_plugins_columns.name] = (*i)->name;
2417 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2422 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2423 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2424 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2425 child_row[favorite_plugins_columns.name] = (*j).label;
2426 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2428 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2429 if (favorite_ui_state[pip->unique_id]) {
2430 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2437 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2439 using namespace Gtk::Menu_Helpers;
2441 Gtk::Menu* m = manage (new Menu);
2442 MenuList& items = m->items ();
2444 if (_selection.routes.empty()) {
2445 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2447 items.push_back (MenuElem (_("Add at the top"),
2448 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2449 items.push_back (MenuElem (_("Add Pre-Fader"),
2450 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2451 items.push_back (MenuElem (_("Add Post-Fader"),
2452 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2453 items.push_back (MenuElem (_("Add at the end"),
2454 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2457 items.push_back (SeparatorElem());
2459 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2461 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2462 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2463 // we cannot currently delete AU presets
2464 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2465 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2469 m->popup (ev->button, ev->time);
2473 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2475 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2476 TreeModel::Path path;
2477 TreeViewColumn* column;
2479 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2480 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2482 selection->unselect_all();
2483 selection->select(path);
2486 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2488 popup_note_context_menu (ev);
2496 Mixer_UI::selected_plugin ()
2498 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2500 return PluginPresetPtr();
2502 Gtk::TreeModel::iterator iter = selection->get_selected();
2504 return PluginPresetPtr();
2506 return (*iter)[favorite_plugins_columns.plugin];
2510 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2512 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2514 add_favorite_processor (ppp, pos);
2519 Mixer_UI::delete_selected_preset ()
2524 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2525 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2528 PluginPtr plugin = ppp->_pip->load (*_session);
2529 plugin->get_presets();
2530 plugin->remove_preset (ppp->_preset.label);
2534 Mixer_UI::remove_selected_from_favorites ()
2536 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2540 PluginManager::PluginStatusType status = PluginManager::Normal;
2541 PluginManager& manager (PluginManager::instance());
2543 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2544 manager.save_statuses ();
2548 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2551 if (!(iter = favorite_plugins_model->get_iter (path))) {
2554 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2555 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2559 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2561 if (!_session || _selection.routes.empty()) {
2565 PluginInfoPtr pip = ppp->_pip;
2566 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2567 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2568 if (!rt) { continue; }
2570 PluginPtr p = pip->load (*_session);
2571 if (!p) { continue; }
2573 if (ppp->_preset.valid) {
2574 p->load_preset (ppp->_preset);
2577 Route::ProcessorStreams err;
2578 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2582 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2585 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2592 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2596 if (!np->display_to_user()) {
2599 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2600 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2605 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2609 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2616 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2618 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2622 // only allow to re-order top-level items
2624 if (TreePath::get_from_selection_data (data, src)) {
2625 if (src.up() && src.up()) {
2630 // don't allow to drop as child-rows.
2631 Gtk::TreeModel::Path _dest = dest; // un const
2632 const bool is_child = _dest.up (); // explicit bool for clang
2633 if (!is_child || _dest.empty ()) {
2640 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2642 if (data.get_target() != "PluginPresetPtr") {
2645 if (data.get_length() != sizeof (PluginPresetPtr)) {
2648 const void *d = data.get_data();
2649 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2651 PluginManager::PluginStatusType status = PluginManager::Favorite;
2652 PluginManager& manager (PluginManager::instance());
2654 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2655 manager.save_statuses ();
2659 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2661 /* call protected MixerActor:: method */
2666 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2668 /* call protected MixerActor:: method */