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());
313 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
315 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
316 _plugin_selector = new PluginSelector (PluginManager::instance ());
318 #error implement deferred Plugin-Favorite list
320 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
321 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
322 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
325 Mixer_UI::~Mixer_UI ()
327 if (_monitor_section) {
328 monitor_section_detached ();
329 delete _monitor_section;
331 delete _plugin_selector;
341 Mixer_UI::track_editor_selection ()
343 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
347 Mixer_UI::use_own_window (bool and_fill_it)
349 bool new_window = !own_window();
351 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
354 if (win && new_window) {
355 win->set_name ("MixerWindow");
356 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
357 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
358 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
359 win->set_data ("ardour-bindings", bindings);
367 Mixer_UI::show_window ()
369 Tabbable::show_window ();
371 /* show/hide group tabs as required */
372 parameter_changed ("show-group-tabs");
374 /* now reset each strips width so the right widgets are shown */
377 TreeModel::Children rows = track_model->children();
378 TreeModel::Children::iterator ri;
380 for (ri = rows.begin(); ri != rows.end(); ++ri) {
381 ms = (*ri)[track_columns.strip];
385 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
386 /* Fix visibility of mixer strip stuff */
387 ms->parameter_changed (X_("mixer-element-visibility"));
390 /* force focus into main area */
391 scroller_base.grab_focus ();
395 Mixer_UI::add_masters (VCAList& vcas)
397 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
399 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
401 TreeModel::Row row = *(track_model->append());
402 row[track_columns.text] = (*v)->name();
403 row[track_columns.visible] = true;
404 row[track_columns.vca] = vms;
406 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
409 redisplay_track_list ();
413 Mixer_UI::remove_master (VCAMasterStrip* vms)
415 if (_session && _session->deletion_in_progress()) {
416 /* its all being taken care of */
420 TreeModel::Children rows = track_model->children();
421 TreeModel::Children::iterator ri;
423 for (ri = rows.begin(); ri != rows.end(); ++ri) {
424 if ((*ri)[track_columns.vca] == vms) {
425 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
426 track_model->erase (ri);
433 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
435 using namespace Menu_Helpers;
437 if (Keyboard::is_context_menu_event (ev)) {
438 ARDOUR_UI::instance()->add_route ();
446 Mixer_UI::add_strips (RouteList& routes)
448 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
449 uint32_t nroutes = 0;
451 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
452 boost::shared_ptr<Route> r = (*it)[track_columns.route];
460 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
467 _selection.clear_routes ();
473 no_track_list_redisplay = true;
474 track_display.set_model (Glib::RefPtr<ListStore>());
476 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
477 boost::shared_ptr<Route> route = (*x);
479 if (route->is_auditioner()) {
483 if (route->is_monitor()) {
485 if (!_monitor_section) {
486 _monitor_section = new MonitorSection (_session);
488 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
490 _monitor_section->tearoff().set_state (*mnode);
494 out_packer.pack_end (_monitor_section->tearoff(), false, false);
495 _monitor_section->set_session (_session);
496 _monitor_section->tearoff().show_all ();
498 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
499 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
501 monitor_section_attached ();
503 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
505 /* no regular strip shown for control out */
510 strip = new MixerStrip (*this, _session, route);
511 strips.push_back (strip);
513 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
515 if (strip->width_owner() != strip) {
516 strip->set_width_enum (_strip_width, this);
521 TreeModel::Row row = *(track_model->insert(insert_iter));
522 row[track_columns.text] = route->name();
523 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
524 row[track_columns.route] = route;
525 row[track_columns.strip] = strip;
526 row[track_columns.vca] = 0;
529 _selection.add (strip);
532 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
534 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
535 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
538 } catch (const std::exception& e) {
539 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
542 no_track_list_redisplay = false;
543 track_display.set_model (track_model);
545 sync_presentation_info_from_treeview ();
546 redisplay_track_list ();
550 Mixer_UI::deselect_all_strip_processors ()
552 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
553 (*i)->deselect_all_processors();
558 Mixer_UI::select_strip (MixerStrip& ms, bool add)
561 _selection.add (&ms);
563 _selection.set (&ms);
568 Mixer_UI::select_none ()
570 _selection.clear_routes();
571 deselect_all_strip_processors();
575 Mixer_UI::delete_processors ()
577 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
578 (*i)->delete_processors();
584 Mixer_UI::remove_strip (MixerStrip* strip)
586 if (_session && _session->deletion_in_progress()) {
587 /* its all being taken care of */
591 TreeModel::Children rows = track_model->children();
592 TreeModel::Children::iterator ri;
593 list<MixerStrip *>::iterator i;
595 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
599 for (ri = rows.begin(); ri != rows.end(); ++ri) {
600 if ((*ri)[track_columns.strip] == strip) {
601 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
602 track_model->erase (ri);
609 Mixer_UI::sync_presentation_info_from_treeview ()
611 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
615 TreeModel::Children rows = track_model->children();
621 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
623 TreeModel::Children::iterator ri;
627 for (ri = rows.begin(); ri != rows.end(); ++ri) {
628 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
629 bool visible = (*ri)[track_columns.visible];
636 if (route->presentation_info().special()) {
641 route->presentation_info().set_flag (PresentationInfo::Hidden);
643 route->presentation_info().unset_flag (PresentationInfo::Hidden);
646 if (order != route->presentation_info().group_order()) {
647 route->set_presentation_group_order_explicit (order);
655 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
656 _session->notify_presentation_info_change ();
661 Mixer_UI::sync_treeview_from_presentation_info ()
663 if (!_session || _session->deletion_in_progress()) {
667 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
669 /* we could get here after either a change in the Mixer or Editor sort
670 * order, but either way, the mixer order keys reflect the intended
671 * order for the GUI, so reorder the treeview model to match it.
674 vector<int> neworder;
675 TreeModel::Children rows = track_model->children();
676 uint32_t old_order = 0;
677 bool changed = false;
684 uint32_t vca_cnt = 0;
685 uint32_t max_route_order_key = 0;
687 /* count number of Routes in track_model (there may be some odd reason
688 why this is not the same as the number in the session, but here we
689 care about the track model.
692 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
693 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
695 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
699 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
700 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
702 /* VCAs need to sort after all routes. We don't display
703 * them in the same place (March 2016), but we don't
704 * want them intermixed in the track_model
706 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
708 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
712 SortByNewDisplayOrder cmp;
714 sort (sorted.begin(), sorted.end(), cmp);
715 neworder.assign (sorted.size(), 0);
719 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
721 neworder[n] = sr->old_display_order;
723 if (sr->old_display_order != n) {
729 Unwinder<bool> uw (ignore_reorder, true);
730 track_model->reorder (neworder);
733 redisplay_track_list ();
737 Mixer_UI::follow_editor_selection ()
739 if (_following_editor_selection) {
743 _following_editor_selection = true;
744 _selection.block_routes_changed (true);
746 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
748 _selection.clear_routes ();
750 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
751 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
753 MixerStrip* ms = strip_by_route (rtav->route());
760 _following_editor_selection = false;
761 _selection.block_routes_changed (false);
766 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
768 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
769 if ((*i)->route() == r) {
778 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
780 if (ev->button == 1) {
781 if (_selection.selected (strip)) {
782 /* primary-click: toggle selection state of strip */
783 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
784 _selection.remove (strip);
785 } else if (_selection.routes.size() > 1) {
786 /* de-select others */
787 _selection.set (strip);
790 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
791 _selection.add (strip);
792 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
794 if (!_selection.selected(strip)) {
796 /* extend selection */
798 vector<MixerStrip*> tmp;
799 bool accumulate = false;
800 bool found_another = false;
802 tmp.push_back (strip);
804 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
806 /* hit clicked strip, start accumulating till we hit the first
815 } else if (_selection.selected (*i)) {
816 /* hit selected strip. if currently accumulating others,
817 we're done. if not accumulating others, start doing so.
819 found_another = true;
834 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
838 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
842 _selection.set (strip);
851 Mixer_UI::set_session (Session* sess)
853 SessionHandlePtr::set_session (sess);
855 if (_plugin_selector) {
856 _plugin_selector->set_session (_session);
859 _group_tabs->set_session (sess);
865 refill_favorite_plugins();
867 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
868 set_state (*node, 0);
872 initial_track_display ();
874 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
875 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
876 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
877 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
878 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
879 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
880 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
882 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
884 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
886 route_groups_changed ();
895 Mixer_UI::session_going_away ()
897 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
899 _in_group_rebuild_or_clear = true;
900 group_model->clear ();
901 _in_group_rebuild_or_clear = false;
904 track_model->clear ();
906 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
910 if (_monitor_section) {
911 _monitor_section->tearoff().hide_visible ();
914 monitor_section_detached ();
920 SessionHandlePtr::session_going_away ();
927 Mixer_UI::track_visibility_changed (std::string const & path)
929 if (_session && _session->deletion_in_progress()) {
935 if ((iter = track_model->get_iter (path))) {
936 MixerStrip* strip = (*iter)[track_columns.strip];
938 bool visible = (*iter)[track_columns.visible];
940 if (strip->set_marked_for_display (!visible)) {
941 update_track_visibility ();
948 Mixer_UI::update_track_visibility ()
950 TreeModel::Children rows = track_model->children();
951 TreeModel::Children::iterator i;
954 Unwinder<bool> uw (no_track_list_redisplay, true);
956 for (i = rows.begin(); i != rows.end(); ++i) {
957 MixerStrip *strip = (*i)[track_columns.strip];
959 (*i)[track_columns.visible] = strip->marked_for_display ();
963 /* force presentation catch up with visibility changes
966 sync_presentation_info_from_treeview ();
969 redisplay_track_list ();
973 Mixer_UI::show_strip (MixerStrip* ms)
975 TreeModel::Children rows = track_model->children();
976 TreeModel::Children::iterator i;
978 for (i = rows.begin(); i != rows.end(); ++i) {
980 MixerStrip* strip = (*i)[track_columns.strip];
982 (*i)[track_columns.visible] = true;
983 redisplay_track_list ();
990 Mixer_UI::hide_strip (MixerStrip* ms)
992 TreeModel::Children rows = track_model->children();
993 TreeModel::Children::iterator i;
995 for (i = rows.begin(); i != rows.end(); ++i) {
997 MixerStrip* strip = (*i)[track_columns.strip];
999 (*i)[track_columns.visible] = false;
1000 redisplay_track_list ();
1007 Mixer_UI::start_updating ()
1009 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1014 Mixer_UI::stop_updating ()
1016 fast_screen_update_connection.disconnect();
1021 Mixer_UI::fast_update_strips ()
1023 if (_content.is_mapped () && _session) {
1024 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1025 (*i)->fast_update ();
1031 Mixer_UI::set_all_strips_visibility (bool yn)
1033 TreeModel::Children rows = track_model->children();
1034 TreeModel::Children::iterator i;
1037 Unwinder<bool> uw (no_track_list_redisplay, true);
1039 for (i = rows.begin(); i != rows.end(); ++i) {
1041 TreeModel::Row row = (*i);
1042 MixerStrip* strip = row[track_columns.strip];
1048 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1052 (*i)[track_columns.visible] = yn;
1056 redisplay_track_list ();
1061 Mixer_UI::set_all_audio_midi_visibility (int tracks, 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) {
1070 TreeModel::Row row = (*i);
1071 MixerStrip* strip = row[track_columns.strip];
1077 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1081 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1082 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1086 (*i)[track_columns.visible] = yn;
1090 if (at) { /* track */
1091 (*i)[track_columns.visible] = yn;
1096 if (!at && !mt) { /* bus */
1097 (*i)[track_columns.visible] = yn;
1102 if (mt) { /* midi-track */
1103 (*i)[track_columns.visible] = yn;
1110 redisplay_track_list ();
1114 Mixer_UI::hide_all_routes ()
1116 set_all_strips_visibility (false);
1120 Mixer_UI::show_all_routes ()
1122 set_all_strips_visibility (true);
1126 Mixer_UI::show_all_audiobus ()
1128 set_all_audio_midi_visibility (2, true);
1131 Mixer_UI::hide_all_audiobus ()
1133 set_all_audio_midi_visibility (2, false);
1137 Mixer_UI::show_all_audiotracks()
1139 set_all_audio_midi_visibility (1, true);
1142 Mixer_UI::hide_all_audiotracks ()
1144 set_all_audio_midi_visibility (1, false);
1148 Mixer_UI::show_all_miditracks()
1150 set_all_audio_midi_visibility (3, true);
1153 Mixer_UI::hide_all_miditracks ()
1155 set_all_audio_midi_visibility (3, false);
1160 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1162 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1163 sync_presentation_info_from_treeview ();
1167 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1169 /* this happens as the second step of a DnD within the treeview as well
1170 as when a row/route is actually deleted.
1172 if it was a deletion then we have to force a redisplay because
1173 order keys may not have changed.
1176 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1177 sync_presentation_info_from_treeview ();
1179 if (_route_deletion_in_progress) {
1180 redisplay_track_list ();
1185 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1187 TreeModel::Children rows = track_model->children();
1189 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1191 MixerStrip* strip = (*i)[track_columns.strip];
1194 /* we're in the middle of changing a row, don't worry */
1198 if (!strip->route()) {
1199 /* non-route element */
1203 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1207 if (strip->route()->slaved_to (vca)) {
1209 strip->set_gui_property ("visible", true);
1211 if (strip->packed()) {
1212 strip_packer.reorder_child (*strip, -1); /* put at end */
1214 strip_packer.pack_start (*strip, false, false);
1215 strip->set_packed (true);
1220 strip->set_gui_property ("visible", false);
1222 if (strip->packed()) {
1223 strip_packer.remove (*strip);
1224 strip->set_packed (false);
1231 Mixer_UI::redisplay_track_list ()
1233 if (no_track_list_redisplay) {
1237 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1240 spill_redisplay (sv);
1244 TreeModel::Children rows = track_model->children();
1245 TreeModel::Children::iterator i;
1246 uint32_t n_masters = 0;
1248 container_clear (vca_packer);
1249 vca_packer.pack_end (vca_scroller_base, true, true);
1251 for (i = rows.begin(); i != rows.end(); ++i) {
1253 VCAMasterStrip* vms = (*i)[track_columns.vca];
1256 vca_packer.pack_start (*vms, false, false);
1262 MixerStrip* strip = (*i)[track_columns.strip];
1265 /* we're in the middle of changing a row, don't worry */
1269 bool const visible = (*i)[track_columns.visible];
1272 strip->set_gui_property ("visible", true);
1274 if (strip->packed()) {
1276 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1277 out_packer.reorder_child (*strip, -1);
1280 strip_packer.reorder_child (*strip, -1); /* put at end */
1285 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1286 out_packer.pack_start (*strip, false, false);
1288 strip_packer.pack_start (*strip, false, false);
1290 strip->set_packed (true);
1295 strip->set_gui_property ("visible", false);
1297 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1298 /* do nothing, these cannot be hidden */
1300 if (strip->packed()) {
1301 strip_packer.remove (*strip);
1302 strip->set_packed (false);
1308 /* update visibility of VCA assign buttons */
1310 if (n_masters == 0) {
1311 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1312 vca_scroller.hide ();
1314 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1315 vca_scroller.show ();
1318 _group_tabs->set_dirty ();
1322 Mixer_UI::strip_width_changed ()
1324 _group_tabs->set_dirty ();
1327 TreeModel::Children rows = track_model->children();
1328 TreeModel::Children::iterator i;
1331 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1332 MixerStrip* strip = (*i)[track_columns.strip];
1338 bool visible = (*i)[track_columns.visible];
1341 strip->queue_draw();
1348 struct PresentationInfoRouteSorter
1350 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1351 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1356 Mixer_UI::initial_track_display ()
1358 boost::shared_ptr<RouteList> routes = _session->get_routes();
1359 RouteList copy (*routes);
1360 PresentationInfoRouteSorter sorter;
1365 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1366 Unwinder<bool> uw2 (ignore_reorder, true);
1368 track_model->clear ();
1369 VCAList vcas = _session->vca_manager().vcas();
1374 redisplay_track_list ();
1378 Mixer_UI::show_track_list_menu ()
1380 if (track_menu == 0) {
1381 build_track_menu ();
1384 track_menu->popup (1, gtk_get_current_event_time());
1388 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1390 if (Keyboard::is_context_menu_event (ev)) {
1391 show_track_list_menu ();
1399 Mixer_UI::build_track_menu ()
1401 using namespace Menu_Helpers;
1402 using namespace Gtk;
1404 track_menu = new Menu;
1405 track_menu->set_name ("ArdourContextMenu");
1406 MenuList& items = track_menu->items();
1408 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1409 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1410 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1411 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1412 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1413 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1414 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1415 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1420 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1422 if (!what_changed.contains (ARDOUR::Properties::name)) {
1426 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1428 TreeModel::Children rows = track_model->children();
1429 TreeModel::Children::iterator i;
1431 for (i = rows.begin(); i != rows.end(); ++i) {
1432 if ((*i)[track_columns.strip] == mx) {
1433 (*i)[track_columns.text] = mx->route()->name();
1438 error << _("track display list item for renamed strip not found!") << endmsg;
1442 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1444 TreeModel::Path path;
1445 TreeViewColumn* column;
1449 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1450 _group_tabs->get_menu(0)->popup (1, ev->time);
1454 TreeIter iter = group_model->get_iter (path);
1456 _group_tabs->get_menu(0)->popup (1, ev->time);
1460 RouteGroup* group = (*iter)[group_columns.group];
1462 if (Keyboard::is_context_menu_event (ev)) {
1463 _group_tabs->get_menu(group)->popup (1, ev->time);
1467 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1469 if (Keyboard::is_edit_event (ev)) {
1471 // edit_route_group (group);
1473 group_display.queue_draw();
1482 bool visible = (*iter)[group_columns.visible];
1483 (*iter)[group_columns.visible] = !visible;
1485 group_display.queue_draw();
1498 Mixer_UI::activate_all_route_groups ()
1500 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1504 Mixer_UI::disable_all_route_groups ()
1506 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1510 Mixer_UI::route_groups_changed ()
1512 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1514 _in_group_rebuild_or_clear = true;
1516 /* just rebuild the while thing */
1518 group_model->clear ();
1521 /* this is currently not used,
1522 * Mixer_UI::group_display_button_press() has a case for it,
1523 * and a commented edit_route_group() but that's n/a since 2011.
1525 * This code is left as reminder that
1526 * row[group_columns.group] = 0 has special meaning.
1530 row = *(group_model->append());
1531 row[group_columns.visible] = true;
1532 row[group_columns.text] = (_("-all-"));
1533 row[group_columns.group] = 0;
1537 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1539 _group_tabs->set_dirty ();
1540 _in_group_rebuild_or_clear = false;
1544 Mixer_UI::new_route_group ()
1548 _group_tabs->run_new_group_dialog (rl, false);
1552 Mixer_UI::remove_selected_route_group ()
1554 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1555 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1561 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1564 /* selection mode is single, so rows.begin() is it */
1566 if ((iter = group_model->get_iter (*i))) {
1568 RouteGroup* rg = (*iter)[group_columns.group];
1571 _session->remove_route_group (*rg);
1577 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1579 if (in_group_row_change) {
1583 /* force an update of any mixer strips that are using this group,
1584 otherwise mix group names don't change in mixer strips
1587 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1588 if ((*i)->route_group() == group) {
1589 (*i)->route_group_changed();
1593 TreeModel::iterator i;
1594 TreeModel::Children rows = group_model->children();
1595 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1597 in_group_row_change = true;
1599 for (i = rows.begin(); i != rows.end(); ++i) {
1600 if ((*i)[group_columns.group] == group) {
1601 (*i)[group_columns.visible] = !group->is_hidden ();
1602 (*i)[group_columns.text] = group->name ();
1607 in_group_row_change = false;
1609 if (change.contains (Properties::name)) {
1610 _group_tabs->set_dirty ();
1613 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1614 if ((*j)->route_group() == group) {
1615 if (group->is_hidden ()) {
1625 Mixer_UI::show_mixer_list (bool yn)
1628 list_vpacker.show ();
1630 //if user wants to show the pane, we should make sure that it is wide enough to be visible
1631 int width = list_hpane.get_position();
1633 list_hpane.set_position(40);
1636 list_vpacker.hide ();
1639 _show_mixer_list = yn;
1643 Mixer_UI::show_monitor_section (bool yn)
1645 if (!monitor_section()) {
1648 if (monitor_section()->tearoff().torn_off()) {
1653 monitor_section()->tearoff().show();
1655 monitor_section()->tearoff().hide();
1660 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1665 if ((iter = group_model->get_iter (path))) {
1667 if ((group = (*iter)[group_columns.group]) == 0) {
1671 if (new_text != group->name()) {
1672 group->set_name (new_text);
1678 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1682 if (in_group_row_change) {
1686 if ((group = (*iter)[group_columns.group]) == 0) {
1690 std::string name = (*iter)[group_columns.text];
1692 if (name != group->name()) {
1693 group->set_name (name);
1696 bool hidden = !(*iter)[group_columns.visible];
1698 if (hidden != group->is_hidden ()) {
1699 group->set_hidden (hidden, this);
1703 /** Called when a group model row is deleted, but also when the model is
1704 * reordered by a user drag-and-drop; the latter is what we are
1705 * interested in here.
1708 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1710 if (_in_group_rebuild_or_clear) {
1714 /* Re-write the session's route group list so that the new order is preserved */
1716 list<RouteGroup*> new_list;
1718 Gtk::TreeModel::Children children = group_model->children();
1719 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1720 RouteGroup* g = (*i)[group_columns.group];
1722 new_list.push_back (g);
1726 _session->reorder_route_groups (new_list);
1731 Mixer_UI::add_route_group (RouteGroup* group)
1733 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1736 in_group_row_change = true;
1738 TreeModel::Row row = *(group_model->append());
1739 row[group_columns.visible] = !group->is_hidden ();
1740 row[group_columns.group] = group;
1741 if (!group->name().empty()) {
1742 row[group_columns.text] = group->name();
1744 row[group_columns.text] = _("unnamed");
1748 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1751 TreeViewColumn* col = group_display.get_column (0);
1752 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1753 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1756 _group_tabs->set_dirty ();
1758 in_group_row_change = false;
1762 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1764 using namespace Menu_Helpers;
1766 if (Keyboard::is_context_menu_event (ev)) {
1767 ARDOUR_UI::instance()->add_route ();
1775 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1777 printf ("Mixer_UI::scroller_drag_data_received\n");
1778 if (data.get_target() != "PluginFavoritePtr") {
1779 context->drag_finish (false, false, time);
1783 const void * d = data.get_data();
1784 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1786 PluginPresetList nfos;
1788 tv->get_object_drag_data (nfos, &source);
1790 Route::ProcessorList pl;
1793 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1794 PluginPresetPtr ppp = (*i);
1795 PluginInfoPtr pip = ppp->_pip;
1796 if (!pip->is_instrument ()) {
1799 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);
1803 context->drag_finish (ok, false, time);
1807 Mixer_UI::set_strip_width (Width w, bool save)
1811 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1812 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1817 struct PluginStateSorter {
1819 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1820 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1821 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1822 if (aiter != _user.end() && biter != _user.end()) {
1823 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1825 if (aiter != _user.end()) {
1828 if (biter != _user.end()) {
1831 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1834 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1836 std::list<std::string> _user;
1840 Mixer_UI::set_state (const XMLNode& node, int version)
1842 XMLProperty const * prop;
1844 Tabbable::set_state (node, version);
1846 if ((prop = node.property ("narrow-strips"))) {
1847 if (string_is_affirmative (prop->value())) {
1848 set_strip_width (Narrow);
1850 set_strip_width (Wide);
1854 if ((prop = node.property ("show-mixer"))) {
1855 if (string_is_affirmative (prop->value())) {
1860 if ((prop = node.property ("maximised"))) {
1861 bool yn = string_is_affirmative (prop->value());
1862 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1864 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1865 bool fs = tact && tact->get_active();
1867 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1871 if ((prop = node.property ("show-mixer-list"))) {
1872 bool yn = string_is_affirmative (prop->value());
1873 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1875 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1877 /* do it twice to force the change */
1878 tact->set_active (!yn);
1879 tact->set_active (yn);
1883 XMLNode* plugin_order;
1884 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1885 store_current_favorite_order ();
1886 std::list<string> order;
1887 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1888 XMLNodeConstIterator i;
1889 for (i = kids.begin(); i != kids.end(); ++i) {
1890 if ((prop = (*i)->property ("unique-id"))) {
1891 std::string unique_id = prop->value();
1892 order.push_back (unique_id);
1893 if ((prop = (*i)->property ("expanded"))) {
1894 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1898 PluginStateSorter cmp (order);
1899 favorite_order.sort (cmp);
1900 sync_treeview_from_favorite_order ();
1906 Mixer_UI::get_state ()
1908 XMLNode* node = new XMLNode (X_("Mixer"));
1911 node->add_child_nocopy (Tabbable::get_state());
1913 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane1, true));
1914 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1915 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane2, true));
1916 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1917 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (list_hpane, false));
1918 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1919 snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (inner_pane, false));
1920 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1922 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1923 node->add_property ("show-mixer", _visible ? "yes" : "no");
1924 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1925 node->add_property ("maximised", _maximised ? "yes" : "no");
1927 store_current_favorite_order ();
1928 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1930 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1931 XMLNode* p = new XMLNode ("PluginInfo");
1932 p->add_property ("sort", cnt);
1933 p->add_property ("unique-id", (*i)->unique_id);
1934 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1935 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1937 plugin_order->add_child_nocopy (*p);
1939 node->add_child_nocopy (*plugin_order);
1945 Mixer_UI::pane_allocation_handler (Allocation& allocation, Gtk::Paned* which)
1948 XMLProperty* prop = 0;
1949 XMLNode* geometry = ARDOUR_UI::instance()->mixer_settings();
1950 int height = default_height;
1951 static bool done[4] = { false, false, false, false };
1953 /* Gtk::Paned behaves very oddly and rather undesirably. Setting the
1954 * position is a crapshoot if you time it incorrectly with the overall
1955 * sizing of the Paned. For example, if you might retrieve the size with
1956 * ::get_position() and then later call ::set_position() on a Paned at
1957 * a time when its allocation is different than it was when you retrieved
1958 * the position. The position will be interpreted as the size of the
1959 * first (top or left) child widget. If packing/size allocation later
1960 * resizes the Paned to a (final) smaller size, the position will be
1961 * used in ways that mean that the Paned ends up NOT in the same state
1962 * that it was in when you originally saved the position.
1964 * Concrete example: Paned is 800 pixels wide, position is 400
1965 * (halfway). Save position as 400. During program restart, set
1966 * position to 400, before Paned has been allocated any space. Paned
1967 * ends up initially sized to 1200 pixels. Position is now 1/3 of the
1968 * way across/down. Subsequent resizes will leave the position 1/3 of
1969 * the way across, rather than leaving it as a fixed pixel
1970 * position. Eventually, the Paned ends up 800 pixels wide/high again,
1971 * but the position is now 267, not 400.
1975 * We deal with this by using two strategies:
1977 * 1) only set the position if the allocated size of the Paned is at
1978 * least as big as it needs to be for the position to make sense.
1980 * 2) in recent versions of Ardour, save the position as a fraction,
1981 * and restore it using that fraction.
1983 * So, we will only call ::set_position() AFTER the Paned is of a
1984 * sensible size, and then in addition, we will set the position in a
1985 * way that will be maintained as/when/if the Paned is resized.
1987 * Once we've called ::set_position() on a Paned, we don't do it
1991 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1997 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
2000 pos = atof (prop->value());
2004 /* older versions of Ardour stored absolute position */
2005 if ((done[0] = (allocation.get_height() > pos))) {
2006 rhs_pane1.set_position (pos);
2009 if ((done[0] = (allocation.get_height() > 1.0/pos))) {
2010 paned_set_position_as_fraction (rhs_pane1, pos, true);
2015 if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
2021 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
2022 pos = 2 * height / 3;
2024 pos = atof (prop->value());
2028 /* older versions of Ardour stored absolute position */
2029 if ((done[1] = (allocation.get_height() > pos))) {
2030 rhs_pane2.set_position (pos);
2033 if ((done[1] = (allocation.get_height() > 1.0/pos))) {
2034 paned_set_position_as_fraction (rhs_pane2, pos, true);
2039 if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
2045 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
2046 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2048 pos = max (0.1, atof (prop->value ()));
2052 if ((done[2] = (allocation.get_width() > pos))) {
2053 list_hpane.set_position (pos);
2056 if ((done[2] = (allocation.get_width() > 1.0/pos))) {
2057 paned_set_position_as_fraction (list_hpane, pos, false);
2062 if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
2068 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
2069 pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2071 pos = max (0.1, atof (prop->value ()));
2075 /* older versions of Ardour stored absolute position */
2076 if ((done[3] = (allocation.get_width() > pos))) {
2077 inner_pane.set_position (pos);
2080 if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2081 paned_set_position_as_fraction (inner_pane, pos, false);
2088 Mixer_UI::scroll_left ()
2090 if (!scroller.get_hscrollbar()) return;
2091 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2092 /* stupid GTK: can't rely on clamping across versions */
2093 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2097 Mixer_UI::scroll_right ()
2099 if (!scroller.get_hscrollbar()) return;
2100 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2101 /* stupid GTK: can't rely on clamping across versions */
2102 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2106 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2108 switch (ev->direction) {
2109 case GDK_SCROLL_LEFT:
2113 if (ev->state & Keyboard::TertiaryModifier) {
2119 case GDK_SCROLL_RIGHT:
2123 case GDK_SCROLL_DOWN:
2124 if (ev->state & Keyboard::TertiaryModifier) {
2136 Mixer_UI::parameter_changed (string const & p)
2138 if (p == "show-group-tabs") {
2139 bool const s = _session->config.get_show_group_tabs ();
2141 _group_tabs->show ();
2143 _group_tabs->hide ();
2145 } else if (p == "default-narrow_ms") {
2146 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2147 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2148 (*i)->set_width_enum (s ? Narrow : Wide, this);
2150 } else if (p == "use-monitor-bus") {
2151 if (_session && !_session->monitor_out()) {
2152 monitor_section_detached ();
2158 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2160 g->set_active (a, this);
2164 Mixer_UI::plugin_selector()
2166 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2167 if (!_plugin_selector)
2168 _plugin_selector = new PluginSelector (PluginManager::instance());
2171 return _plugin_selector;
2175 Mixer_UI::setup_track_display ()
2177 track_model = ListStore::create (track_columns);
2178 track_display.set_model (track_model);
2179 track_display.append_column (_("Strips"), track_columns.text);
2180 track_display.append_column (_("Show"), track_columns.visible);
2181 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2182 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2183 track_display.get_column (0)->set_expand(true);
2184 track_display.get_column (1)->set_expand(false);
2185 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2186 track_display.set_name (X_("EditGroupList"));
2187 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2188 track_display.set_reorderable (true);
2189 track_display.set_headers_visible (true);
2190 track_display.set_can_focus(false);
2192 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2193 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2195 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2196 track_list_visible_cell->property_activatable() = true;
2197 track_list_visible_cell->property_radio() = false;
2198 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2200 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2202 track_display_scroller.add (track_display);
2203 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2205 VBox* v = manage (new VBox);
2207 v->pack_start (track_display_scroller, true, true);
2209 Button* b = manage (new Button);
2211 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2215 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2217 v->pack_start (*b, false, false);
2219 track_display_frame.set_name("BaseFrame");
2220 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2221 track_display_frame.add (*v);
2223 track_display_scroller.show();
2224 track_display_frame.show();
2225 track_display.show();
2229 Mixer_UI::new_track_or_bus ()
2231 ARDOUR_UI::instance()->add_route ();
2235 Mixer_UI::update_title ()
2237 if (!own_window()) {
2244 if (_session->snap_name() != _session->name()) {
2245 n = _session->snap_name ();
2247 n = _session->name ();
2250 if (_session->dirty ()) {
2254 WindowTitle title (n);
2255 title += S_("Window|Mixer");
2256 title += Glib::get_application_name ();
2257 own_window()->set_title (title.get_string());
2261 WindowTitle title (S_("Window|Mixer"));
2262 title += Glib::get_application_name ();
2263 own_window()->set_title (title.get_string());
2268 Mixer_UI::strip_by_x (int x)
2270 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2273 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2274 x2 = x1 + (*i)->get_width();
2276 if (x >= x1 && x <= x2) {
2285 Mixer_UI::set_route_targets_for_operation ()
2287 _route_targets.clear ();
2289 if (!_selection.empty()) {
2290 _route_targets = _selection.routes;
2294 // removed "implicit" selections of strips, after discussion on IRC
2299 Mixer_UI::monitor_section_going_away ()
2301 if (_monitor_section) {
2302 monitor_section_detached ();
2303 out_packer.remove (_monitor_section->tearoff());
2304 _monitor_section->set_session (0);
2305 delete _monitor_section;
2306 _monitor_section = 0;
2311 Mixer_UI::toggle_midi_input_active (bool flip_others)
2313 boost::shared_ptr<RouteList> rl (new RouteList);
2316 set_route_targets_for_operation ();
2318 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2319 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2322 rl->push_back ((*r)->route());
2323 onoff = !mt->input_active();
2327 _session->set_exclusive_input_active (rl, onoff, flip_others);
2331 Mixer_UI::maximise_mixer_space ()
2333 if (!own_window()) {
2341 _window->fullscreen ();
2346 Mixer_UI::restore_mixer_space ()
2348 if (!own_window()) {
2356 own_window()->unfullscreen();
2361 Mixer_UI::monitor_section_attached ()
2363 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2364 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2365 act->set_sensitive (true);
2366 tact->set_active ();
2370 Mixer_UI::monitor_section_detached ()
2372 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2373 act->set_sensitive (false);
2377 Mixer_UI::store_current_favorite_order ()
2379 typedef Gtk::TreeModel::Children type_children;
2380 type_children children = favorite_plugins_model->children();
2381 favorite_order.clear();
2382 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2384 Gtk::TreeModel::Row row = *iter;
2385 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2386 favorite_order.push_back (ppp->_pip);
2387 std::string name = row[favorite_plugins_columns.name];
2388 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2393 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2395 Gtk::TreeModel::Row row = *iter;
2396 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2398 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2402 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2404 PluginManager& manager (PluginManager::instance());
2405 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2406 if (manager.get_status (*i) != PluginManager::Favorite) {
2409 result.push_back (*i);
2413 struct PluginCustomSorter {
2415 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2416 PluginInfoList::const_iterator aiter = _user.begin();
2417 PluginInfoList::const_iterator biter = _user.begin();
2418 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2419 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2421 if (aiter != _user.end() && biter != _user.end()) {
2422 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2424 if (aiter != _user.end()) {
2427 if (biter != _user.end()) {
2430 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2432 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2434 PluginInfoList _user;
2438 Mixer_UI::refill_favorite_plugins ()
2440 PluginInfoList plugs;
2441 PluginManager& mgr (PluginManager::instance());
2444 refiller (plugs, mgr.lv2_plugin_info ());
2446 #ifdef WINDOWS_VST_SUPPORT
2447 refiller (plugs, mgr.windows_vst_plugin_info ());
2449 #ifdef LXVST_SUPPORT
2450 refiller (plugs, mgr.lxvst_plugin_info ());
2452 #ifdef AUDIOUNIT_SUPPORT
2453 refiller (plugs, mgr.au_plugin_info ());
2455 refiller (plugs, mgr.ladspa_plugin_info ());
2456 refiller (plugs, mgr.lua_plugin_info ());
2458 store_current_favorite_order ();
2460 PluginCustomSorter cmp (favorite_order);
2463 favorite_order = plugs;
2465 sync_treeview_from_favorite_order ();
2469 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2472 if (!(iter = favorite_plugins_model->get_iter (path))) {
2475 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2479 PluginInfoPtr pip = ppp->_pip;
2480 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2481 if (favorite_ui_state[pip->unique_id]) {
2482 favorite_plugins_display.expand_row (path, true);
2488 Mixer_UI::sync_treeview_from_favorite_order ()
2490 favorite_plugins_model->clear ();
2491 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2492 PluginInfoPtr pip = (*i);
2494 TreeModel::Row newrow = *(favorite_plugins_model->append());
2495 newrow[favorite_plugins_columns.name] = (*i)->name;
2496 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2501 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2502 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2503 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2504 child_row[favorite_plugins_columns.name] = (*j).label;
2505 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2507 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2508 if (favorite_ui_state[pip->unique_id]) {
2509 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2516 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2518 using namespace Gtk::Menu_Helpers;
2520 Gtk::Menu* m = manage (new Menu);
2521 MenuList& items = m->items ();
2523 if (_selection.routes.empty()) {
2524 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2526 items.push_back (MenuElem (_("Add at the top"),
2527 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2528 items.push_back (MenuElem (_("Add Pre-Fader"),
2529 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2530 items.push_back (MenuElem (_("Add Post-Fader"),
2531 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2532 items.push_back (MenuElem (_("Add at the end"),
2533 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2536 items.push_back (SeparatorElem());
2538 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2540 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2541 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2542 // we cannot currently delete AU presets
2543 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2544 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2548 m->popup (ev->button, ev->time);
2552 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2554 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2555 TreeModel::Path path;
2556 TreeViewColumn* column;
2558 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2559 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2561 selection->unselect_all();
2562 selection->select(path);
2565 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2567 popup_note_context_menu (ev);
2575 Mixer_UI::selected_plugin ()
2577 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2579 return PluginPresetPtr();
2581 Gtk::TreeModel::iterator iter = selection->get_selected();
2583 return PluginPresetPtr();
2585 return (*iter)[favorite_plugins_columns.plugin];
2589 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2591 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2593 add_favorite_processor (ppp, pos);
2598 Mixer_UI::delete_selected_preset ()
2603 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2604 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2607 PluginPtr plugin = ppp->_pip->load (*_session);
2608 plugin->get_presets();
2609 plugin->remove_preset (ppp->_preset.label);
2613 Mixer_UI::remove_selected_from_favorites ()
2615 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2619 PluginManager::PluginStatusType status = PluginManager::Normal;
2620 PluginManager& manager (PluginManager::instance());
2622 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2623 manager.save_statuses ();
2627 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2630 if (!(iter = favorite_plugins_model->get_iter (path))) {
2633 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2634 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2638 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2640 if (!_session || _selection.routes.empty()) {
2644 PluginInfoPtr pip = ppp->_pip;
2645 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2646 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2647 if (!rt) { continue; }
2649 PluginPtr p = pip->load (*_session);
2650 if (!p) { continue; }
2652 if (ppp->_preset.valid) {
2653 p->load_preset (ppp->_preset);
2656 Route::ProcessorStreams err;
2657 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2661 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2664 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2671 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2675 if (!np->display_to_user()) {
2678 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2679 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2684 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2688 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2695 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2697 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2701 // only allow to re-order top-level items
2703 if (TreePath::get_from_selection_data (data, src)) {
2704 if (src.up() && src.up()) {
2709 // don't allow to drop as child-rows.
2710 Gtk::TreeModel::Path _dest = dest; // un const
2711 const bool is_child = _dest.up (); // explicit bool for clang
2712 if (!is_child || _dest.empty ()) {
2719 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2721 if (data.get_target() != "PluginPresetPtr") {
2724 if (data.get_length() != sizeof (PluginPresetPtr)) {
2727 const void *d = data.get_data();
2728 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2730 PluginManager::PluginStatusType status = PluginManager::Favorite;
2731 PluginManager& manager (PluginManager::instance());
2733 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2734 manager.save_statuses ();
2738 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2740 /* call protected MixerActor:: method */
2745 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2747 /* call protected MixerActor:: method */
2752 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2754 boost::shared_ptr<VCA> v = spilled_vca.lock();
2757 show_vca_change (vca); /* EMIT SIGNAL */
2758 redisplay_track_list ();
2763 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2765 return vca == spilled_vca.lock();