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/midi_track.h"
46 #include "ardour/plugin_manager.h"
47 #include "ardour/route_group.h"
48 #include "ardour/route_sorters.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 Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_order_keys, 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;
390 redisplay_track_list ();
394 Mixer_UI::remove_master (VCAMasterStrip* vms)
399 Mixer_UI::add_strips (RouteList& routes)
401 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
402 uint32_t nroutes = 0;
404 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
405 boost::shared_ptr<Route> r = (*it)[track_columns.route];
413 if (r->order_key() == (routes.front()->order_key() + routes.size())) {
420 _selection.clear_routes ();
426 no_track_list_redisplay = true;
427 track_display.set_model (Glib::RefPtr<ListStore>());
429 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
430 boost::shared_ptr<Route> route = (*x);
432 if (route->is_auditioner()) {
436 if (route->is_monitor()) {
438 if (!_monitor_section) {
439 _monitor_section = new MonitorSection (_session);
441 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
443 _monitor_section->tearoff().set_state (*mnode);
447 out_packer.pack_end (_monitor_section->tearoff(), false, false);
448 _monitor_section->set_session (_session);
449 _monitor_section->tearoff().show_all ();
451 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
452 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
454 monitor_section_attached ();
456 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
458 /* no regular strip shown for control out */
463 strip = new MixerStrip (*this, _session, route);
464 strips.push_back (strip);
466 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
468 if (strip->width_owner() != strip) {
469 strip->set_width_enum (_strip_width, this);
474 TreeModel::Row row = *(track_model->insert(insert_iter));
475 row[track_columns.text] = route->name();
476 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
477 row[track_columns.route] = route;
478 row[track_columns.strip] = strip;
479 row[track_columns.vca] = 0;
482 _selection.add (strip);
485 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
487 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
488 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
491 } catch (const std::exception& e) {
492 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
495 no_track_list_redisplay = false;
496 track_display.set_model (track_model);
498 sync_order_keys_from_treeview ();
499 redisplay_track_list ();
503 Mixer_UI::deselect_all_strip_processors ()
505 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
506 (*i)->deselect_all_processors();
511 Mixer_UI::select_strip (MixerStrip& ms, bool add)
514 _selection.add (&ms);
516 _selection.set (&ms);
521 Mixer_UI::select_none ()
523 _selection.clear_routes();
524 deselect_all_strip_processors();
528 Mixer_UI::delete_processors ()
530 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
531 (*i)->delete_processors();
537 Mixer_UI::remove_strip (MixerStrip* strip)
539 if (_session && _session->deletion_in_progress()) {
540 /* its all being taken care of */
544 TreeModel::Children rows = track_model->children();
545 TreeModel::Children::iterator ri;
546 list<MixerStrip *>::iterator i;
548 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
552 for (ri = rows.begin(); ri != rows.end(); ++ri) {
553 if ((*ri)[track_columns.strip] == strip) {
554 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
555 track_model->erase (ri);
562 Mixer_UI::reset_remote_control_ids ()
564 if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
568 TreeModel::Children rows = track_model->children();
574 DEBUG_TRACE (DEBUG::OrderKeys, "mixer resets remote control ids after remote model change\n");
576 TreeModel::Children::iterator ri;
577 bool rid_change = false;
579 uint32_t invisible_key = UINT32_MAX;
581 for (ri = rows.begin(); ri != rows.end(); ++ri) {
583 /* skip two special values */
585 if (rid == Route::MasterBusRemoteControlID) {
589 if (rid == Route::MonitorBusRemoteControlID) {
593 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
594 bool visible = (*ri)[track_columns.visible];
600 if (!route->is_master() && !route->is_monitor()) {
602 uint32_t new_rid = (visible ? rid : invisible_key--);
604 if (new_rid != route->remote_control_id()) {
605 route->set_remote_control_id_explicit (new_rid);
616 /* tell the world that we changed the remote control IDs */
617 _session->notify_remote_id_change ();
622 Mixer_UI::sync_order_keys_from_treeview ()
624 if (ignore_reorder || !_session || _session->deletion_in_progress()) {
628 TreeModel::Children rows = track_model->children();
634 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync order keys from model\n");
636 TreeModel::Children::iterator ri;
637 bool changed = false;
638 bool rid_change = false;
641 uint32_t invisible_key = UINT32_MAX;
643 for (ri = rows.begin(); ri != rows.end(); ++ri) {
644 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
645 bool visible = (*ri)[track_columns.visible];
651 uint32_t old_key = route->order_key ();
653 if (order != old_key) {
654 route->set_order_key (order);
658 if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
660 uint32_t new_rid = (visible ? rid : invisible_key--);
662 if (new_rid != route->remote_control_id()) {
663 route->set_remote_control_id_explicit (new_rid);
677 /* tell everyone that we changed the mixer sort keys */
678 _session->sync_order_keys ();
682 /* tell the world that we changed the remote control IDs */
683 _session->notify_remote_id_change ();
688 Mixer_UI::sync_treeview_from_order_keys ()
690 if (!_session || _session->deletion_in_progress()) {
694 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from order keys.\n");
696 /* we could get here after either a change in the Mixer or Editor sort
697 * order, but either way, the mixer order keys reflect the intended
698 * order for the GUI, so reorder the treeview model to match it.
701 vector<int> neworder;
702 TreeModel::Children rows = track_model->children();
703 uint32_t old_order = 0;
704 bool changed = false;
711 uint32_t vca_cnt = 0;
712 uint32_t max_route_order_key = 0;
714 /* count number of Routes in track_model (there may be some odd reason
715 why this is not the same as the number in the session, but here we
716 care about the track model.
719 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
720 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
722 max_route_order_key = max (route->order_key(), max_route_order_key);
726 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
727 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
729 /* VCAs need to sort after all routes. We don't display
730 * them in the same place (March 2016), but we don't
731 * want them intermixed in the track_model
733 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
735 sorted.push_back (OrderKeys (old_order, route->order_key ()));
739 SortByNewDisplayOrder cmp;
741 sort (sorted.begin(), sorted.end(), cmp);
742 neworder.assign (sorted.size(), 0);
746 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
748 neworder[n] = sr->old_display_order;
750 if (sr->old_display_order != n) {
754 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("MIXER change order from %1 to %2\n",
755 sr->old_display_order, n));
759 Unwinder<bool> uw (ignore_reorder, true);
760 track_model->reorder (neworder);
763 redisplay_track_list ();
767 Mixer_UI::follow_editor_selection ()
769 if (_following_editor_selection) {
773 _following_editor_selection = true;
774 _selection.block_routes_changed (true);
776 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
778 _selection.clear_routes ();
780 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
781 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
783 MixerStrip* ms = strip_by_route (rtav->route());
790 _following_editor_selection = false;
791 _selection.block_routes_changed (false);
796 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
798 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
799 if ((*i)->route() == r) {
808 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
810 if (ev->button == 1) {
811 if (_selection.selected (strip)) {
812 /* primary-click: toggle selection state of strip */
813 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
814 _selection.remove (strip);
815 } else if (_selection.routes.size() > 1) {
816 /* de-select others */
817 _selection.set (strip);
820 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
821 _selection.add (strip);
822 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
824 if (!_selection.selected(strip)) {
826 /* extend selection */
828 vector<MixerStrip*> tmp;
829 bool accumulate = false;
830 bool found_another = false;
832 tmp.push_back (strip);
834 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
836 /* hit clicked strip, start accumulating till we hit the first
845 } else if (_selection.selected (*i)) {
846 /* hit selected strip. if currently accumulating others,
847 we're done. if not accumulating others, start doing so.
849 found_another = true;
864 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
868 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
872 _selection.set (strip);
881 Mixer_UI::set_session (Session* sess)
883 SessionHandlePtr::set_session (sess);
885 if (_plugin_selector) {
886 _plugin_selector->set_session (_session);
889 _group_tabs->set_session (sess);
895 refill_favorite_plugins();
897 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
898 set_state (*node, 0);
902 initial_track_display ();
904 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
905 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
906 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
907 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
908 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
909 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
910 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
912 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
914 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
916 route_groups_changed ();
925 Mixer_UI::session_going_away ()
927 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
929 _in_group_rebuild_or_clear = true;
930 group_model->clear ();
931 _in_group_rebuild_or_clear = false;
934 track_model->clear ();
936 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
940 if (_monitor_section) {
941 _monitor_section->tearoff().hide_visible ();
944 monitor_section_detached ();
950 SessionHandlePtr::session_going_away ();
957 Mixer_UI::track_visibility_changed (std::string const & path)
959 if (_session && _session->deletion_in_progress()) {
965 if ((iter = track_model->get_iter (path))) {
966 MixerStrip* strip = (*iter)[track_columns.strip];
968 bool visible = (*iter)[track_columns.visible];
970 if (strip->set_marked_for_display (!visible)) {
971 update_track_visibility ();
978 Mixer_UI::update_track_visibility ()
980 TreeModel::Children rows = track_model->children();
981 TreeModel::Children::iterator i;
984 Unwinder<bool> uw (no_track_list_redisplay, true);
986 for (i = rows.begin(); i != rows.end(); ++i) {
987 MixerStrip *strip = (*i)[track_columns.strip];
989 (*i)[track_columns.visible] = strip->marked_for_display ();
993 /* force route order keys catch up with visibility changes
996 sync_order_keys_from_treeview ();
999 redisplay_track_list ();
1003 Mixer_UI::show_strip (MixerStrip* ms)
1005 TreeModel::Children rows = track_model->children();
1006 TreeModel::Children::iterator i;
1008 for (i = rows.begin(); i != rows.end(); ++i) {
1010 MixerStrip* strip = (*i)[track_columns.strip];
1012 (*i)[track_columns.visible] = true;
1013 redisplay_track_list ();
1020 Mixer_UI::hide_strip (MixerStrip* ms)
1022 TreeModel::Children rows = track_model->children();
1023 TreeModel::Children::iterator i;
1025 for (i = rows.begin(); i != rows.end(); ++i) {
1027 MixerStrip* strip = (*i)[track_columns.strip];
1029 (*i)[track_columns.visible] = false;
1030 redisplay_track_list ();
1037 Mixer_UI::start_updating ()
1039 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1044 Mixer_UI::stop_updating ()
1046 fast_screen_update_connection.disconnect();
1051 Mixer_UI::fast_update_strips ()
1053 if (_content.is_mapped () && _session) {
1054 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1055 (*i)->fast_update ();
1061 Mixer_UI::set_all_strips_visibility (bool yn)
1063 TreeModel::Children rows = track_model->children();
1064 TreeModel::Children::iterator i;
1067 Unwinder<bool> uw (no_track_list_redisplay, true);
1069 for (i = rows.begin(); i != rows.end(); ++i) {
1071 TreeModel::Row row = (*i);
1072 MixerStrip* strip = row[track_columns.strip];
1078 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1082 (*i)[track_columns.visible] = yn;
1086 redisplay_track_list ();
1091 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1093 TreeModel::Children rows = track_model->children();
1094 TreeModel::Children::iterator i;
1097 Unwinder<bool> uw (no_track_list_redisplay, true);
1099 for (i = rows.begin(); i != rows.end(); ++i) {
1100 TreeModel::Row row = (*i);
1101 MixerStrip* strip = row[track_columns.strip];
1107 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1111 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1112 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1116 (*i)[track_columns.visible] = yn;
1120 if (at) { /* track */
1121 (*i)[track_columns.visible] = yn;
1126 if (!at && !mt) { /* bus */
1127 (*i)[track_columns.visible] = yn;
1132 if (mt) { /* midi-track */
1133 (*i)[track_columns.visible] = yn;
1140 redisplay_track_list ();
1144 Mixer_UI::hide_all_routes ()
1146 set_all_strips_visibility (false);
1150 Mixer_UI::show_all_routes ()
1152 set_all_strips_visibility (true);
1156 Mixer_UI::show_all_audiobus ()
1158 set_all_audio_midi_visibility (2, true);
1161 Mixer_UI::hide_all_audiobus ()
1163 set_all_audio_midi_visibility (2, false);
1167 Mixer_UI::show_all_audiotracks()
1169 set_all_audio_midi_visibility (1, true);
1172 Mixer_UI::hide_all_audiotracks ()
1174 set_all_audio_midi_visibility (1, false);
1178 Mixer_UI::show_all_miditracks()
1180 set_all_audio_midi_visibility (3, true);
1183 Mixer_UI::hide_all_miditracks ()
1185 set_all_audio_midi_visibility (3, false);
1190 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1192 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1193 sync_order_keys_from_treeview ();
1197 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1199 /* this happens as the second step of a DnD within the treeview as well
1200 as when a row/route is actually deleted.
1202 if it was a deletion then we have to force a redisplay because
1203 order keys may not have changed.
1206 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1207 sync_order_keys_from_treeview ();
1209 if (_route_deletion_in_progress) {
1210 redisplay_track_list ();
1215 Mixer_UI::redisplay_track_list ()
1217 TreeModel::Children rows = track_model->children();
1218 TreeModel::Children::iterator i;
1220 if (no_track_list_redisplay) {
1224 container_clear (vca_packer);
1226 for (i = rows.begin(); i != rows.end(); ++i) {
1228 VCAMasterStrip* vms = (*i)[track_columns.vca];
1231 vca_packer.pack_start (*vms, false, false);
1236 MixerStrip* strip = (*i)[track_columns.strip];
1239 /* we're in the middle of changing a row, don't worry */
1243 bool const visible = (*i)[track_columns.visible];
1246 strip->set_gui_property ("visible", true);
1248 if (strip->packed()) {
1250 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1251 out_packer.reorder_child (*strip, -1);
1254 strip_packer.reorder_child (*strip, -1); /* put at end */
1259 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1260 out_packer.pack_start (*strip, false, false);
1262 strip_packer.pack_start (*strip, false, false);
1264 strip->set_packed (true);
1269 strip->set_gui_property ("visible", false);
1271 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1272 /* do nothing, these cannot be hidden */
1274 if (strip->packed()) {
1275 strip_packer.remove (*strip);
1276 strip->set_packed (false);
1282 _group_tabs->set_dirty ();
1286 Mixer_UI::strip_width_changed ()
1288 _group_tabs->set_dirty ();
1291 TreeModel::Children rows = track_model->children();
1292 TreeModel::Children::iterator i;
1295 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1296 MixerStrip* strip = (*i)[track_columns.strip];
1302 bool visible = (*i)[track_columns.visible];
1305 strip->queue_draw();
1313 Mixer_UI::initial_track_display ()
1315 boost::shared_ptr<RouteList> routes = _session->get_routes();
1316 RouteList copy (*routes);
1317 ARDOUR::SignalOrderRouteSorter sorter;
1322 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1323 Unwinder<bool> uw2 (ignore_reorder, true);
1325 track_model->clear ();
1326 VCAList vcas = _session->vca_manager().vcas();
1331 _session->sync_order_keys ();
1333 redisplay_track_list ();
1337 Mixer_UI::show_track_list_menu ()
1339 if (track_menu == 0) {
1340 build_track_menu ();
1343 track_menu->popup (1, gtk_get_current_event_time());
1347 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1349 if (Keyboard::is_context_menu_event (ev)) {
1350 show_track_list_menu ();
1358 Mixer_UI::build_track_menu ()
1360 using namespace Menu_Helpers;
1361 using namespace Gtk;
1363 track_menu = new Menu;
1364 track_menu->set_name ("ArdourContextMenu");
1365 MenuList& items = track_menu->items();
1367 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1368 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1369 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1370 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1371 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1372 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1373 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1374 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1379 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1381 if (!what_changed.contains (ARDOUR::Properties::name)) {
1385 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1387 TreeModel::Children rows = track_model->children();
1388 TreeModel::Children::iterator i;
1390 for (i = rows.begin(); i != rows.end(); ++i) {
1391 if ((*i)[track_columns.strip] == mx) {
1392 (*i)[track_columns.text] = mx->route()->name();
1397 error << _("track display list item for renamed strip not found!") << endmsg;
1401 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1403 TreeModel::Path path;
1404 TreeViewColumn* column;
1408 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1409 _group_tabs->get_menu(0)->popup (1, ev->time);
1413 TreeIter iter = group_model->get_iter (path);
1415 _group_tabs->get_menu(0)->popup (1, ev->time);
1419 RouteGroup* group = (*iter)[group_columns.group];
1421 if (Keyboard::is_context_menu_event (ev)) {
1422 _group_tabs->get_menu(group)->popup (1, ev->time);
1426 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1428 if (Keyboard::is_edit_event (ev)) {
1430 // edit_route_group (group);
1432 group_display.queue_draw();
1441 bool visible = (*iter)[group_columns.visible];
1442 (*iter)[group_columns.visible] = !visible;
1444 group_display.queue_draw();
1457 Mixer_UI::activate_all_route_groups ()
1459 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1463 Mixer_UI::disable_all_route_groups ()
1465 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1469 Mixer_UI::route_groups_changed ()
1471 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1473 _in_group_rebuild_or_clear = true;
1475 /* just rebuild the while thing */
1477 group_model->clear ();
1480 /* this is currently not used,
1481 * Mixer_UI::group_display_button_press() has a case for it,
1482 * and a commented edit_route_group() but that's n/a since 2011.
1484 * This code is left as reminder that
1485 * row[group_columns.group] = 0 has special meaning.
1489 row = *(group_model->append());
1490 row[group_columns.visible] = true;
1491 row[group_columns.text] = (_("-all-"));
1492 row[group_columns.group] = 0;
1496 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1498 _group_tabs->set_dirty ();
1499 _in_group_rebuild_or_clear = false;
1503 Mixer_UI::new_route_group ()
1507 _group_tabs->run_new_group_dialog (rl);
1511 Mixer_UI::remove_selected_route_group ()
1513 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1514 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1520 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1523 /* selection mode is single, so rows.begin() is it */
1525 if ((iter = group_model->get_iter (*i))) {
1527 RouteGroup* rg = (*iter)[group_columns.group];
1530 _session->remove_route_group (*rg);
1536 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1538 if (in_group_row_change) {
1542 /* force an update of any mixer strips that are using this group,
1543 otherwise mix group names don't change in mixer strips
1546 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1547 if ((*i)->route_group() == group) {
1548 (*i)->route_group_changed();
1552 TreeModel::iterator i;
1553 TreeModel::Children rows = group_model->children();
1554 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1556 in_group_row_change = true;
1558 for (i = rows.begin(); i != rows.end(); ++i) {
1559 if ((*i)[group_columns.group] == group) {
1560 (*i)[group_columns.visible] = !group->is_hidden ();
1561 (*i)[group_columns.text] = group->name ();
1566 in_group_row_change = false;
1568 if (change.contains (Properties::name)) {
1569 _group_tabs->set_dirty ();
1572 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1573 if ((*j)->route_group() == group) {
1574 if (group->is_hidden ()) {
1584 Mixer_UI::show_mixer_list (bool yn)
1587 list_vpacker.show ();
1589 //if user wants to show the pane, we should make sure that it is wide enough to be visible
1590 int width = list_hpane.get_position();
1592 list_hpane.set_position(40);
1595 list_vpacker.hide ();
1598 _show_mixer_list = yn;
1602 Mixer_UI::show_monitor_section (bool yn)
1604 if (!monitor_section()) {
1607 if (monitor_section()->tearoff().torn_off()) {
1612 monitor_section()->tearoff().show();
1614 monitor_section()->tearoff().hide();
1619 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1624 if ((iter = group_model->get_iter (path))) {
1626 if ((group = (*iter)[group_columns.group]) == 0) {
1630 if (new_text != group->name()) {
1631 group->set_name (new_text);
1637 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1641 if (in_group_row_change) {
1645 if ((group = (*iter)[group_columns.group]) == 0) {
1649 std::string name = (*iter)[group_columns.text];
1651 if (name != group->name()) {
1652 group->set_name (name);
1655 bool hidden = !(*iter)[group_columns.visible];
1657 if (hidden != group->is_hidden ()) {
1658 group->set_hidden (hidden, this);
1662 /** Called when a group model row is deleted, but also when the model is
1663 * reordered by a user drag-and-drop; the latter is what we are
1664 * interested in here.
1667 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1669 if (_in_group_rebuild_or_clear) {
1673 /* Re-write the session's route group list so that the new order is preserved */
1675 list<RouteGroup*> new_list;
1677 Gtk::TreeModel::Children children = group_model->children();
1678 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1679 RouteGroup* g = (*i)[group_columns.group];
1681 new_list.push_back (g);
1685 _session->reorder_route_groups (new_list);
1690 Mixer_UI::add_route_group (RouteGroup* group)
1692 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1695 in_group_row_change = true;
1697 TreeModel::Row row = *(group_model->append());
1698 row[group_columns.visible] = !group->is_hidden ();
1699 row[group_columns.group] = group;
1700 if (!group->name().empty()) {
1701 row[group_columns.text] = group->name();
1703 row[group_columns.text] = _("unnamed");
1707 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1710 TreeViewColumn* col = group_display.get_column (0);
1711 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1712 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1715 _group_tabs->set_dirty ();
1717 in_group_row_change = false;
1721 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1723 using namespace Menu_Helpers;
1725 if (Keyboard::is_context_menu_event (ev)) {
1726 ARDOUR_UI::instance()->add_route ();
1734 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1736 printf ("Mixer_UI::scroller_drag_data_received\n");
1737 if (data.get_target() != "PluginFavoritePtr") {
1738 context->drag_finish (false, false, time);
1742 const void * d = data.get_data();
1743 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1745 PluginPresetList nfos;
1747 tv->get_object_drag_data (nfos, &source);
1749 Route::ProcessorList pl;
1752 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1753 PluginPresetPtr ppp = (*i);
1754 PluginInfoPtr pip = ppp->_pip;
1755 if (!pip->is_instrument ()) {
1758 ARDOUR_UI::instance()->session_add_midi_track (NULL, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0);
1762 context->drag_finish (ok, false, time);
1766 Mixer_UI::set_strip_width (Width w, bool save)
1770 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1771 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1776 struct PluginStateSorter {
1778 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1779 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1780 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1781 if (aiter != _user.end() && biter != _user.end()) {
1782 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1784 if (aiter != _user.end()) {
1787 if (biter != _user.end()) {
1790 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1793 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1795 std::list<std::string> _user;
1799 Mixer_UI::set_state (const XMLNode& node, int version)
1801 XMLProperty const * prop;
1803 Tabbable::set_state (node, version);
1805 if ((prop = node.property ("narrow-strips"))) {
1806 if (string_is_affirmative (prop->value())) {
1807 set_strip_width (Narrow);
1809 set_strip_width (Wide);
1813 if ((prop = node.property ("show-mixer"))) {
1814 if (string_is_affirmative (prop->value())) {
1819 if ((prop = node.property ("maximised"))) {
1820 bool yn = string_is_affirmative (prop->value());
1821 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1823 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1824 bool fs = tact && tact->get_active();
1826 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1830 if ((prop = node.property ("show-mixer-list"))) {
1831 bool yn = string_is_affirmative (prop->value());
1832 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1834 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1836 /* do it twice to force the change */
1837 tact->set_active (!yn);
1838 tact->set_active (yn);
1842 XMLNode* plugin_order;
1843 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1844 store_current_favorite_order ();
1845 std::list<string> order;
1846 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1847 XMLNodeConstIterator i;
1848 for (i = kids.begin(); i != kids.end(); ++i) {
1849 if ((prop = (*i)->property ("unique-id"))) {
1850 std::string unique_id = prop->value();
1851 order.push_back (unique_id);
1852 if ((prop = (*i)->property ("expanded"))) {
1853 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1857 PluginStateSorter cmp (order);
1858 favorite_order.sort (cmp);
1859 sync_treeview_from_favorite_order ();
1865 Mixer_UI::get_state ()
1867 XMLNode* node = new XMLNode (X_("Mixer"));
1870 node->add_child_nocopy (Tabbable::get_state());
1872 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane1, true));
1873 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1874 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane2, true));
1875 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1876 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (list_hpane, false));
1877 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1878 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (inner_pane, false));
1879 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1881 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1882 node->add_property ("show-mixer", _visible ? "yes" : "no");
1883 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1884 node->add_property ("maximised", _maximised ? "yes" : "no");
1886 store_current_favorite_order ();
1887 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1889 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1890 XMLNode* p = new XMLNode ("PluginInfo");
1891 p->add_property ("sort", cnt);
1892 p->add_property ("unique-id", (*i)->unique_id);
1893 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1894 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1896 plugin_order->add_child_nocopy (*p);
1898 node->add_child_nocopy (*plugin_order);
1904 Mixer_UI::pane_allocation_handler (Allocation& allocation, Gtk::Paned* which)
1907 XMLProperty* prop = 0;
1908 XMLNode* geometry = ARDOUR_UI::instance()->mixer_settings();
1909 int height = default_height;
1910 static bool done[4] = { false, false, false, false };
1912 /* Gtk::Paned behaves very oddly and rather undesirably. Setting the
1913 * position is a crapshoot if you time it incorrectly with the overall
1914 * sizing of the Paned. For example, if you might retrieve the size with
1915 * ::get_position() and then later call ::set_position() on a Paned at
1916 * a time when its allocation is different than it was when you retrieved
1917 * the position. The position will be interpreted as the size of the
1918 * first (top or left) child widget. If packing/size allocation later
1919 * resizes the Paned to a (final) smaller size, the position will be
1920 * used in ways that mean that the Paned ends up NOT in the same state
1921 * that it was in when you originally saved the position.
1923 * Concrete example: Paned is 800 pixels wide, position is 400
1924 * (halfway). Save position as 400. During program restart, set
1925 * position to 400, before Paned has been allocated any space. Paned
1926 * ends up initially sized to 1200 pixels. Position is now 1/3 of the
1927 * way across/down. Subsequent resizes will leave the position 1/3 of
1928 * the way across, rather than leaving it as a fixed pixel
1929 * position. Eventually, the Paned ends up 800 pixels wide/high again,
1930 * but the position is now 267, not 400.
1934 * We deal with this by using two strategies:
1936 * 1) only set the position if the allocated size of the Paned is at
1937 * least as big as it needs to be for the position to make sense.
1939 * 2) in recent versions of Ardour, save the position as a fraction,
1940 * and restore it using that fraction.
1942 * So, we will only call ::set_position() AFTER the Paned is of a
1943 * sensible size, and then in addition, we will set the position in a
1944 * way that will be maintained as/when/if the Paned is resized.
1946 * Once we've called ::set_position() on a Paned, we don't do it
1950 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1956 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1959 pos = atof (prop->value());
1963 /* older versions of Ardour stored absolute position */
1964 if ((done[0] = (allocation.get_height() > pos))) {
1965 rhs_pane1.set_position (pos);
1968 if ((done[0] = (allocation.get_height() > 1.0/pos))) {
1969 paned_set_position_as_fraction (rhs_pane1, pos, true);
1974 if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
1980 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
1981 pos = 2 * height / 3;
1983 pos = atof (prop->value());
1987 /* older versions of Ardour stored absolute position */
1988 if ((done[1] = (allocation.get_height() > pos))) {
1989 rhs_pane2.set_position (pos);
1992 if ((done[1] = (allocation.get_height() > 1.0/pos))) {
1993 paned_set_position_as_fraction (rhs_pane2, pos, true);
1998 if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
2004 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
2005 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2007 pos = max (0.1, atof (prop->value ()));
2011 if ((done[2] = (allocation.get_width() > pos))) {
2012 list_hpane.set_position (pos);
2015 if ((done[2] = (allocation.get_width() > 1.0/pos))) {
2016 paned_set_position_as_fraction (list_hpane, pos, false);
2021 if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
2027 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
2028 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2030 pos = max (0.1, atof (prop->value ()));
2034 /* older versions of Ardour stored absolute position */
2035 if ((done[3] = (allocation.get_width() > pos))) {
2036 inner_pane.set_position (pos);
2039 if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2040 paned_set_position_as_fraction (inner_pane, pos, false);
2047 Mixer_UI::scroll_left ()
2049 if (!scroller.get_hscrollbar()) return;
2050 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2051 /* stupid GTK: can't rely on clamping across versions */
2052 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2056 Mixer_UI::scroll_right ()
2058 if (!scroller.get_hscrollbar()) return;
2059 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2060 /* stupid GTK: can't rely on clamping across versions */
2061 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2065 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2067 switch (ev->direction) {
2068 case GDK_SCROLL_LEFT:
2072 if (ev->state & Keyboard::TertiaryModifier) {
2078 case GDK_SCROLL_RIGHT:
2082 case GDK_SCROLL_DOWN:
2083 if (ev->state & Keyboard::TertiaryModifier) {
2095 Mixer_UI::parameter_changed (string const & p)
2097 if (p == "show-group-tabs") {
2098 bool const s = _session->config.get_show_group_tabs ();
2100 _group_tabs->show ();
2102 _group_tabs->hide ();
2104 } else if (p == "default-narrow_ms") {
2105 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2106 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2107 (*i)->set_width_enum (s ? Narrow : Wide, this);
2109 } else if (p == "remote-model") {
2110 reset_remote_control_ids ();
2111 } else if (p == "use-monitor-bus") {
2112 if (_session && !_session->monitor_out()) {
2113 monitor_section_detached ();
2119 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2121 g->set_active (a, this);
2125 Mixer_UI::plugin_selector()
2127 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2128 if (!_plugin_selector)
2129 _plugin_selector = new PluginSelector (PluginManager::instance());
2132 return _plugin_selector;
2136 Mixer_UI::setup_track_display ()
2138 track_model = ListStore::create (track_columns);
2139 track_display.set_model (track_model);
2140 track_display.append_column (_("Strips"), track_columns.text);
2141 track_display.append_column (_("Show"), track_columns.visible);
2142 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2143 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2144 track_display.get_column (0)->set_expand(true);
2145 track_display.get_column (1)->set_expand(false);
2146 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2147 track_display.set_name (X_("EditGroupList"));
2148 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2149 track_display.set_reorderable (true);
2150 track_display.set_headers_visible (true);
2151 track_display.set_can_focus(false);
2153 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2154 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2156 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2157 track_list_visible_cell->property_activatable() = true;
2158 track_list_visible_cell->property_radio() = false;
2159 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2161 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2163 track_display_scroller.add (track_display);
2164 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2166 VBox* v = manage (new VBox);
2168 v->pack_start (track_display_scroller, true, true);
2170 Button* b = manage (new Button);
2172 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2176 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2178 v->pack_start (*b, false, false);
2180 track_display_frame.set_name("BaseFrame");
2181 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2182 track_display_frame.add (*v);
2184 track_display_scroller.show();
2185 track_display_frame.show();
2186 track_display.show();
2190 Mixer_UI::new_track_or_bus ()
2192 ARDOUR_UI::instance()->add_route ();
2197 Mixer_UI::update_title ()
2199 if (!own_window()) {
2206 if (_session->snap_name() != _session->name()) {
2207 n = _session->snap_name ();
2209 n = _session->name ();
2212 if (_session->dirty ()) {
2216 WindowTitle title (n);
2217 title += S_("Window|Mixer");
2218 title += Glib::get_application_name ();
2219 own_window()->set_title (title.get_string());
2223 WindowTitle title (S_("Window|Mixer"));
2224 title += Glib::get_application_name ();
2225 own_window()->set_title (title.get_string());
2230 Mixer_UI::strip_by_x (int x)
2232 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2235 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2236 x2 = x1 + (*i)->get_width();
2238 if (x >= x1 && x <= x2) {
2247 Mixer_UI::set_route_targets_for_operation ()
2249 _route_targets.clear ();
2251 if (!_selection.empty()) {
2252 _route_targets = _selection.routes;
2256 // removed "implicit" selections of strips, after discussion on IRC
2261 Mixer_UI::monitor_section_going_away ()
2263 if (_monitor_section) {
2264 monitor_section_detached ();
2265 out_packer.remove (_monitor_section->tearoff());
2266 _monitor_section->set_session (0);
2267 delete _monitor_section;
2268 _monitor_section = 0;
2273 Mixer_UI::toggle_midi_input_active (bool flip_others)
2275 boost::shared_ptr<RouteList> rl (new RouteList);
2278 set_route_targets_for_operation ();
2280 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2281 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2284 rl->push_back ((*r)->route());
2285 onoff = !mt->input_active();
2289 _session->set_exclusive_input_active (rl, onoff, flip_others);
2293 Mixer_UI::maximise_mixer_space ()
2295 if (!own_window()) {
2303 _window->fullscreen ();
2308 Mixer_UI::restore_mixer_space ()
2310 if (!own_window()) {
2318 own_window()->unfullscreen();
2323 Mixer_UI::monitor_section_attached ()
2325 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2326 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2327 act->set_sensitive (true);
2328 tact->set_active ();
2332 Mixer_UI::monitor_section_detached ()
2334 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2335 act->set_sensitive (false);
2339 Mixer_UI::store_current_favorite_order ()
2341 typedef Gtk::TreeModel::Children type_children;
2342 type_children children = favorite_plugins_model->children();
2343 favorite_order.clear();
2344 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2346 Gtk::TreeModel::Row row = *iter;
2347 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2348 favorite_order.push_back (ppp->_pip);
2349 std::string name = row[favorite_plugins_columns.name];
2350 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2355 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2357 Gtk::TreeModel::Row row = *iter;
2358 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2360 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2364 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2366 PluginManager& manager (PluginManager::instance());
2367 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2368 if (manager.get_status (*i) != PluginManager::Favorite) {
2371 result.push_back (*i);
2375 struct PluginCustomSorter {
2377 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2378 PluginInfoList::const_iterator aiter = _user.begin();
2379 PluginInfoList::const_iterator biter = _user.begin();
2380 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2381 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2383 if (aiter != _user.end() && biter != _user.end()) {
2384 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2386 if (aiter != _user.end()) {
2389 if (biter != _user.end()) {
2392 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2394 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2396 PluginInfoList _user;
2400 Mixer_UI::refill_favorite_plugins ()
2402 PluginInfoList plugs;
2403 PluginManager& mgr (PluginManager::instance());
2406 refiller (plugs, mgr.lv2_plugin_info ());
2408 #ifdef WINDOWS_VST_SUPPORT
2409 refiller (plugs, mgr.windows_vst_plugin_info ());
2411 #ifdef LXVST_SUPPORT
2412 refiller (plugs, mgr.lxvst_plugin_info ());
2414 #ifdef AUDIOUNIT_SUPPORT
2415 refiller (plugs, mgr.au_plugin_info ());
2417 refiller (plugs, mgr.ladspa_plugin_info ());
2418 refiller (plugs, mgr.lua_plugin_info ());
2420 store_current_favorite_order ();
2422 PluginCustomSorter cmp (favorite_order);
2425 favorite_order = plugs;
2427 sync_treeview_from_favorite_order ();
2431 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2434 if (!(iter = favorite_plugins_model->get_iter (path))) {
2437 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2441 PluginInfoPtr pip = ppp->_pip;
2442 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2443 if (favorite_ui_state[pip->unique_id]) {
2444 favorite_plugins_display.expand_row (path, true);
2450 Mixer_UI::sync_treeview_from_favorite_order ()
2452 favorite_plugins_model->clear ();
2453 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2454 PluginInfoPtr pip = (*i);
2456 TreeModel::Row newrow = *(favorite_plugins_model->append());
2457 newrow[favorite_plugins_columns.name] = (*i)->name;
2458 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2463 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2464 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2465 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2466 child_row[favorite_plugins_columns.name] = (*j).label;
2467 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2469 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2470 if (favorite_ui_state[pip->unique_id]) {
2471 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2478 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2480 using namespace Gtk::Menu_Helpers;
2482 Gtk::Menu* m = manage (new Menu);
2483 MenuList& items = m->items ();
2485 if (_selection.routes.empty()) {
2486 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2488 items.push_back (MenuElem (_("Add at the top"),
2489 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2490 items.push_back (MenuElem (_("Add Pre-Fader"),
2491 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2492 items.push_back (MenuElem (_("Add Post-Fader"),
2493 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2494 items.push_back (MenuElem (_("Add at the end"),
2495 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2498 items.push_back (SeparatorElem());
2500 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2502 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2503 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2504 // we cannot currently delete AU presets
2505 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2506 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2510 m->popup (ev->button, ev->time);
2514 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2516 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2517 TreeModel::Path path;
2518 TreeViewColumn* column;
2520 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2521 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2523 selection->unselect_all();
2524 selection->select(path);
2527 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2529 popup_note_context_menu (ev);
2537 Mixer_UI::selected_plugin ()
2539 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2541 return PluginPresetPtr();
2543 Gtk::TreeModel::iterator iter = selection->get_selected();
2545 return PluginPresetPtr();
2547 return (*iter)[favorite_plugins_columns.plugin];
2551 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2553 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2555 add_favorite_processor (ppp, pos);
2560 Mixer_UI::delete_selected_preset ()
2565 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2566 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2569 PluginPtr plugin = ppp->_pip->load (*_session);
2570 plugin->get_presets();
2571 plugin->remove_preset (ppp->_preset.label);
2575 Mixer_UI::remove_selected_from_favorites ()
2577 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2581 PluginManager::PluginStatusType status = PluginManager::Normal;
2582 PluginManager& manager (PluginManager::instance());
2584 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2585 manager.save_statuses ();
2589 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2592 if (!(iter = favorite_plugins_model->get_iter (path))) {
2595 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2596 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2600 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2602 if (!_session || _selection.routes.empty()) {
2606 PluginInfoPtr pip = ppp->_pip;
2607 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2608 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2609 if (!rt) { continue; }
2611 PluginPtr p = pip->load (*_session);
2612 if (!p) { continue; }
2614 if (ppp->_preset.valid) {
2615 p->load_preset (ppp->_preset);
2618 Route::ProcessorStreams err;
2619 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2623 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2626 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2633 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2637 if (!np->display_to_user()) {
2640 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2641 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2646 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2650 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2657 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2659 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2663 // only allow to re-order top-level items
2665 if (TreePath::get_from_selection_data (data, src)) {
2666 if (src.up() && src.up()) {
2671 // don't allow to drop as child-rows.
2672 Gtk::TreeModel::Path _dest = dest; // un const
2673 const bool is_child = _dest.up (); // explicit bool for clang
2674 if (!is_child || _dest.empty ()) {
2681 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2683 if (data.get_target() != "PluginPresetPtr") {
2686 if (data.get_length() != sizeof (PluginPresetPtr)) {
2689 const void *d = data.get_data();
2690 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2692 PluginManager::PluginStatusType status = PluginManager::Favorite;
2693 PluginManager& manager (PluginManager::instance());
2695 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2696 manager.save_statuses ();
2700 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2702 /* call protected MixerActor:: method */
2707 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2709 /* call protected MixerActor:: method */