2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include <sigc++/bind.h>
28 #include <gtkmm/accelmap.h>
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/unwind.h"
34 #include <glibmm/threads.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/keyboard.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/tearoff.h>
40 #include <gtkmm2ext/window_title.h>
41 #include <gtkmm2ext/doi.h>
43 #include "ardour/amp.h"
44 #include "ardour/debug.h"
45 #include "ardour/audio_track.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/plugin_manager.h"
48 #include "ardour/route_group.h"
49 #include "ardour/session.h"
50 #include "ardour/vca.h"
51 #include "ardour/vca_manager.h"
55 #include "mixer_strip.h"
56 #include "monitor_section.h"
57 #include "plugin_selector.h"
58 #include "public_editor.h"
59 #include "ardour_ui.h"
62 #include "route_sorter.h"
64 #include "gui_thread.h"
65 #include "mixer_group_tabs.h"
67 #include "ui_config.h"
68 #include "vca_master_strip.h"
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
83 Mixer_UI* Mixer_UI::_instance = 0;
89 _instance = new Mixer_UI;
96 : Tabbable (_content, _("Mixer"))
97 , no_track_list_redisplay (false)
98 , in_group_row_change (false)
100 , _monitor_section (0)
101 , _plugin_selector (0)
102 , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
103 , ignore_reorder (false)
104 , _in_group_rebuild_or_clear (false)
105 , _route_deletion_in_progress (false)
106 , _following_editor_selection (false)
108 , _show_mixer_list (true)
110 Stripable::PresentationInfoChange.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_presentation_info, this), gui_context());
112 /* bindings was already set in MixerActor constructor */
114 _content.set_data ("ardour-bindings", bindings);
116 scroller.set_can_default (true);
117 // set_default (scroller);
119 scroller_base.set_flags (Gtk::CAN_FOCUS);
120 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
121 scroller_base.set_name ("MixerWindow");
122 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
124 /* set up drag-n-drop */
125 vector<TargetEntry> target_table;
126 target_table.push_back (TargetEntry ("PluginFavoritePtr"));
127 scroller_base.drag_dest_set (target_table);
128 scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
130 // add as last item of strip packer
131 strip_packer.pack_end (scroller_base, true, true);
133 _group_tabs = new MixerGroupTabs (this);
134 VBox* b = manage (new VBox);
135 b->pack_start (*_group_tabs, PACK_SHRINK);
136 b->pack_start (strip_packer);
140 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
142 setup_track_display ();
144 group_model = ListStore::create (group_columns);
145 group_display.set_model (group_model);
146 group_display.append_column (_("Group"), group_columns.text);
147 group_display.append_column (_("Show"), group_columns.visible);
148 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
149 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
150 group_display.get_column (0)->set_expand(true);
151 group_display.get_column (1)->set_expand(false);
152 group_display.set_name ("EditGroupList");
153 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
154 group_display.set_reorderable (true);
155 group_display.set_headers_visible (true);
156 group_display.set_rules_hint (true);
157 group_display.set_can_focus(false);
159 /* name is directly editable */
161 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
162 name_cell->property_editable() = true;
163 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
165 /* use checkbox for the active column */
167 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
168 active_cell->property_activatable() = true;
169 active_cell->property_radio() = false;
171 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
172 /* We use this to notice drag-and-drop reorders of the group list */
173 group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
174 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
176 group_display_scroller.add (group_display);
177 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
179 HBox* route_group_display_button_box = manage (new HBox());
181 Button* route_group_add_button = manage (new Button ());
182 Button* route_group_remove_button = manage (new Button ());
186 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
188 route_group_add_button->add (*w);
190 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
192 route_group_remove_button->add (*w);
194 route_group_display_button_box->set_homogeneous (true);
196 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
197 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
199 route_group_display_button_box->add (*route_group_add_button);
200 route_group_display_button_box->add (*route_group_remove_button);
202 group_display_vbox.pack_start (group_display_scroller, true, true);
203 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
205 group_display_frame.set_name ("BaseFrame");
206 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
207 group_display_frame.add (group_display_vbox);
210 list<TargetEntry> target_list;
211 target_list.push_back (TargetEntry ("PluginPresetPtr"));
213 favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
214 favorite_plugins_display.set_model (favorite_plugins_model);
215 favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
216 favorite_plugins_display.set_name ("EditGroupList");
217 favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
218 favorite_plugins_display.set_reorderable (false);
219 favorite_plugins_display.set_headers_visible (true);
220 favorite_plugins_display.set_rules_hint (true);
221 favorite_plugins_display.set_can_focus (false);
222 favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
223 favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
224 favorite_plugins_display.add_drop_targets (target_list);
225 favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
226 favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
227 favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
228 favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
229 favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
230 favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
232 favorite_plugins_scroller.add (favorite_plugins_display);
233 favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
235 favorite_plugins_frame.set_name ("BaseFrame");
236 favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
237 favorite_plugins_frame.add (favorite_plugins_scroller);
239 rhs_pane1.pack1 (favorite_plugins_frame, false, true);
240 rhs_pane1.pack2 (track_display_frame);
241 rhs_pane2.pack1 (rhs_pane1);
242 rhs_pane2.pack2 (group_display_frame);
244 list_vpacker.pack_start (rhs_pane2, true, true);
246 vca_scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
247 vca_scroller_base.set_name ("MixerWindow");
248 vca_scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::masters_scroller_button_release), false);
249 vca_packer.pack_end (vca_scroller_base, true, true);
251 vca_scroller.add (vca_packer);
252 vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
253 vca_scroller.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
255 inner_pane.pack1 (scroller);
256 inner_pane.pack2 (vca_scroller);
258 global_hpacker.pack_start (inner_pane, true, true);
259 global_hpacker.pack_start (out_packer, false, false);
261 list_hpane.pack1(list_vpacker, false, true);
262 list_hpane.pack2(global_hpacker, true, false);
264 rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
265 static_cast<Gtk::Paned*> (&rhs_pane1)));
266 rhs_pane2.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
267 static_cast<Gtk::Paned*> (&rhs_pane2)));
268 list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
269 static_cast<Gtk::Paned*> (&list_hpane)));
270 inner_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
271 static_cast<Gtk::Paned*> (&inner_pane)));
273 _content.pack_start (list_hpane, true, true);
277 route_group_display_button_box->show();
278 route_group_add_button->show();
279 route_group_remove_button->show();
282 _content.set_name ("MixerWindow");
284 global_hpacker.show();
286 scroller_base.show();
287 scroller_hpacker.show();
288 mixer_scroller_vpacker.show();
290 group_display_button_label.show();
291 group_display_button.show();
292 group_display_scroller.show();
293 favorite_plugins_scroller.show();
294 group_display_vbox.show();
295 group_display_frame.show();
296 favorite_plugins_frame.show();
303 vca_scroller_base.show();
306 group_display.show();
307 favorite_plugins_display.show();
309 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
311 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
312 _plugin_selector = new PluginSelector (PluginManager::instance ());
314 #error implement deferred Plugin-Favorite list
316 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
317 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
318 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
321 Mixer_UI::~Mixer_UI ()
323 if (_monitor_section) {
324 monitor_section_detached ();
325 delete _monitor_section;
327 delete _plugin_selector;
331 Mixer_UI::track_editor_selection ()
333 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
337 Mixer_UI::use_own_window (bool and_fill_it)
339 bool new_window = !own_window();
341 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
344 if (win && new_window) {
345 win->set_name ("MixerWindow");
346 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
347 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
348 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
349 win->set_data ("ardour-bindings", bindings);
357 Mixer_UI::show_window ()
359 Tabbable::show_window ();
361 /* show/hide group tabs as required */
362 parameter_changed ("show-group-tabs");
364 /* now reset each strips width so the right widgets are shown */
367 TreeModel::Children rows = track_model->children();
368 TreeModel::Children::iterator ri;
370 for (ri = rows.begin(); ri != rows.end(); ++ri) {
371 ms = (*ri)[track_columns.strip];
375 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
376 /* Fix visibility of mixer strip stuff */
377 ms->parameter_changed (X_("mixer-element-visibility"));
380 /* force focus into main area */
381 scroller_base.grab_focus ();
385 Mixer_UI::add_masters (VCAList& vcas)
387 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
389 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
391 TreeModel::Row row = *(track_model->append());
392 row[track_columns.text] = (*v)->name();
393 row[track_columns.visible] = true;
394 row[track_columns.vca] = vms;
396 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
399 redisplay_track_list ();
403 Mixer_UI::remove_master (VCAMasterStrip* vms)
405 if (_session && _session->deletion_in_progress()) {
406 /* its all being taken care of */
410 TreeModel::Children rows = track_model->children();
411 TreeModel::Children::iterator ri;
413 for (ri = rows.begin(); ri != rows.end(); ++ri) {
414 if ((*ri)[track_columns.vca] == vms) {
415 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
416 track_model->erase (ri);
423 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
425 using namespace Menu_Helpers;
427 if (Keyboard::is_context_menu_event (ev)) {
428 ARDOUR_UI::instance()->add_route ();
436 Mixer_UI::add_strips (RouteList& routes)
438 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
439 uint32_t nroutes = 0;
441 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
442 boost::shared_ptr<Route> r = (*it)[track_columns.route];
450 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
457 _selection.clear_routes ();
463 no_track_list_redisplay = true;
464 track_display.set_model (Glib::RefPtr<ListStore>());
466 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
467 boost::shared_ptr<Route> route = (*x);
469 if (route->is_auditioner()) {
473 if (route->is_monitor()) {
475 if (!_monitor_section) {
476 _monitor_section = new MonitorSection (_session);
478 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
480 _monitor_section->tearoff().set_state (*mnode);
484 out_packer.pack_end (_monitor_section->tearoff(), false, false);
485 _monitor_section->set_session (_session);
486 _monitor_section->tearoff().show_all ();
488 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
489 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
491 monitor_section_attached ();
493 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
495 /* no regular strip shown for control out */
500 strip = new MixerStrip (*this, _session, route);
501 strips.push_back (strip);
503 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
505 if (strip->width_owner() != strip) {
506 strip->set_width_enum (_strip_width, this);
511 TreeModel::Row row = *(track_model->insert(insert_iter));
512 row[track_columns.text] = route->name();
513 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
514 row[track_columns.route] = route;
515 row[track_columns.strip] = strip;
516 row[track_columns.vca] = 0;
519 _selection.add (strip);
522 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
524 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
525 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
528 } catch (const std::exception& e) {
529 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
532 no_track_list_redisplay = false;
533 track_display.set_model (track_model);
535 sync_presentation_info_from_treeview ();
536 redisplay_track_list ();
540 Mixer_UI::deselect_all_strip_processors ()
542 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
543 (*i)->deselect_all_processors();
548 Mixer_UI::select_strip (MixerStrip& ms, bool add)
551 _selection.add (&ms);
553 _selection.set (&ms);
558 Mixer_UI::select_none ()
560 _selection.clear_routes();
561 deselect_all_strip_processors();
565 Mixer_UI::delete_processors ()
567 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
568 (*i)->delete_processors();
574 Mixer_UI::remove_strip (MixerStrip* strip)
576 if (_session && _session->deletion_in_progress()) {
577 /* its all being taken care of */
581 TreeModel::Children rows = track_model->children();
582 TreeModel::Children::iterator ri;
583 list<MixerStrip *>::iterator i;
585 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
589 for (ri = rows.begin(); ri != rows.end(); ++ri) {
590 if ((*ri)[track_columns.strip] == strip) {
591 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
592 track_model->erase (ri);
599 Mixer_UI::sync_presentation_info_from_treeview ()
601 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
605 TreeModel::Children rows = track_model->children();
611 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
613 TreeModel::Children::iterator ri;
617 for (ri = rows.begin(); ri != rows.end(); ++ri) {
618 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
619 bool visible = (*ri)[track_columns.visible];
626 if (route->presentation_info().special()) {
631 route->presentation_info().set_flag (PresentationInfo::Hidden);
633 route->presentation_info().unset_flag (PresentationInfo::Hidden);
636 if (order != route->presentation_info().group_order()) {
637 route->set_presentation_group_order_explicit (order);
645 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
646 _session->notify_presentation_info_change ();
651 Mixer_UI::sync_treeview_from_presentation_info ()
653 if (!_session || _session->deletion_in_progress()) {
657 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
659 /* we could get here after either a change in the Mixer or Editor sort
660 * order, but either way, the mixer order keys reflect the intended
661 * order for the GUI, so reorder the treeview model to match it.
664 vector<int> neworder;
665 TreeModel::Children rows = track_model->children();
666 uint32_t old_order = 0;
667 bool changed = false;
674 uint32_t vca_cnt = 0;
675 uint32_t max_route_order_key = 0;
677 /* count number of Routes in track_model (there may be some odd reason
678 why this is not the same as the number in the session, but here we
679 care about the track model.
682 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
683 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
685 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
689 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
690 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
692 /* VCAs need to sort after all routes. We don't display
693 * them in the same place (March 2016), but we don't
694 * want them intermixed in the track_model
696 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
698 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
702 SortByNewDisplayOrder cmp;
704 sort (sorted.begin(), sorted.end(), cmp);
705 neworder.assign (sorted.size(), 0);
709 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
711 neworder[n] = sr->old_display_order;
713 if (sr->old_display_order != n) {
719 Unwinder<bool> uw (ignore_reorder, true);
720 track_model->reorder (neworder);
723 redisplay_track_list ();
727 Mixer_UI::follow_editor_selection ()
729 if (_following_editor_selection) {
733 _following_editor_selection = true;
734 _selection.block_routes_changed (true);
736 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
738 _selection.clear_routes ();
740 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
741 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
743 MixerStrip* ms = strip_by_route (rtav->route());
750 _following_editor_selection = false;
751 _selection.block_routes_changed (false);
756 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
758 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
759 if ((*i)->route() == r) {
768 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
770 if (ev->button == 1) {
771 if (_selection.selected (strip)) {
772 /* primary-click: toggle selection state of strip */
773 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
774 _selection.remove (strip);
775 } else if (_selection.routes.size() > 1) {
776 /* de-select others */
777 _selection.set (strip);
780 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
781 _selection.add (strip);
782 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
784 if (!_selection.selected(strip)) {
786 /* extend selection */
788 vector<MixerStrip*> tmp;
789 bool accumulate = false;
790 bool found_another = false;
792 tmp.push_back (strip);
794 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
796 /* hit clicked strip, start accumulating till we hit the first
805 } else if (_selection.selected (*i)) {
806 /* hit selected strip. if currently accumulating others,
807 we're done. if not accumulating others, start doing so.
809 found_another = true;
824 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
828 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
832 _selection.set (strip);
841 Mixer_UI::set_session (Session* sess)
843 SessionHandlePtr::set_session (sess);
845 if (_plugin_selector) {
846 _plugin_selector->set_session (_session);
849 _group_tabs->set_session (sess);
855 refill_favorite_plugins();
857 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
858 set_state (*node, 0);
862 initial_track_display ();
864 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
865 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
866 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
867 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
868 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
869 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
870 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
872 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
874 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
876 route_groups_changed ();
885 Mixer_UI::session_going_away ()
887 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
889 _in_group_rebuild_or_clear = true;
890 group_model->clear ();
891 _in_group_rebuild_or_clear = false;
894 track_model->clear ();
896 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
900 if (_monitor_section) {
901 _monitor_section->tearoff().hide_visible ();
904 monitor_section_detached ();
910 SessionHandlePtr::session_going_away ();
917 Mixer_UI::track_visibility_changed (std::string const & path)
919 if (_session && _session->deletion_in_progress()) {
925 if ((iter = track_model->get_iter (path))) {
926 MixerStrip* strip = (*iter)[track_columns.strip];
928 bool visible = (*iter)[track_columns.visible];
930 if (strip->set_marked_for_display (!visible)) {
931 update_track_visibility ();
938 Mixer_UI::update_track_visibility ()
940 TreeModel::Children rows = track_model->children();
941 TreeModel::Children::iterator i;
944 Unwinder<bool> uw (no_track_list_redisplay, true);
946 for (i = rows.begin(); i != rows.end(); ++i) {
947 MixerStrip *strip = (*i)[track_columns.strip];
949 (*i)[track_columns.visible] = strip->marked_for_display ();
953 /* force presentation catch up with visibility changes
956 sync_presentation_info_from_treeview ();
959 redisplay_track_list ();
963 Mixer_UI::show_strip (MixerStrip* ms)
965 TreeModel::Children rows = track_model->children();
966 TreeModel::Children::iterator i;
968 for (i = rows.begin(); i != rows.end(); ++i) {
970 MixerStrip* strip = (*i)[track_columns.strip];
972 (*i)[track_columns.visible] = true;
973 redisplay_track_list ();
980 Mixer_UI::hide_strip (MixerStrip* ms)
982 TreeModel::Children rows = track_model->children();
983 TreeModel::Children::iterator i;
985 for (i = rows.begin(); i != rows.end(); ++i) {
987 MixerStrip* strip = (*i)[track_columns.strip];
989 (*i)[track_columns.visible] = false;
990 redisplay_track_list ();
997 Mixer_UI::start_updating ()
999 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1004 Mixer_UI::stop_updating ()
1006 fast_screen_update_connection.disconnect();
1011 Mixer_UI::fast_update_strips ()
1013 if (_content.is_mapped () && _session) {
1014 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1015 (*i)->fast_update ();
1021 Mixer_UI::set_all_strips_visibility (bool yn)
1023 TreeModel::Children rows = track_model->children();
1024 TreeModel::Children::iterator i;
1027 Unwinder<bool> uw (no_track_list_redisplay, true);
1029 for (i = rows.begin(); i != rows.end(); ++i) {
1031 TreeModel::Row row = (*i);
1032 MixerStrip* strip = row[track_columns.strip];
1038 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1042 (*i)[track_columns.visible] = yn;
1046 redisplay_track_list ();
1051 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1053 TreeModel::Children rows = track_model->children();
1054 TreeModel::Children::iterator i;
1057 Unwinder<bool> uw (no_track_list_redisplay, true);
1059 for (i = rows.begin(); i != rows.end(); ++i) {
1060 TreeModel::Row row = (*i);
1061 MixerStrip* strip = row[track_columns.strip];
1067 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1071 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1072 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1076 (*i)[track_columns.visible] = yn;
1080 if (at) { /* track */
1081 (*i)[track_columns.visible] = yn;
1086 if (!at && !mt) { /* bus */
1087 (*i)[track_columns.visible] = yn;
1092 if (mt) { /* midi-track */
1093 (*i)[track_columns.visible] = yn;
1100 redisplay_track_list ();
1104 Mixer_UI::hide_all_routes ()
1106 set_all_strips_visibility (false);
1110 Mixer_UI::show_all_routes ()
1112 set_all_strips_visibility (true);
1116 Mixer_UI::show_all_audiobus ()
1118 set_all_audio_midi_visibility (2, true);
1121 Mixer_UI::hide_all_audiobus ()
1123 set_all_audio_midi_visibility (2, false);
1127 Mixer_UI::show_all_audiotracks()
1129 set_all_audio_midi_visibility (1, true);
1132 Mixer_UI::hide_all_audiotracks ()
1134 set_all_audio_midi_visibility (1, false);
1138 Mixer_UI::show_all_miditracks()
1140 set_all_audio_midi_visibility (3, true);
1143 Mixer_UI::hide_all_miditracks ()
1145 set_all_audio_midi_visibility (3, false);
1150 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1152 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1153 sync_presentation_info_from_treeview ();
1157 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1159 /* this happens as the second step of a DnD within the treeview as well
1160 as when a row/route is actually deleted.
1162 if it was a deletion then we have to force a redisplay because
1163 order keys may not have changed.
1166 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1167 sync_presentation_info_from_treeview ();
1169 if (_route_deletion_in_progress) {
1170 redisplay_track_list ();
1175 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1177 TreeModel::Children rows = track_model->children();
1179 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1181 MixerStrip* strip = (*i)[track_columns.strip];
1184 /* we're in the middle of changing a row, don't worry */
1188 if (!strip->route()) {
1189 /* non-route element */
1193 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1197 if (strip->route()->slaved_to (vca)) {
1199 strip->set_gui_property ("visible", true);
1201 if (strip->packed()) {
1202 strip_packer.reorder_child (*strip, -1); /* put at end */
1204 strip_packer.pack_start (*strip, false, false);
1205 strip->set_packed (true);
1210 strip->set_gui_property ("visible", false);
1212 if (strip->packed()) {
1213 strip_packer.remove (*strip);
1214 strip->set_packed (false);
1221 Mixer_UI::redisplay_track_list ()
1223 if (no_track_list_redisplay) {
1227 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1230 spill_redisplay (sv);
1234 TreeModel::Children rows = track_model->children();
1235 TreeModel::Children::iterator i;
1236 uint32_t n_masters = 0;
1238 container_clear (vca_packer);
1239 vca_packer.pack_end (vca_scroller_base, true, true);
1241 for (i = rows.begin(); i != rows.end(); ++i) {
1243 VCAMasterStrip* vms = (*i)[track_columns.vca];
1246 vca_packer.pack_start (*vms, false, false);
1252 MixerStrip* strip = (*i)[track_columns.strip];
1255 /* we're in the middle of changing a row, don't worry */
1259 bool const visible = (*i)[track_columns.visible];
1262 strip->set_gui_property ("visible", true);
1264 if (strip->packed()) {
1266 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1267 out_packer.reorder_child (*strip, -1);
1270 strip_packer.reorder_child (*strip, -1); /* put at end */
1275 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1276 out_packer.pack_start (*strip, false, false);
1278 strip_packer.pack_start (*strip, false, false);
1280 strip->set_packed (true);
1285 strip->set_gui_property ("visible", false);
1287 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1288 /* do nothing, these cannot be hidden */
1290 if (strip->packed()) {
1291 strip_packer.remove (*strip);
1292 strip->set_packed (false);
1298 /* update visibility of VCA assign buttons */
1300 if (n_masters == 0) {
1301 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1302 vca_scroller.hide ();
1304 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1305 vca_scroller.show ();
1308 _group_tabs->set_dirty ();
1312 Mixer_UI::strip_width_changed ()
1314 _group_tabs->set_dirty ();
1317 TreeModel::Children rows = track_model->children();
1318 TreeModel::Children::iterator i;
1321 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1322 MixerStrip* strip = (*i)[track_columns.strip];
1328 bool visible = (*i)[track_columns.visible];
1331 strip->queue_draw();
1338 struct PresentationInfoRouteSorter
1340 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1341 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1346 Mixer_UI::initial_track_display ()
1348 boost::shared_ptr<RouteList> routes = _session->get_routes();
1349 RouteList copy (*routes);
1350 PresentationInfoRouteSorter sorter;
1355 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1356 Unwinder<bool> uw2 (ignore_reorder, true);
1358 track_model->clear ();
1359 VCAList vcas = _session->vca_manager().vcas();
1364 redisplay_track_list ();
1368 Mixer_UI::show_track_list_menu ()
1370 if (track_menu == 0) {
1371 build_track_menu ();
1374 track_menu->popup (1, gtk_get_current_event_time());
1378 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1380 if (Keyboard::is_context_menu_event (ev)) {
1381 show_track_list_menu ();
1389 Mixer_UI::build_track_menu ()
1391 using namespace Menu_Helpers;
1392 using namespace Gtk;
1394 track_menu = new Menu;
1395 track_menu->set_name ("ArdourContextMenu");
1396 MenuList& items = track_menu->items();
1398 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1399 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1400 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1401 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1402 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1403 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1404 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1405 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1410 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1412 if (!what_changed.contains (ARDOUR::Properties::name)) {
1416 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1418 TreeModel::Children rows = track_model->children();
1419 TreeModel::Children::iterator i;
1421 for (i = rows.begin(); i != rows.end(); ++i) {
1422 if ((*i)[track_columns.strip] == mx) {
1423 (*i)[track_columns.text] = mx->route()->name();
1428 error << _("track display list item for renamed strip not found!") << endmsg;
1432 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1434 TreeModel::Path path;
1435 TreeViewColumn* column;
1439 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1440 _group_tabs->get_menu(0)->popup (1, ev->time);
1444 TreeIter iter = group_model->get_iter (path);
1446 _group_tabs->get_menu(0)->popup (1, ev->time);
1450 RouteGroup* group = (*iter)[group_columns.group];
1452 if (Keyboard::is_context_menu_event (ev)) {
1453 _group_tabs->get_menu(group)->popup (1, ev->time);
1457 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1459 if (Keyboard::is_edit_event (ev)) {
1461 // edit_route_group (group);
1463 group_display.queue_draw();
1472 bool visible = (*iter)[group_columns.visible];
1473 (*iter)[group_columns.visible] = !visible;
1475 group_display.queue_draw();
1488 Mixer_UI::activate_all_route_groups ()
1490 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1494 Mixer_UI::disable_all_route_groups ()
1496 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1500 Mixer_UI::route_groups_changed ()
1502 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1504 _in_group_rebuild_or_clear = true;
1506 /* just rebuild the while thing */
1508 group_model->clear ();
1511 /* this is currently not used,
1512 * Mixer_UI::group_display_button_press() has a case for it,
1513 * and a commented edit_route_group() but that's n/a since 2011.
1515 * This code is left as reminder that
1516 * row[group_columns.group] = 0 has special meaning.
1520 row = *(group_model->append());
1521 row[group_columns.visible] = true;
1522 row[group_columns.text] = (_("-all-"));
1523 row[group_columns.group] = 0;
1527 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1529 _group_tabs->set_dirty ();
1530 _in_group_rebuild_or_clear = false;
1534 Mixer_UI::new_route_group ()
1538 _group_tabs->run_new_group_dialog (rl);
1542 Mixer_UI::remove_selected_route_group ()
1544 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1545 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1551 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1554 /* selection mode is single, so rows.begin() is it */
1556 if ((iter = group_model->get_iter (*i))) {
1558 RouteGroup* rg = (*iter)[group_columns.group];
1561 _session->remove_route_group (*rg);
1567 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1569 if (in_group_row_change) {
1573 /* force an update of any mixer strips that are using this group,
1574 otherwise mix group names don't change in mixer strips
1577 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1578 if ((*i)->route_group() == group) {
1579 (*i)->route_group_changed();
1583 TreeModel::iterator i;
1584 TreeModel::Children rows = group_model->children();
1585 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1587 in_group_row_change = true;
1589 for (i = rows.begin(); i != rows.end(); ++i) {
1590 if ((*i)[group_columns.group] == group) {
1591 (*i)[group_columns.visible] = !group->is_hidden ();
1592 (*i)[group_columns.text] = group->name ();
1597 in_group_row_change = false;
1599 if (change.contains (Properties::name)) {
1600 _group_tabs->set_dirty ();
1603 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1604 if ((*j)->route_group() == group) {
1605 if (group->is_hidden ()) {
1615 Mixer_UI::show_mixer_list (bool yn)
1618 list_vpacker.show ();
1620 //if user wants to show the pane, we should make sure that it is wide enough to be visible
1621 int width = list_hpane.get_position();
1623 list_hpane.set_position(40);
1626 list_vpacker.hide ();
1629 _show_mixer_list = yn;
1633 Mixer_UI::show_monitor_section (bool yn)
1635 if (!monitor_section()) {
1638 if (monitor_section()->tearoff().torn_off()) {
1643 monitor_section()->tearoff().show();
1645 monitor_section()->tearoff().hide();
1650 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1655 if ((iter = group_model->get_iter (path))) {
1657 if ((group = (*iter)[group_columns.group]) == 0) {
1661 if (new_text != group->name()) {
1662 group->set_name (new_text);
1668 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1672 if (in_group_row_change) {
1676 if ((group = (*iter)[group_columns.group]) == 0) {
1680 std::string name = (*iter)[group_columns.text];
1682 if (name != group->name()) {
1683 group->set_name (name);
1686 bool hidden = !(*iter)[group_columns.visible];
1688 if (hidden != group->is_hidden ()) {
1689 group->set_hidden (hidden, this);
1693 /** Called when a group model row is deleted, but also when the model is
1694 * reordered by a user drag-and-drop; the latter is what we are
1695 * interested in here.
1698 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1700 if (_in_group_rebuild_or_clear) {
1704 /* Re-write the session's route group list so that the new order is preserved */
1706 list<RouteGroup*> new_list;
1708 Gtk::TreeModel::Children children = group_model->children();
1709 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1710 RouteGroup* g = (*i)[group_columns.group];
1712 new_list.push_back (g);
1716 _session->reorder_route_groups (new_list);
1721 Mixer_UI::add_route_group (RouteGroup* group)
1723 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1726 in_group_row_change = true;
1728 TreeModel::Row row = *(group_model->append());
1729 row[group_columns.visible] = !group->is_hidden ();
1730 row[group_columns.group] = group;
1731 if (!group->name().empty()) {
1732 row[group_columns.text] = group->name();
1734 row[group_columns.text] = _("unnamed");
1738 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1741 TreeViewColumn* col = group_display.get_column (0);
1742 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1743 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1746 _group_tabs->set_dirty ();
1748 in_group_row_change = false;
1752 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1754 using namespace Menu_Helpers;
1756 if (Keyboard::is_context_menu_event (ev)) {
1757 ARDOUR_UI::instance()->add_route ();
1765 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1767 printf ("Mixer_UI::scroller_drag_data_received\n");
1768 if (data.get_target() != "PluginFavoritePtr") {
1769 context->drag_finish (false, false, time);
1773 const void * d = data.get_data();
1774 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1776 PluginPresetList nfos;
1778 tv->get_object_drag_data (nfos, &source);
1780 Route::ProcessorList pl;
1783 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1784 PluginPresetPtr ppp = (*i);
1785 PluginInfoPtr pip = ppp->_pip;
1786 if (!pip->is_instrument ()) {
1789 ARDOUR_UI::instance()->session_add_midi_track ((RouteGroup*) 0, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0, PresentationInfo::max_order);
1793 context->drag_finish (ok, false, time);
1797 Mixer_UI::set_strip_width (Width w, bool save)
1801 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1802 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1807 struct PluginStateSorter {
1809 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1810 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1811 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1812 if (aiter != _user.end() && biter != _user.end()) {
1813 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1815 if (aiter != _user.end()) {
1818 if (biter != _user.end()) {
1821 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1824 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1826 std::list<std::string> _user;
1830 Mixer_UI::set_state (const XMLNode& node, int version)
1832 XMLProperty const * prop;
1834 Tabbable::set_state (node, version);
1836 if ((prop = node.property ("narrow-strips"))) {
1837 if (string_is_affirmative (prop->value())) {
1838 set_strip_width (Narrow);
1840 set_strip_width (Wide);
1844 if ((prop = node.property ("show-mixer"))) {
1845 if (string_is_affirmative (prop->value())) {
1850 if ((prop = node.property ("maximised"))) {
1851 bool yn = string_is_affirmative (prop->value());
1852 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1854 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1855 bool fs = tact && tact->get_active();
1857 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1861 if ((prop = node.property ("show-mixer-list"))) {
1862 bool yn = string_is_affirmative (prop->value());
1863 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1865 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1867 /* do it twice to force the change */
1868 tact->set_active (!yn);
1869 tact->set_active (yn);
1873 XMLNode* plugin_order;
1874 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1875 store_current_favorite_order ();
1876 std::list<string> order;
1877 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1878 XMLNodeConstIterator i;
1879 for (i = kids.begin(); i != kids.end(); ++i) {
1880 if ((prop = (*i)->property ("unique-id"))) {
1881 std::string unique_id = prop->value();
1882 order.push_back (unique_id);
1883 if ((prop = (*i)->property ("expanded"))) {
1884 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1888 PluginStateSorter cmp (order);
1889 favorite_order.sort (cmp);
1890 sync_treeview_from_favorite_order ();
1896 Mixer_UI::get_state ()
1898 XMLNode* node = new XMLNode (X_("Mixer"));
1901 node->add_child_nocopy (Tabbable::get_state());
1903 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane1, true));
1904 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1905 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane2, true));
1906 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1907 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (list_hpane, false));
1908 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1909 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (inner_pane, false));
1910 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1912 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1913 node->add_property ("show-mixer", _visible ? "yes" : "no");
1914 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1915 node->add_property ("maximised", _maximised ? "yes" : "no");
1917 store_current_favorite_order ();
1918 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1920 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1921 XMLNode* p = new XMLNode ("PluginInfo");
1922 p->add_property ("sort", cnt);
1923 p->add_property ("unique-id", (*i)->unique_id);
1924 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1925 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1927 plugin_order->add_child_nocopy (*p);
1929 node->add_child_nocopy (*plugin_order);
1935 Mixer_UI::pane_allocation_handler (Allocation& allocation, Gtk::Paned* which)
1938 XMLProperty* prop = 0;
1939 XMLNode* geometry = ARDOUR_UI::instance()->mixer_settings();
1940 int height = default_height;
1941 static bool done[4] = { false, false, false, false };
1943 /* Gtk::Paned behaves very oddly and rather undesirably. Setting the
1944 * position is a crapshoot if you time it incorrectly with the overall
1945 * sizing of the Paned. For example, if you might retrieve the size with
1946 * ::get_position() and then later call ::set_position() on a Paned at
1947 * a time when its allocation is different than it was when you retrieved
1948 * the position. The position will be interpreted as the size of the
1949 * first (top or left) child widget. If packing/size allocation later
1950 * resizes the Paned to a (final) smaller size, the position will be
1951 * used in ways that mean that the Paned ends up NOT in the same state
1952 * that it was in when you originally saved the position.
1954 * Concrete example: Paned is 800 pixels wide, position is 400
1955 * (halfway). Save position as 400. During program restart, set
1956 * position to 400, before Paned has been allocated any space. Paned
1957 * ends up initially sized to 1200 pixels. Position is now 1/3 of the
1958 * way across/down. Subsequent resizes will leave the position 1/3 of
1959 * the way across, rather than leaving it as a fixed pixel
1960 * position. Eventually, the Paned ends up 800 pixels wide/high again,
1961 * but the position is now 267, not 400.
1965 * We deal with this by using two strategies:
1967 * 1) only set the position if the allocated size of the Paned is at
1968 * least as big as it needs to be for the position to make sense.
1970 * 2) in recent versions of Ardour, save the position as a fraction,
1971 * and restore it using that fraction.
1973 * So, we will only call ::set_position() AFTER the Paned is of a
1974 * sensible size, and then in addition, we will set the position in a
1975 * way that will be maintained as/when/if the Paned is resized.
1977 * Once we've called ::set_position() on a Paned, we don't do it
1981 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1987 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1990 pos = atof (prop->value());
1994 /* older versions of Ardour stored absolute position */
1995 if ((done[0] = (allocation.get_height() > pos))) {
1996 rhs_pane1.set_position (pos);
1999 if ((done[0] = (allocation.get_height() > 1.0/pos))) {
2000 paned_set_position_as_fraction (rhs_pane1, pos, true);
2005 if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
2011 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
2012 pos = 2 * height / 3;
2014 pos = atof (prop->value());
2018 /* older versions of Ardour stored absolute position */
2019 if ((done[1] = (allocation.get_height() > pos))) {
2020 rhs_pane2.set_position (pos);
2023 if ((done[1] = (allocation.get_height() > 1.0/pos))) {
2024 paned_set_position_as_fraction (rhs_pane2, pos, true);
2029 if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
2035 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
2036 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2038 pos = max (0.1, atof (prop->value ()));
2042 if ((done[2] = (allocation.get_width() > pos))) {
2043 list_hpane.set_position (pos);
2046 if ((done[2] = (allocation.get_width() > 1.0/pos))) {
2047 paned_set_position_as_fraction (list_hpane, pos, false);
2052 if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
2058 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
2059 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2061 pos = max (0.1, atof (prop->value ()));
2065 /* older versions of Ardour stored absolute position */
2066 if ((done[3] = (allocation.get_width() > pos))) {
2067 inner_pane.set_position (pos);
2070 if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2071 paned_set_position_as_fraction (inner_pane, pos, false);
2078 Mixer_UI::scroll_left ()
2080 if (!scroller.get_hscrollbar()) return;
2081 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2082 /* stupid GTK: can't rely on clamping across versions */
2083 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2087 Mixer_UI::scroll_right ()
2089 if (!scroller.get_hscrollbar()) return;
2090 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2091 /* stupid GTK: can't rely on clamping across versions */
2092 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2096 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2098 switch (ev->direction) {
2099 case GDK_SCROLL_LEFT:
2103 if (ev->state & Keyboard::TertiaryModifier) {
2109 case GDK_SCROLL_RIGHT:
2113 case GDK_SCROLL_DOWN:
2114 if (ev->state & Keyboard::TertiaryModifier) {
2126 Mixer_UI::parameter_changed (string const & p)
2128 if (p == "show-group-tabs") {
2129 bool const s = _session->config.get_show_group_tabs ();
2131 _group_tabs->show ();
2133 _group_tabs->hide ();
2135 } else if (p == "default-narrow_ms") {
2136 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2137 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2138 (*i)->set_width_enum (s ? Narrow : Wide, this);
2140 } else if (p == "use-monitor-bus") {
2141 if (_session && !_session->monitor_out()) {
2142 monitor_section_detached ();
2148 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2150 g->set_active (a, this);
2154 Mixer_UI::plugin_selector()
2156 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2157 if (!_plugin_selector)
2158 _plugin_selector = new PluginSelector (PluginManager::instance());
2161 return _plugin_selector;
2165 Mixer_UI::setup_track_display ()
2167 track_model = ListStore::create (track_columns);
2168 track_display.set_model (track_model);
2169 track_display.append_column (_("Strips"), track_columns.text);
2170 track_display.append_column (_("Show"), track_columns.visible);
2171 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2172 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2173 track_display.get_column (0)->set_expand(true);
2174 track_display.get_column (1)->set_expand(false);
2175 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2176 track_display.set_name (X_("EditGroupList"));
2177 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2178 track_display.set_reorderable (true);
2179 track_display.set_headers_visible (true);
2180 track_display.set_can_focus(false);
2182 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2183 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2185 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2186 track_list_visible_cell->property_activatable() = true;
2187 track_list_visible_cell->property_radio() = false;
2188 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2190 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2192 track_display_scroller.add (track_display);
2193 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2195 VBox* v = manage (new VBox);
2197 v->pack_start (track_display_scroller, true, true);
2199 Button* b = manage (new Button);
2201 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2205 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2207 v->pack_start (*b, false, false);
2209 track_display_frame.set_name("BaseFrame");
2210 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2211 track_display_frame.add (*v);
2213 track_display_scroller.show();
2214 track_display_frame.show();
2215 track_display.show();
2219 Mixer_UI::new_track_or_bus ()
2221 ARDOUR_UI::instance()->add_route ();
2225 Mixer_UI::update_title ()
2227 if (!own_window()) {
2234 if (_session->snap_name() != _session->name()) {
2235 n = _session->snap_name ();
2237 n = _session->name ();
2240 if (_session->dirty ()) {
2244 WindowTitle title (n);
2245 title += S_("Window|Mixer");
2246 title += Glib::get_application_name ();
2247 own_window()->set_title (title.get_string());
2251 WindowTitle title (S_("Window|Mixer"));
2252 title += Glib::get_application_name ();
2253 own_window()->set_title (title.get_string());
2258 Mixer_UI::strip_by_x (int x)
2260 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2263 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2264 x2 = x1 + (*i)->get_width();
2266 if (x >= x1 && x <= x2) {
2275 Mixer_UI::set_route_targets_for_operation ()
2277 _route_targets.clear ();
2279 if (!_selection.empty()) {
2280 _route_targets = _selection.routes;
2284 // removed "implicit" selections of strips, after discussion on IRC
2289 Mixer_UI::monitor_section_going_away ()
2291 if (_monitor_section) {
2292 monitor_section_detached ();
2293 out_packer.remove (_monitor_section->tearoff());
2294 _monitor_section->set_session (0);
2295 delete _monitor_section;
2296 _monitor_section = 0;
2301 Mixer_UI::toggle_midi_input_active (bool flip_others)
2303 boost::shared_ptr<RouteList> rl (new RouteList);
2306 set_route_targets_for_operation ();
2308 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2309 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2312 rl->push_back ((*r)->route());
2313 onoff = !mt->input_active();
2317 _session->set_exclusive_input_active (rl, onoff, flip_others);
2321 Mixer_UI::maximise_mixer_space ()
2323 if (!own_window()) {
2331 _window->fullscreen ();
2336 Mixer_UI::restore_mixer_space ()
2338 if (!own_window()) {
2346 own_window()->unfullscreen();
2351 Mixer_UI::monitor_section_attached ()
2353 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2354 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2355 act->set_sensitive (true);
2356 tact->set_active ();
2360 Mixer_UI::monitor_section_detached ()
2362 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2363 act->set_sensitive (false);
2367 Mixer_UI::store_current_favorite_order ()
2369 typedef Gtk::TreeModel::Children type_children;
2370 type_children children = favorite_plugins_model->children();
2371 favorite_order.clear();
2372 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2374 Gtk::TreeModel::Row row = *iter;
2375 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2376 favorite_order.push_back (ppp->_pip);
2377 std::string name = row[favorite_plugins_columns.name];
2378 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2383 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2385 Gtk::TreeModel::Row row = *iter;
2386 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2388 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2392 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2394 PluginManager& manager (PluginManager::instance());
2395 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2396 if (manager.get_status (*i) != PluginManager::Favorite) {
2399 result.push_back (*i);
2403 struct PluginCustomSorter {
2405 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2406 PluginInfoList::const_iterator aiter = _user.begin();
2407 PluginInfoList::const_iterator biter = _user.begin();
2408 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2409 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2411 if (aiter != _user.end() && biter != _user.end()) {
2412 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2414 if (aiter != _user.end()) {
2417 if (biter != _user.end()) {
2420 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2422 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2424 PluginInfoList _user;
2428 Mixer_UI::refill_favorite_plugins ()
2430 PluginInfoList plugs;
2431 PluginManager& mgr (PluginManager::instance());
2434 refiller (plugs, mgr.lv2_plugin_info ());
2436 #ifdef WINDOWS_VST_SUPPORT
2437 refiller (plugs, mgr.windows_vst_plugin_info ());
2439 #ifdef LXVST_SUPPORT
2440 refiller (plugs, mgr.lxvst_plugin_info ());
2442 #ifdef AUDIOUNIT_SUPPORT
2443 refiller (plugs, mgr.au_plugin_info ());
2445 refiller (plugs, mgr.ladspa_plugin_info ());
2446 refiller (plugs, mgr.lua_plugin_info ());
2448 store_current_favorite_order ();
2450 PluginCustomSorter cmp (favorite_order);
2453 favorite_order = plugs;
2455 sync_treeview_from_favorite_order ();
2459 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2462 if (!(iter = favorite_plugins_model->get_iter (path))) {
2465 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2469 PluginInfoPtr pip = ppp->_pip;
2470 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2471 if (favorite_ui_state[pip->unique_id]) {
2472 favorite_plugins_display.expand_row (path, true);
2478 Mixer_UI::sync_treeview_from_favorite_order ()
2480 favorite_plugins_model->clear ();
2481 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2482 PluginInfoPtr pip = (*i);
2484 TreeModel::Row newrow = *(favorite_plugins_model->append());
2485 newrow[favorite_plugins_columns.name] = (*i)->name;
2486 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2491 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2492 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2493 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2494 child_row[favorite_plugins_columns.name] = (*j).label;
2495 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2497 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2498 if (favorite_ui_state[pip->unique_id]) {
2499 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2506 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2508 using namespace Gtk::Menu_Helpers;
2510 Gtk::Menu* m = manage (new Menu);
2511 MenuList& items = m->items ();
2513 if (_selection.routes.empty()) {
2514 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2516 items.push_back (MenuElem (_("Add at the top"),
2517 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2518 items.push_back (MenuElem (_("Add Pre-Fader"),
2519 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2520 items.push_back (MenuElem (_("Add Post-Fader"),
2521 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2522 items.push_back (MenuElem (_("Add at the end"),
2523 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2526 items.push_back (SeparatorElem());
2528 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2530 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2531 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2532 // we cannot currently delete AU presets
2533 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2534 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2538 m->popup (ev->button, ev->time);
2542 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2544 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2545 TreeModel::Path path;
2546 TreeViewColumn* column;
2548 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2549 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2551 selection->unselect_all();
2552 selection->select(path);
2555 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2557 popup_note_context_menu (ev);
2565 Mixer_UI::selected_plugin ()
2567 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2569 return PluginPresetPtr();
2571 Gtk::TreeModel::iterator iter = selection->get_selected();
2573 return PluginPresetPtr();
2575 return (*iter)[favorite_plugins_columns.plugin];
2579 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2581 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2583 add_favorite_processor (ppp, pos);
2588 Mixer_UI::delete_selected_preset ()
2593 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2594 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2597 PluginPtr plugin = ppp->_pip->load (*_session);
2598 plugin->get_presets();
2599 plugin->remove_preset (ppp->_preset.label);
2603 Mixer_UI::remove_selected_from_favorites ()
2605 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2609 PluginManager::PluginStatusType status = PluginManager::Normal;
2610 PluginManager& manager (PluginManager::instance());
2612 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2613 manager.save_statuses ();
2617 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2620 if (!(iter = favorite_plugins_model->get_iter (path))) {
2623 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2624 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2628 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2630 if (!_session || _selection.routes.empty()) {
2634 PluginInfoPtr pip = ppp->_pip;
2635 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2636 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2637 if (!rt) { continue; }
2639 PluginPtr p = pip->load (*_session);
2640 if (!p) { continue; }
2642 if (ppp->_preset.valid) {
2643 p->load_preset (ppp->_preset);
2646 Route::ProcessorStreams err;
2647 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2651 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2654 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2661 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2665 if (!np->display_to_user()) {
2668 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2669 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2674 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2678 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2685 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2687 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2691 // only allow to re-order top-level items
2693 if (TreePath::get_from_selection_data (data, src)) {
2694 if (src.up() && src.up()) {
2699 // don't allow to drop as child-rows.
2700 Gtk::TreeModel::Path _dest = dest; // un const
2701 const bool is_child = _dest.up (); // explicit bool for clang
2702 if (!is_child || _dest.empty ()) {
2709 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2711 if (data.get_target() != "PluginPresetPtr") {
2714 if (data.get_length() != sizeof (PluginPresetPtr)) {
2717 const void *d = data.get_data();
2718 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2720 PluginManager::PluginStatusType status = PluginManager::Favorite;
2721 PluginManager& manager (PluginManager::instance());
2723 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2724 manager.save_statuses ();
2728 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2730 /* call protected MixerActor:: method */
2735 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2737 /* call protected MixerActor:: method */
2742 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2744 boost::shared_ptr<VCA> v = spilled_vca.lock();
2747 show_vca_change (vca); /* EMIT SIGNAL */
2748 redisplay_track_list ();
2753 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2755 return vca == spilled_vca.lock();