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 if ((done[0] = (allocation.get_height() > pos))) {
1964 rhs_pane1.set_position (pos);
1967 if ((done[0] = (allocation.get_height() > 1.0/pos))) {
1968 paned_set_position_as_fraction (rhs_pane1, pos, true);
1973 if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
1979 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
1980 pos = 2 * height / 3;
1982 pos = atof (prop->value());
1986 if ((done[1] = (allocation.get_height() > pos))) {
1987 rhs_pane2.set_position (pos);
1990 if ((done[1] = (allocation.get_height() > 1.0/pos))) {
1991 paned_set_position_as_fraction (rhs_pane2, pos, true);
1996 if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
2002 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
2003 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2005 pos = max (0.1, atof (prop->value ()));
2009 if ((done[2] = (allocation.get_width() > pos))) {
2010 list_hpane.set_position (pos);
2013 if ((done[2] = (allocation.get_width() > 1.0/pos))) {
2014 paned_set_position_as_fraction (list_hpane, pos, false);
2019 if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
2025 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
2026 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2028 pos = max (0.1, atof (prop->value ()));
2032 if ((done[3] = (allocation.get_width() > pos))) {
2033 inner_pane.set_position (pos);
2036 if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2037 paned_set_position_as_fraction (inner_pane, pos, false);
2044 Mixer_UI::scroll_left ()
2046 if (!scroller.get_hscrollbar()) return;
2047 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2048 /* stupid GTK: can't rely on clamping across versions */
2049 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2053 Mixer_UI::scroll_right ()
2055 if (!scroller.get_hscrollbar()) return;
2056 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2057 /* stupid GTK: can't rely on clamping across versions */
2058 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2062 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2064 switch (ev->direction) {
2065 case GDK_SCROLL_LEFT:
2069 if (ev->state & Keyboard::TertiaryModifier) {
2075 case GDK_SCROLL_RIGHT:
2079 case GDK_SCROLL_DOWN:
2080 if (ev->state & Keyboard::TertiaryModifier) {
2092 Mixer_UI::parameter_changed (string const & p)
2094 if (p == "show-group-tabs") {
2095 bool const s = _session->config.get_show_group_tabs ();
2097 _group_tabs->show ();
2099 _group_tabs->hide ();
2101 } else if (p == "default-narrow_ms") {
2102 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2103 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2104 (*i)->set_width_enum (s ? Narrow : Wide, this);
2106 } else if (p == "remote-model") {
2107 reset_remote_control_ids ();
2108 } else if (p == "use-monitor-bus") {
2109 if (_session && !_session->monitor_out()) {
2110 monitor_section_detached ();
2116 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2118 g->set_active (a, this);
2122 Mixer_UI::plugin_selector()
2124 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2125 if (!_plugin_selector)
2126 _plugin_selector = new PluginSelector (PluginManager::instance());
2129 return _plugin_selector;
2133 Mixer_UI::setup_track_display ()
2135 track_model = ListStore::create (track_columns);
2136 track_display.set_model (track_model);
2137 track_display.append_column (_("Strips"), track_columns.text);
2138 track_display.append_column (_("Show"), track_columns.visible);
2139 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2140 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2141 track_display.get_column (0)->set_expand(true);
2142 track_display.get_column (1)->set_expand(false);
2143 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2144 track_display.set_name (X_("EditGroupList"));
2145 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2146 track_display.set_reorderable (true);
2147 track_display.set_headers_visible (true);
2148 track_display.set_can_focus(false);
2150 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2151 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2153 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2154 track_list_visible_cell->property_activatable() = true;
2155 track_list_visible_cell->property_radio() = false;
2156 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2158 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2160 track_display_scroller.add (track_display);
2161 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2163 VBox* v = manage (new VBox);
2165 v->pack_start (track_display_scroller, true, true);
2167 Button* b = manage (new Button);
2169 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2173 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2175 v->pack_start (*b, false, false);
2177 track_display_frame.set_name("BaseFrame");
2178 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2179 track_display_frame.add (*v);
2181 track_display_scroller.show();
2182 track_display_frame.show();
2183 track_display.show();
2187 Mixer_UI::new_track_or_bus ()
2189 ARDOUR_UI::instance()->add_route ();
2194 Mixer_UI::update_title ()
2196 if (!own_window()) {
2203 if (_session->snap_name() != _session->name()) {
2204 n = _session->snap_name ();
2206 n = _session->name ();
2209 if (_session->dirty ()) {
2213 WindowTitle title (n);
2214 title += S_("Window|Mixer");
2215 title += Glib::get_application_name ();
2216 own_window()->set_title (title.get_string());
2220 WindowTitle title (S_("Window|Mixer"));
2221 title += Glib::get_application_name ();
2222 own_window()->set_title (title.get_string());
2227 Mixer_UI::strip_by_x (int x)
2229 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2232 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2233 x2 = x1 + (*i)->get_width();
2235 if (x >= x1 && x <= x2) {
2244 Mixer_UI::set_route_targets_for_operation ()
2246 _route_targets.clear ();
2248 if (!_selection.empty()) {
2249 _route_targets = _selection.routes;
2253 // removed "implicit" selections of strips, after discussion on IRC
2258 Mixer_UI::monitor_section_going_away ()
2260 if (_monitor_section) {
2261 monitor_section_detached ();
2262 out_packer.remove (_monitor_section->tearoff());
2263 _monitor_section->set_session (0);
2264 delete _monitor_section;
2265 _monitor_section = 0;
2270 Mixer_UI::toggle_midi_input_active (bool flip_others)
2272 boost::shared_ptr<RouteList> rl (new RouteList);
2275 set_route_targets_for_operation ();
2277 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2278 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2281 rl->push_back ((*r)->route());
2282 onoff = !mt->input_active();
2286 _session->set_exclusive_input_active (rl, onoff, flip_others);
2290 Mixer_UI::maximise_mixer_space ()
2292 if (!own_window()) {
2300 _window->fullscreen ();
2305 Mixer_UI::restore_mixer_space ()
2307 if (!own_window()) {
2315 own_window()->unfullscreen();
2320 Mixer_UI::monitor_section_attached ()
2322 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2323 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2324 act->set_sensitive (true);
2325 tact->set_active ();
2329 Mixer_UI::monitor_section_detached ()
2331 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2332 act->set_sensitive (false);
2336 Mixer_UI::store_current_favorite_order ()
2338 typedef Gtk::TreeModel::Children type_children;
2339 type_children children = favorite_plugins_model->children();
2340 favorite_order.clear();
2341 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2343 Gtk::TreeModel::Row row = *iter;
2344 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2345 favorite_order.push_back (ppp->_pip);
2346 std::string name = row[favorite_plugins_columns.name];
2347 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2352 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2354 Gtk::TreeModel::Row row = *iter;
2355 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2357 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2361 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2363 PluginManager& manager (PluginManager::instance());
2364 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2365 if (manager.get_status (*i) != PluginManager::Favorite) {
2368 result.push_back (*i);
2372 struct PluginCustomSorter {
2374 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2375 PluginInfoList::const_iterator aiter = _user.begin();
2376 PluginInfoList::const_iterator biter = _user.begin();
2377 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2378 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2380 if (aiter != _user.end() && biter != _user.end()) {
2381 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2383 if (aiter != _user.end()) {
2386 if (biter != _user.end()) {
2389 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2391 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2393 PluginInfoList _user;
2397 Mixer_UI::refill_favorite_plugins ()
2399 PluginInfoList plugs;
2400 PluginManager& mgr (PluginManager::instance());
2403 refiller (plugs, mgr.lv2_plugin_info ());
2405 #ifdef WINDOWS_VST_SUPPORT
2406 refiller (plugs, mgr.windows_vst_plugin_info ());
2408 #ifdef LXVST_SUPPORT
2409 refiller (plugs, mgr.lxvst_plugin_info ());
2411 #ifdef AUDIOUNIT_SUPPORT
2412 refiller (plugs, mgr.au_plugin_info ());
2414 refiller (plugs, mgr.ladspa_plugin_info ());
2415 refiller (plugs, mgr.lua_plugin_info ());
2417 store_current_favorite_order ();
2419 PluginCustomSorter cmp (favorite_order);
2422 favorite_order = plugs;
2424 sync_treeview_from_favorite_order ();
2428 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2431 if (!(iter = favorite_plugins_model->get_iter (path))) {
2434 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2438 PluginInfoPtr pip = ppp->_pip;
2439 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2440 if (favorite_ui_state[pip->unique_id]) {
2441 favorite_plugins_display.expand_row (path, true);
2447 Mixer_UI::sync_treeview_from_favorite_order ()
2449 favorite_plugins_model->clear ();
2450 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2451 PluginInfoPtr pip = (*i);
2453 TreeModel::Row newrow = *(favorite_plugins_model->append());
2454 newrow[favorite_plugins_columns.name] = (*i)->name;
2455 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2460 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2461 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2462 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2463 child_row[favorite_plugins_columns.name] = (*j).label;
2464 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2466 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2467 if (favorite_ui_state[pip->unique_id]) {
2468 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2475 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2477 using namespace Gtk::Menu_Helpers;
2479 Gtk::Menu* m = manage (new Menu);
2480 MenuList& items = m->items ();
2482 if (_selection.routes.empty()) {
2483 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2485 items.push_back (MenuElem (_("Add at the top"),
2486 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2487 items.push_back (MenuElem (_("Add Pre-Fader"),
2488 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2489 items.push_back (MenuElem (_("Add Post-Fader"),
2490 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2491 items.push_back (MenuElem (_("Add at the end"),
2492 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2495 items.push_back (SeparatorElem());
2497 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2499 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2500 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2501 // we cannot currently delete AU presets
2502 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2503 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2507 m->popup (ev->button, ev->time);
2511 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2513 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2514 TreeModel::Path path;
2515 TreeViewColumn* column;
2517 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2518 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2520 selection->unselect_all();
2521 selection->select(path);
2524 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2526 popup_note_context_menu (ev);
2534 Mixer_UI::selected_plugin ()
2536 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2538 return PluginPresetPtr();
2540 Gtk::TreeModel::iterator iter = selection->get_selected();
2542 return PluginPresetPtr();
2544 return (*iter)[favorite_plugins_columns.plugin];
2548 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2550 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2552 add_favorite_processor (ppp, pos);
2557 Mixer_UI::delete_selected_preset ()
2562 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2563 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2566 PluginPtr plugin = ppp->_pip->load (*_session);
2567 plugin->get_presets();
2568 plugin->remove_preset (ppp->_preset.label);
2572 Mixer_UI::remove_selected_from_favorites ()
2574 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2578 PluginManager::PluginStatusType status = PluginManager::Normal;
2579 PluginManager& manager (PluginManager::instance());
2581 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2582 manager.save_statuses ();
2586 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2589 if (!(iter = favorite_plugins_model->get_iter (path))) {
2592 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2593 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2597 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2599 if (!_session || _selection.routes.empty()) {
2603 PluginInfoPtr pip = ppp->_pip;
2604 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2605 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2606 if (!rt) { continue; }
2608 PluginPtr p = pip->load (*_session);
2609 if (!p) { continue; }
2611 if (ppp->_preset.valid) {
2612 p->load_preset (ppp->_preset);
2615 Route::ProcessorStreams err;
2616 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2620 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2623 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2630 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2634 if (!np->display_to_user()) {
2637 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2638 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2643 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2647 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2654 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2656 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2660 // only allow to re-order top-level items
2662 if (TreePath::get_from_selection_data (data, src)) {
2663 if (src.up() && src.up()) {
2668 // don't allow to drop as child-rows.
2669 Gtk::TreeModel::Path _dest = dest; // un const
2670 const bool is_child = _dest.up (); // explicit bool for clang
2671 if (!is_child || _dest.empty ()) {
2678 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2680 if (data.get_target() != "PluginPresetPtr") {
2683 if (data.get_length() != sizeof (PluginPresetPtr)) {
2686 const void *d = data.get_data();
2687 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2689 PluginManager::PluginStatusType status = PluginManager::Favorite;
2690 PluginManager& manager (PluginManager::instance());
2692 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2693 manager.save_statuses ();
2697 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2699 /* call protected MixerActor:: method */
2704 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2706 /* call protected MixerActor:: method */